Skill History Component in Susi Skill CMS

SUSI Skill CMS is an editor to write and edit skill easily. It is built on ReactJS framework and follows an API centric approach where the Susi server acts as API server. Using Skill CMS we can browse history of a skill, where we get commit ID, commit message and name the author who made the changes to that skills. In this blog post, we will see how to add skill revision history component in Susi Skill CMS.

One text file represents one skill, it may contain several intents which all belong together. Susi skills are stored in susi_skill_data repository. We can access any skill based on four tuples parameters model, group, language, skill.

<Menu.Item key="BrowseRevision">
 <Icon type="fork" />
   Browse Skills Revision
 <Link to="/browseHistory"></Link>
</Menu.Item>

First let’s create a option in sidebar menu, and link it “/browseHistory” route created at index.js 

 <Route path="/browseHistory" component={BrowseHistory} />

Next we will be adding skill versioning using endpoints provided by Susi Server, to select a skill we will create a drop down list, for this we will be using Select Field a component of  Material UI.

request('http://cors-anywhere.herokuapp.com/api.susi.ai/cms/getModel.json').then((data) => {
    console.log(data.data);
    data = data.data;
    for (let i = 0; i < data.length; i++) {
        models.push(<MenuItem value={i} key={data[i]} primaryText={`${data[i]}`}/>);
    }

    console.log(models);
});
 <SelectField
   floatingLabelText="Expert"
   style={{width: '50px'}}
   value={this.state.value}
   onChange={this.handleChange}>
      {experts}
  </SelectField>

We store the models, groups and languages in array using the endpoints api.susi.ai/cms/getModel.json, api.susi.ai/cms/getGroups.json, api.susi.ai/cms/getAllLanguages.json set the values in respective select fields. The request functions takes the url as string and the parses the json and fetches the object containing data or error depending on the response from the server. Once run your project using

npm start

And you would be able to see the drop down list working

Next, we will use Material UI tables for displaying the organized data. For using  table component we need to import table, it’s body, header and row column from Material ui class. 

import {Table, TableBody, TableHeader, TableHeaderColumn, TableRow, TableRowColumn} from "material-ui/Table";

We then make our header Columns, in our case it’s three, namely Commit ID, Commit Message, Author name.

 <TableRow>
  <TableHeaderColumn tooltip="Commit ID">Commit ID</TableHeaderColumn>
  <TableHeaderColumn tooltip="Commit Message">Commit Message</TableHeaderColumn>
  <TableHeaderColumn tooltip="Author Name">Author Name</TableHeaderColumn>
 </TableRow>

To get the history of modification of a skill, we will use endpoint “http://api.susi.ai/cms/getSkillHistory.json”. It uses JGit for managing version control in skill data repository. JGit is a library which implements the Git functionality in Java. An endpoint is accessed based on userRoles, which can be Admin, Privilege, User, Anonymous. In our case it is Anonymous. Thus a User need not to log in to access this endpoint.

 url = "http://api.susi.ai/cms/getSkillHistory.json?model="+models[this.state.modelValue].key+"&group="+groups[this.state.groupValue].key+"&language="+languages[this.state.languageValue].key+"&skill="+this.state.expertValue;

