Implementing Tweet Search Suggestions in Loklak Wok Android
Loklak Wok Android not only is a peer harvester for Loklak Server but it also provides users to search tweets using Loklak’s API endpoints. To provide a better search tweet search experience to the users, the app provides search suggestions using suggest API endpoint. The blog describes how “Search Suggestions” is implemented. Third Party Libraries used to Implement Suggestion Feature Retrofit2: Used for sending network request Gson: Used for serialization, JSON to POJOs (Plain old java objects). RxJava and RxAndroid: Used to implement a clean asynchronous workflow. Retrolambda: Provides support for lambdas in Android. These libraries can be installed by adding the following dependencies in app/build.gradle android { …. // removes rxjava file repetations packagingOptions { exclude 'META-INF/rxjava.properties' } } dependencies { // gson and retrofit2 compile 'com.google.code.gson:gson:2.8.1' compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.squareup.retrofit2:converter-gson:2.3.0' compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' // rxjava and rxandroid compile 'io.reactivex.rxjava2:rxjava:2.0.5' compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0' } To add retrolambda // in project's build.gradle dependencies { … classpath 'me.tatarka:gradle-retrolambda:3.2.0' } // in app level build.gradle at the top apply plugin: 'me.tatarka.retrolambda' Fetching Suggestions Retrofit2 sends a GET request to search API endpoint, the JSON response returned is serialized to Java Objects using the models defined in models.suggest package. The models can be easily generated using JSONSchema2Pojo. The benefit of using Gson is that, the hard work of parsing JSON is easily handled by it. The static method createRestClient creates the retrofit instance to be used for network calls private static void createRestClient() { sRetrofit = new Retrofit.Builder() .baseUrl(BASE_URL) // base url : https://api.loklak.org/api/ .addConverterFactory(GsonConverterFactory.create(gson)) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); } The suggest endpoint is defined in LoklakApi interface public interface LoklakApi { @GET("/api/suggest.json") Observable<SuggestData> getSuggestions(@Query("q") String query); @GET("/api/suggest.json") Observable<SuggestData> getSuggestions(@Query("q") String query, @Query("count") int count); …. } Now, the suggestions are obtained using fetchSuggestion method. First, it creates the rest client to send network requests using createApi method (which internally calls creteRestClient implemented above). The suggestion query is obtained from the EditText. Then the RxJava Observable is subscribed in a separate thread which is specially meant for doing IO operations and finally the obtained data is observed i.e. views are inflated in the MainUI thread. private void fetchSuggestion() { LoklakApi loklakApi = RestClient.createApi(LoklakApi.class); // rest client created String query = tweetSearchEditText.getText().toString(); // suggestion query from EditText Observable<SuggestData> suggestionObservable = loklakApi.getSuggestions(query); // observable created Disposable disposable = suggestionObservable .subscribeOn(Schedulers.io()) // subscribed on IO thread .observeOn(AndroidSchedulers.mainThread()) // observed on MainUI thread .subscribe(this::onSuccessfulRequest, this::onFailedRequest); // views are manipulated accordingly mCompositeDisposable.add(disposable); } If the network request is successful onSuccessfulRequest method is called which updates the data in the RecyclerView. private void onSuccessfulRequest(SuggestData suggestData) { if (suggestData != null) { mSuggestAdapter.setQueries(suggestData.getQueries()); // data updated. } setAfterRefreshingState(); } If the network request fails then onFailedRequest is called which displays a toast saying “Cannot fetch suggestions, Try Again!”. If requests are sent simultaneously and they fail, the previous message i.e. the previous toast is removed. private void onFailedRequest(Throwable throwable) { Log.e(LOG_TAG, throwable.toString()); if (mToast != null) { // checks if a previous toast is present mToast.cancel();…