After getting the url, we will next make a ajax network call to get the modification history of skill., if the method returns success, we get the desired data in table array and display it in rows through its render() method, which checks if the data is set in state -if so, it renders the contents otherwise we display the error occurred while processing the request.

 $.ajax({
            url: url,
            jsonpCallback: 'pccd',
            dataType: 'jsonp',
            jsonp: 'callback',
            crossDomain: true,
            success: function(data) {
                data = data.commits;
                let array = [];
                for(let i=0;i<data.length;i++){
                    array.push(data[i]);
                }
{tableData.map((row, index) => (
  <TableRow key={index}>
    <TableRowColumn>{row.commitID}</TableRowColumn>
    <TableRowColumn>{row.commit_message}</TableRowColumn>
    <TableRowColumn>{row.author}</TableRowColumn>
   </TableRow>
))}

Test the final output  on http://skills.susi.ai/browseHistory or http://localhost:3000/browseHistory , select the model, group , language and skill and get the history of that skill.


Next time when you need drop down list or tables to organize your data, do check out https://github.com/callemall/material-ui for examples, which can help in providing good outlines to you apps. For contributions to susi_skill_cms, join our chat channel  on gitter: https://gitter.im/fossasia/susi_server and browse https://github.com/fossasia/susi_skill_cms for complete code.

Resources

Continue ReadingSkill History Component in Susi Skill CMS

Dynamic Base URL Support in the Open Event Organizer App

Open Event API Server acts as a backend for Open Event Organizer Android App. The server has a development instance running on the web for developers. Developers use this instance to try out new feature additions, bug fixings and other such changes in the source code. And when confirmed working, these changes are updated to the main running instance which is kept live throughout for the users. Similarly for Android app developers, to test the app with both the instances, we have implemented the dynamic base URL support in the app. The app has a default base URL set to development instance or main instance dependent on the debug mode. That means the app will use a server on developer instance when used under debug mode and will use a main instance server if used under release mode. The app also provides an option to enter an alternate URL while login in the app which replaces default base URL in the app for the session.

In the organizer app, we are using Retrofit + Okhttp for handling network requests and dagger for dependency injection. The OkhttpClient provider in NetworkModule class looks like:

@Provides
@Singleton
OkHttpClient providesOkHttpClient(HostSelectionInterceptor interceptor) {
   return new OkHttpClient.Builder()
       .addNetworkInterceptor(new StethoInterceptor())
       .build();
}

 

Retrofit had a support for mutable base URL in the earlier versions but the feature is no longer available in the recent versions. We are using Interceptor class for changing base URL. The class has a method named intercept, which gets called at each network request. In this method, base URL is reset to the new URL.

So first you have to extend Interceptor class and reset base URL in the intercept method. The Interceptor class in the app looks like:

public final class HostSelectionInterceptor implements Interceptor {
   private String host;
   private String scheme;

   public HostSelectionInterceptor(){
       //Intentionally left blank
   }

   public void setInterceptor(String url) {
       HttpUrl httpUrl = HttpUrl.parse(url);
       scheme = httpUrl.scheme();
       host = httpUrl.host();
   }

   @Override
   public Response intercept(Chain chain) throws IOException {
       Request original = chain.request();

       // If new Base URL is properly formatted then replace the old one
       if (scheme != null && host != null) {
           HttpUrl newUrl = original.url().newBuilder()
               .scheme(scheme)
               .host(host)
               .build();
           original = original.newBuilder()
               .url(newUrl)
               .build();
       }
       return chain.proceed(original);
   }
}

 

The class has a private string field host to save base URL. The method setInterceptor is used to change the base URL. Once the base URL is changed, thereafter all the network requests use changed URL to call. So now our interceptor is ready which can be used to support dynamic base URL in the app. This interceptor is added to Okhttp builder using its method addInterceptor.

@Provides
@Singleton
HostSelectionInterceptor providesHostSelectionInterceptor() {
   return new HostSelectionInterceptor();
}

@Provides
@Singleton
OkHttpClient providesOkHttpClient(HostSelectionInterceptor interceptor) {
   return new OkHttpClient.Builder()
       .addInterceptor(interceptor)
       .addNetworkInterceptor(new StethoInterceptor())
       .build();
}

 

And now you are able to change base URL just by using the setInterceptor method of Interceptor class from anywhere in the app. And by then all the network calls use the updated base URL.

Application

I will show you here, how exactly this works in the Open Event Organizer app. On the login page, we have provided an option to enter an alternate base URL.

                                

We have kept a default URL checked. The default URL is set as per debug mode. This is done by setting the fields in the build.gradle. The code looks like:

buildTypes {
       release {
           ...
           buildConfigField "String", "DEFAULT_BASE_URL", '"https://www.eventyay.com/api/v1/"'
       }
       debug {
           buildConfigField "String", "DEFAULT_BASE_URL", '"https://open-event-dev.herokuapp.com/api/v1/"'
       }
   }

 

The field is used in the app as:

private final String DEFAULT_BASE_URL = BuildConfig.DEFAULT_BASE_URL;

 

On login, the loginPresenter calls setInterceptor method of the Interceptor to update the URL according to the user’s input. And the base URL is changed in the app for further network requests.

Links:
1. Gist link for Interceptor implementation code – https://gist.github.com/swankjesse/8571a8207a5815cca1fb
2. Google dagger dependency injector Github Repo
3. Retrofit http client Github Repo
4. Okhttp client Github Repo

Continue ReadingDynamic Base URL Support in the Open Event Organizer App

Displaying a Comments dialogfragment on a Button Click from the Feed Adapter in the Open Event Android App

Developing the live feed of the event page from Facebook for the Open Event Android App, there were questions how best to display the comments in the feed.  A dialog fragment over the feeds on the click of a button was the most suitable solution. Now the problem was, a dialogfragment can only be called from an app component (eg- fragment or an activity). Therefore, the only challenge which remained was to call the dialogfragment from the adapter over the feed fragment with the corresponding comments of the particular post on a button click.

What is a dialogfragment?

A dialogfragment displays a dialog window, floating on top of its activity’s window. This fragment contains a Dialog object, which it displays as appropriate based on the fragment’s state. Control of the dialog (deciding when to show, hide, dismiss it) should be done through the API here, not with direct calls on the dialog (Developer.Android.com).

Solution

The solution which worked on was to define a adapter callback interface with a onMethodCallback method in the feed adapter class itself with the list of comment items fetched at runtime on the button click of a particular post. The interface had to be implemented by the main activity which housed the feed fragment that would be creating the comments dialogfragment with the passed list of comments.

Implementation

Define an interface adapterCallback with the method onMethodCallback parameterized by the list of comment items in your adapter class.

public interface AdapterCallback {
   void onMethodCallback(List<CommentItem> commentItems);
}

 

Create a constructor of the adapter with the adapterCallback as a parameter. Do not forget to surround it with a try/catch.

public FeedAdapter(Context context, AdapterCallback adapterCallback, List<FeedItem> feedItems) {
     this.mAdapterCallback = adapterCallback;
}

 

On the click of the comments button, call onMethodCallback method with the corresponding comment items of a particular feed.

getComments.setOnClickListener(v -> {
   if(commentItems.size()!=0)
       mAdapterCallback.onMethodCallback(commentItems);
});

 

Finally implement the interface in the activity to display the comments dialog fragment populated with the corresponding comments of a feed post. Pass the comments with the help of arraylist through the bundle.

@Override
public void onMethodCallback(List<CommentItem> commentItems) {
   CommentsDialogFragment newFragment = new CommentsDialogFragment();
   Bundle bundle = new Bundle();
   bundle.putParcelableArrayList(ConstantStrings.FACEBOOK_COMMENTS, new ArrayList<>(commentItems));
   newFragment.setArguments(bundle);
   newFragment.show(fragmentManager, "Comments");
}

 

Conclusion

The comments generated with each feed post in the open event android app does complement the feed well. The pagination is something which is an option in the comments and the feed both however that is something for some other time. Until then, keep coding!

Resources

Continue ReadingDisplaying a Comments dialogfragment on a Button Click from the Feed Adapter in the Open Event Android App

Create an App Widget for Bookmarked Sessions for the Open Event Android App

What is an app widget?

App Widgets are miniature application views that can be embedded in other applications (such as the Home screen) and receive periodic updates. These views are referred to as Widgets in the user interface, and you can publish one with an App Widget provider. – (Android Documentation).

Android widget is an important functionality that any app can take advantage of. It could be used to show important dates, things that the user personalizes on the app etc. In the context of the Open Event Android App, it was necessary to create a bookmark widget for the Android phones so that the user could see his bookmarks on the homescreen itself and need not open the app for the same. In the open event android app, the widget was already created but it needed bug fixes and UI enhancements due to migration to the Realm database migration. Therefore, my majority of work circled around that.

Implementation

Declare the app widget in the manifest. All the updates in the application would be received by the class which extends the AppWidgetProvider if it needs to be reflected in the widget.

<receiver
   android:name=".widget.BookmarkWidgetProvider"
   android:enabled="true"
   android:label="Bookmarks">
   <intent-filter>
       <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
       <action android:name="${applicationId}.ACTION_DATA_UPDATED" />
       <action android:name="${applicationId}.UPDATE_MY_WIDGET" />
   </intent-filter>
   <meta-data
       android:name="android.appwidget.provider"
       android:resource="@xml/widget_info" />
</receiver>

 

Create a layout for the widget that is to be displayed on the homescreen. Remember to use only the views defined in the documentation. After the creation of the layout, create a custom widget updater which will broadcast the data from the app to the receiver to update the widget.

public class WidgetUpdater {
   public  static  void updateWidget(Context context){
       int widgetIds[] = AppWidgetManager.getInstance(context.getApplicationContext()).getAppWidgetIds(new ComponentName(context.getApplicationContext(), BookmarkWidgetProvider.class));
       BookmarkWidgetProvider bookmarkWidgetProvider = new BookmarkWidgetProvider();
       bookmarkWidgetProvider.onUpdate(context.getApplicationContext(), AppWidgetManager.getInstance(context.getApplicationContext()),widgetIds);
       context.sendBroadcast(new Intent(BookmarkWidgetProvider.ACTION_UPDATE));
   }
}

 

Next, create a custom RemoteViewService to update the views in the widget. The reason this is required is because the app widget does not operate in the usual lifecycle of the app. And therefore a remote service is required which acts as the remote adapter to connect to the remote views. In your class, override the onGetViewFactory() method and create a new remoteViewsFactory object to get the the data from the app on updation of the bookmark list. To populate the remote views, override the getViewsAt() method.

public class BookmarkWidgetRemoteViewsService extends RemoteViewsService {

@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {

return new RemoteViewsFactory() {
   private MatrixCursor data = null;

   @Override
   public void onCreate() {
       //Called when your factory is first constructed.
   }

   @Override
   public void onDataSetChanged() {
       }

   @Override
   public RemoteViews getViewAt(int position) {
       } 
   }
}

 

Finally, create a custom AppWidgetProvider which parses the relevant fields out of the intent and updates the UI. It acts like a broadcast receiver, hence all the updates by the widgetUpdater is received here.

public class BookmarkWidgetProvider extends AppWidgetProvider {

   public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
	  RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.bookmark_widget);
             setRemoteAdapter(context, views);

   }

   @Override
   public void onReceive(@NonNull Context context, @NonNull Intent intent) {
       super.onReceive(context, intent);
   }

   private void setRemoteAdapter(Context context, @NonNull final RemoteViews views) {
       views.setRemoteAdapter(R.id.widget_list,
               new Intent(context, BookmarkWidgetRemoteViewsService.class));
   }

}

 

Conclusion

For any event based apps, it is crucial that it regularly provide updates to its users and therefore app widget forms an integral part of that whole experience.

References

 

 

Continue ReadingCreate an App Widget for Bookmarked Sessions for the Open Event Android App

Implementing Sponsors API in Open Event Frontend to Display Sponsors

This article will illustrate how the sponsors have been displayed on the public event page in Open Event Frontend using the Open-Event-Orga sponsors API. As we know that the project is an ember application so, it uses Ember data to consume the API. For fetching the sponsors, we would be mainly focusing on the following API endpoint:

GET /v1/events/{event_identifier}/sponsors

 

In the application we need to display the sponsors is the event’s public page which contains the event details, ticketing information, speaker details etc. along with the list of sponsors so, we will be only concerned with the public/index route in the application. As the sponsors details are nested within the events model so we need to first fetch the event and then from there we need to fetch the sponsors list from the model.

The model to fetch the event details looks like this:

model(params) {
return this.store.findRecord('event', params.event_id, { include: 'social-links' });
}

 

But we can easily observe that there is no parameter related to sponsor in the above model. The reason behind this is the fact that we want our sponsors to be displayed only on the event’s index route rather than displaying them on all the sub routes under public route. To display the sponsors on the public/index route our modal looks like this:

model() {
    const eventDetails = this._super(...arguments);
    return RSVP.hash({
      event   : eventDetails,
      sponsors: eventDetails.get('sponsors')
   });
}

 

As we can see in the above code that we have used this._super(…arguments) to fetch the event details from the event’s public route model which contains all the information related to the event thereby eliminating the need of another API call to fetch sponsors. Now using the ember’s get method we are fetching the sponsors from the eventDetails and putting it inside the sponsors JSON object for using it lately to display sponsors in public/index route.

Till now, we’ve fetched and stored the sponsors now our next task is to display the sponsors list on the event’s index page. The code for displaying the sponsors list on the index page is

{{public/sponsor-list sponsors=model.sponsors}} 

 

The sample user interface without  for displaying the sponsors looks like this:  

Fig. 1: The sample user interface for displaying the sponsors

After replacing the real time data with the sample one, the user interface (UI) for the sponsors looks like this.

Fig. 2: The user interface for sponsors with real time data

The entire code for implementing the sponsors API can be seen here.

To conclude, this is how we efficiently fetched the sponsors list using the Open-Event-Orga sponsors API, ensuring that there is no unnecessary API call to fetch the data.  

Resources:

Continue ReadingImplementing Sponsors API in Open Event Frontend to Display Sponsors

Sharing Images on Twitter from Phimpme Android App Using twitter4j

As sharing an image to the social media platform is an important feature in Phimpme android. In my previous blog, I have explained how to authenticate the Android application with Twitter. In this blog, I will discuss how to upload an image directly on Twitter from the application after successfully logging to Twitter.

To check if the application is authenticated to Twitter or not.

When the application is successfully authenticated Twitter issues a Token which tells the application if it is connected to Twitter or not. In LoginActivity.java the function isActive returns a boolean value. True if the Twitter token is successfully issued or else false.  

public static boolean isActive(Context ctx) {
        SharedPreferences sharedPrefs = ctx.getSharedPreferences(AppConstant.SHARED_PREF_NAME, Context.MODE_PRIVATE);
        return sharedPrefs.getString(AppConstant.SHARED_PREF_KEY_TOKEN, null) != null;
    }

We call isActive function from LoginActive class to check if the application is authenticated to Twitter or not. We call it before using the share function in sharingActivity:

if (LoginActivity.isActive(context)) {
                try {
                    // Send Image function
} catch (Exception ex) {
                    Toast.makeText(context, "ERROR", Toast.LENGTH_SHORT).show();
 }

We have saved the image in the internal storage of the device and use saveFilePath to use the path of the saved image. In Phimpme we used HelperMethod class where our share function resides, and while the image is being shared an alert dialog box with spinner pops on the screen.

Sending the image to HelperMethod class

First,

We need to get the image and convert it into Bitmaps. Since, the image captured by the phone camera is usually large to upload and it will take a lot of time we need to compress the Bitmap first. BitmapFactory.decodeFile(specify name of the file) is used to fetch the file and convert it into bitmap.

To send the data we used FileOutStream to the set the path of the file or image in this case. Bitmap.compress method is used to compress the image to desired value and format. In Phimpme we are converting it into PNG.  

Bitmap bmp = BitmapFactory.decodeFile(saveFilePath);
                    String filename = Environment.getExternalStorageDirectory().toString() + File.separator + "1.png";
                    Log.d("BITMAP", filename);
                    FileOutputStream out = new FileOutputStream(saveFilePath);
                    bmp.compress(Bitmap.CompressFormat.PNG, 90, out);

                    HelperMethods.postToTwitterWithImage(context, ((Activity) context), saveFilePath, caption, new HelperMethods.TwitterCallback() {

                        @Override
                        public void onFinsihed(Boolean response) {
                            mAlertBuilder.dismiss();
                            Snackbar.make(parent, R.string.tweet_posted_on_twitter, Snackbar.LENGTH_LONG).show();
                        }

Post image function

To post the image on Twitter we will use ConfigurationBuilder class. We will create a new object of the class and then attach Twitter consumer key, consumer secret key, Twitter access token, and twitter token secret.

  • setOAuthConsumerKey() function is used to set the consumer key which is generated by the Twitter when creating the application in the Twitter development environment.
  • Similarly, setOAuthConsumerSecret() function is used to set the consumer secret key.
  • Specify the token key which generated after successfully connecting to twitter in setOAuthAcessToken() fuction and Token secret in setOAuthAcessTokenSecret() function.  
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();       configurationBuilder.setOAuthConsumerKey(context.getResources().getString(R.string.twitter_consumer_key));
configurationBuilder.setOAuthConsumerSecret(context.getResources().getString(R.string.twitter_consumer_secret));
configurationBuilder.setOAuthAccessToken(LoginActivity.getAccessToken((context)));
configurationBuilder.setOAuthAccessTokenSecret(LoginActivity.getAccessTokenSecret(context));
        Configuration configuration = configurationBuilder.build();
final Twitter twitter = new TwitterFactory(configuration).getInstance();

Sending Image to twitter:

  • The image is uploaded to twitter using statusUpdate class specified in Twitter4j API.
  • Pass the image file name in status.setMedia(file).
  • Pass the caption in status.updateStatus(caption).
  • updateStatus is used to finally upload the image with the caption.
final File file = new File(imageUrl);

        new Thread(new Runnable() {
            @Override
            public void run() {
                boolean success = true;
                try {
                    if (file.exists()) {
                        StatusUpdate status = new StatusUpdate(message);
                        status.setMedia(file);
                        twitter.updateStatus(status);
                    }else{
                        Log.d(TAG, "----- Invalid File ----------");
                        success = false;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    success = false;
                }

 Conclusion:                                                                                                                      Using Twitter4j API allows sharing image on Twitter without leaving the  application and opening any additional view.

Github

Resources

Continue ReadingSharing Images on Twitter from Phimpme Android App Using twitter4j

Presenter Abstraction Layer in Open Event Organizer Android App

Open Event Organizer App design follows Model View Presenter (MVP) architecture which enables heavy unit testing. MVP is a trending architecture design followed these days. If you are not aware of MVP architecture, then please refer any of the tutorial (few links are given at the end of this blog) about it before reading this. In the design, the code becomes little repetitive as the application size increases due to so many presenters and views, which degrades the code readability. So to avoid this and keep the functionality code clean in the App, we have created a Presenter Abstraction Layer which contains the repetitive code and the layer is extended wherever required in the app. I will be talking about the Presenter Abstraction Layer implementation through the App in this blog.

First of all, create a base interface. The base interface contains methods which every presenter will have. The base interface for presenter in the App looks like:

public interface IBasePresenter {
   void start();
   void detach();
}

 

In the method start, presenter loads all the required data from the model and sends it to the view. And releases all the resources in detach. These two methods are required in all the presenters. This interface is extended by other two interfaces which will be actually used by the views. The relevant code is:

public interface IPresenter<V> extends IBasePresenter {
   void attach(V view);
}

public interface IDetailPresenter<K, V> extends IBasePresenter {
   void attach(K key, V view);
}

 

Method attach is used to attach view and the data id (if required) to the presenter. In the app, most of the presenters require an extra data which is used in loading data from the model. Hence two interfaces are extended from the base interface. Now comes the implementation part.

public abstract class BasePresenter<V> implements IPresenter<V> {
   private V view;
   private CompositeDisposable compositeDisposable;

   @Override
   @CallSuper
   public void attach(V view) {
       this.view = view;
       this.compositeDisposable = new CompositeDisposable();
   }

   @Override
   @CallSuper
   public void detach() {
       view = null;
       compositeDisposable.dispose();
   }

   protected V getView() {
       return view;
   }

   protected CompositeDisposable getDisposable() {
       return compositeDisposable;
   }
}

 

The App uses ReactiveX Observables for async operations which contain fragment/activity context hence these need to be disposed at some lifecycle of fragment/activity. detach nulls the view and disposes the compositeDisposable. This method is called at the onStop lifecycle of fragment/activity. The observable subscriptions are one of the major reasons for memory leaks if not disposed at correct lifecycle in Android. So the detach method is called at onStop lifecycle when activity goes into background or fragment is switched by FragmentTransaction. Another base presenter class looks like:

public  abstract class BaseDetailPresenter<K, V> extends BasePresenter<V> implements IDetailPresenter<K, V> {
   private K id;

   @Override
   @CallSuper
   public void attach(K id, V view) {
       super.attach(view);
       this.id = id;
   }

   protected K getId() {
       return id;
   }
}

 

This class extends the previous one except for the attach method. As the presenters extending this, require an extra data id which is passed through this method. So the id can be used in the presenter extending this class using getId. The presenters in the app extend one of these two classes. This helps in making a firm app structure and the development process easier. Abstraction layer should be used wherever same code is repeated. This increases code readability and decreases the chances of creating bugs especially when a team is working on the same project.

Links:
1. MVP for Android: how to organize the presentation layer, by Antonio Leiva
2. Android Code That Scales, With MVP, by Nathan Barraille
3. Ted Mosby – Software Architect, by Hannes Dorfmann

Continue ReadingPresenter Abstraction Layer in Open Event Organizer Android App

Integrating Twitter Authenticating using Twitter4j in Phimpme Android Application

We have used Twitter4j API to authenticate Twitter in Phimpme application. Below are the following steps in setting up the Twitter4j API in Phimpme and Login to Twitter from Phimpme android application.

Setting up the environment

Download the Twitter4j package from http://twitter4j.org/en/. For sharing images we will only need twitter4j-core-3.0.5.jar and twitter4j-media-support-3.0.5.jar files. Copy these files and save it in the libs folder of the application.

Go to build.gradle and add the following codes in dependencies:

dependencies {
compile files('libs/twitter4j-core-3.0.5.jar')
compile files('libs/twitter4j-media-support-3.0.5.jar')
}

Adding Phimpme application in Twitter development page

Go to https://dev.twitter.com/->My apps-> Create new apps. Create an application window opens where we have to fill all the necessary details about the application. It is mandatory to fill all the fields. In website field, if you are making an android application then anything can be filled in website field for example www.google.com. But it is necessary to fill this field also.

After filling all the details click on “Create your Twitter application” button.

Adding Twitter Consumer Key and Secret Key

This generates twitter consumer key and twitter secret key. We need to add this in our string.xml folder.

<string name="twitter_consumer_key">ry1PDPXM6rwFVC1KhQ585bJPy</string>
<string name="twitter_consumer_secret">O3qUqqBLinr8qrRvx3GXHWBB1AN10Ax26vXZdNlYlEBF3vzPFt</string> 

Twitter Authentication

Make a new JAVA class say LoginActivity. Where we have to first fetch the twitter consumer key and Twitter secret key.

private static Twitter twitter;
    private static RequestToken requestToken;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_twitter_login);
        twitterConsumerKey = getResources().getString(R.string.twitter_consumer_key);
        twitterConsumerSecret = getResources().getString(R.string.twitter_consumer_secret);  

We are using a web view to interact with the Twitter login page.

twitterLoginWebView = (WebView)findViewById(R.id.twitterLoginWebView);
        twitterLoginWebView.setBackgroundColor(Color.TRANSPARENT);
        twitterLoginWebView.setWebViewClient( new WebViewClient(){
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url){

                if( url.contains(AppConstant.TWITTER_CALLBACK_URL)){
                    Uri uri = Uri.parse(url);
                    LoginActivity.this.saveAccessTokenAndFinish(uri);
                    return true;
                }
                return false;
            }             

If the access Token is already saved then the user is already signed in or else it sends the Twitter consumer key and the Twitter secret key to gain access Token. ConfigurationBuilder function is used to set the consumer key and consumer secret key.

ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
        configurationBuilder.setOAuthConsumerKey(twitterConsumerKey);     configurationBuilder.setOAuthConsumerSecret(twitterConsumerSecret);
        Configuration configuration = configurationBuilder.build();
        twitter = new TwitterFactory(configuration).getInstance();

It is followed by the following Runnable thread to check if the request token is received or not. If authentication fails, an error Toast message pops.

new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    requestToken = twitter.getOAuthRequestToken(AppConstant.TWITTER_CALLBACK_URL);
                } catch (Exception e) {
                    final String errorString = e.toString();
                    LoginActivity.this.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mAlertBuilder.cancel();
                            Toast.makeText(LoginActivity.this, errorString, Toast.LENGTH_SHORT).show();
                            finish();
                        }
                    });
                    return;
                }

                LoginActivity.this.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        twitterLoginWebView.loadUrl(requestToken.getAuthenticationURL());
                    }
                });
            }
        }).start();

Conclusion

It offers seamless integration of Twitter in any application. Without leaving actual application, easier to authenticate. Further, it is used to upload the photo to Twitter directly from Phimpme Android application, fetch profile picture and username.

Github

Resources

 

Continue ReadingIntegrating Twitter Authenticating using Twitter4j in Phimpme Android Application

Hiding Intelligence of Susper When a Query is Empty or Erased with Angular

Recently, we have implemented intelligence feature in Susper using SUSI chat API to provide users answer a question without going deeper in search results. When a user types “height of Trump”, it shows answer like this:

Problem which we faced after implementing the feature:

When a user was erasing a query or query field was empty, Susper was still showing the answer of the intelligence component like this:

The answer should not be displayed when a query is empty because the user is not asking any question. The answer was still displayed because it had received a response from SUSI API.

How did we solve the problem?

The problem was solved in two ways.

  1. By using if/else condition: We checked if the statement shown inside the component is similar to the if-and-else condition. If the condition is true, it should hide the component.
  2. Using [hidden] attribute method: The Angular 4 supports [hidden] attribute which acts as { display:none; } . [hidden] attribute generally works as ngShow and ngHide which was earlier supported by Angular 2.

We preferred both the methods to solve the problem. The intelligence component is being loaded inside results component using <app-intelligence> element. Further, we added [hidden] attribute to this element like this :

<appintelligence [hidden]=“hideIntelligence”></app-intelligence>
We created hideIntelligence as variable and assign it as boolean. To check if a query is empty, searchdata variable was used.
searchdata: any = {
  query: ‘ ‘,
  rows: 10,
  start: 0
};
And then checked if a query is empty using if-else condition :
// checks if query is empty or erased
if (this.searchdata.query === ‘ ‘) {// display: none; is true
  this.hideIntelligence = true;

} else {
// display: none; is false
  this.hideIntelligence = false;
}

 

Applying this solution, we succeeded in hiding the intelligence component. We would also had used *ngIf statement but we preferred using [hidden]. [hidden] modifies the display property.  *ngIf is a structural directive which creates or destroys content inside DOM.

The source code for the implementation can be found here: https://github.com/fossasia/susper.com/pull/613

Resources:

 

Continue ReadingHiding Intelligence of Susper When a Query is Empty or Erased with Angular

Customizing Serializers in Open Event Front-end

Open Event Front-end project primarily uses Ember Data for API requests, which handles sending the request to correct endpoint, serializing and deserializing the request/response. The Open Event API project uses JSON API specs for implementation of the API, supported by Ember data.

While sending request we might want to customize the payload using a custom serializer. While implementing the Users API in the project, we faced a similiar problem. Let’s see how we solved it.

Creating a serializer for model

A serializer is created for a model, in this example we will create a user serializer for the user model. One important thing that we must keep in mind while creating a serializer is to use same name as that of model, so that ember can map the model with the serializer. We can create a serializer using ember-cli command:

ember g serializer user

 
Customizing serializer

In Open Event Front-end project every serializer extends the base serializer application.js which defines basic serialization like omitting readOnly attributes from the payload.

The user serializer provides more customization for the user model on top of application model. We override the serialize function, which lets us manipulate the payload of the request. We use `snapshot.id` to differentiate between a create request & an update request. If `snapshot.id` exists then it is an update request else it is a create request.

While manipulation user properties like email, contact etc we do not need to pass ‘password’ in the payload. We make use of ‘adapterOptions’ property associated with the ‘save()’ method. If the adapterOptions are associated and the ‘includePassword’ is set then we add ‘password’ attribute to the payload.

import ApplicationSerializer from 'open-event-frontend/serializers/application';
import { pick, omit } from 'lodash';

export default ApplicationSerializer.extend({
  serialize(snapshot, options) {
    const json = this._super(...arguments);
    if (snapshot.id) {
      let attributesToOmit = [];
      if (!snapshot.adapterOptions || !snapshot.adapterOptions.includePassword) {
        attributesToOmit.push('password');
      }
      json.data.attributes = omit(json.data.attributes, attributesToOmit);
    } else if (options && options.includeId) {
      json.data.attributes = pick(json.data.attributes, ['email', 'password']);
    }
    return json;
  }
});

If we want to add the password in the payload we can simply add ‘includePassword’ property to the ‘adapterOptions’ and pass it in the save method for operations like changing the password of the user.

user.save({
  adapterOptions: {
    includePassword: true
  }
})

Thank you for reading the blog, you can check the source code for the example here.
Resources

Learn more about how to customize serializers in ember data here

Continue ReadingCustomizing Serializers in Open Event Front-end