Posting Scraped Tweets to Loklak server from Loklak Wok Android

Loklak Wok Android is a peer harvester that posts collected  messages to the Loklak Server. The suggestions to search tweets are fetched using suggest API endpoint. Using the suggestion queries, tweets are scraped. The scraped tweets are shown in a RecyclerView and simultaneously they are posted to loklak server using push API endpoint. Let’s see how this is implemented. Adding Dependencies to the project This feature heavily uses Retrofit2, Reactive extensions(RxJava2, RxAndroid and Retrofit RxJava adapter) and RetroLambda (for Java lambda support in Android). In app/build.gradle: apply plugin: 'com.android.application' apply plugin: 'me.tatarka.retrolambda' android { ... packagingOptions { exclude 'META-INF/rxjava.properties' } } dependencies { ... 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' compile 'io.reactivex.rxjava2:rxjava:2.0.5' compile 'io.reactivex.rxjava2:rxandroid:2.0.1' }   In build.gradle project level: dependencies { classpath 'com.android.tools.build:gradle:2.3.3' classpath 'me.tatarka:gradle-retrolambda:3.2.0' }   Implementation The suggest and push API endpoint is defined in LoklakApi interface public interface LoklakApi { @GET("/api/suggest.json") Observable<SuggestData> getSuggestions(@Query("q") String query, @Query("count") int count); @POST("/api/push.json") @FormUrlEncoded Observable<Push> pushTweetsToLoklak(@Field("data") String data); }   The POJOs (Plain Old Java Objects) for suggestions and posting tweets are obtained using jsonschema2pojo, Gson uses POJOs to convert JSON to Java objects. The REST client is created by Retrofit2 and is implemented in RestClient class. The Gson converter and RxJava adapter for retrofit is added in the retrofit builder. create method is called to generate the API methods(retrofit implements LoklakApi Interface). public class RestClient { private RestClient() { } private static void createRestClient() { sRetrofit = new Retrofit.Builder() .baseUrl(BASE_URL) // gson converter .addConverterFactory(GsonConverterFactory.create(gson)) // retrofit adapter for rxjava .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); } private static Retrofit getRetrofitInstance() { if (sRetrofit == null) { createRestClient(); } return sRetrofit; } public static <T> T createApi(Class<T> apiInterface) { // create method to generate API methods return getRetrofitInstance().create(apiInterface); } }   The suggestions are fetched by calling getSuggestions after LoklakApi interface is implemented. getSuggestions returns an Observable of type SuggestData, which contains the suggestions in a List. For scraping tweets only a single query needs to be passed to LiquidCore, so flatmap is used to transform the observabe and then fromIterable operator is used to emit single queries as string to LiquidCore which then scrapes tweets, as implemented in fetchSuggestions private Observable<String> fetchSuggestions() { LoklakApi loklakApi = RestClient.createApi(LoklakApi.class); Observable<SuggestData> observable = loklakApi.getSuggestions("", 2); return observable.flatMap(suggestData -> { List<Query> queryList = suggestData.getQueries(); List<String> queries = new ArrayList<>(); for (Query query : queryList) { queries.add(query.getQuery()); } return Observable.fromIterable(queries); }); }   As LiquidCore uses callbacks to create a connection between NodeJS instance and Android, to maintain a flow of observables a custom observable is created using create operator which encapsulates the callbacks inside it. For a detail understanding of how LiquidCore event handling works, please go through the example. The way it is implemented in getScrapedTweets: private Observable<ScrapedData> getScrapedTweets(final String query) { final String LC_TWITTER_URI = "android.resource://org.loklak.android.wok/raw/twitter"; URI uri = URI.create(LC_TWITTER_URI); return Observable.create(emitter -> { // custom observable creation EventListener startEventListener = (service, event, payload) -> { service.emit(LC_QUERY_EVENT, query); service.emit(LC_FETCH_TWEETS_EVENT); }; EventListener getTweetsEventListener = (service, event, payload) -> { ScrapedData scrapedData = mGson.fromJson(payload.toString(), ScrapedData.class);…

Continue ReadingPosting Scraped Tweets to Loklak server from Loklak Wok Android

Realm database in Loklak Wok Android for Persistent view

Loklak Wok Android provides suggestions for tweet searches. The suggestions are stored in local database to provide a persistent view, resulting in a better user experience. The local database used here is Realm database instead of sqlite3 which is supported by Android SDK. The proper way to use an sqlite3 database is to first create a contract where the schema of the database is defined, then a database helper class which extends from SQLiteOpenHelper class where the schema is created i.e. tables are created and finally write ContentProvider so that you don’t have to write long SQL queries every time a database operation needs to be performed. This is just a lot of hard work to do, as this includes a lot of steps, debugging is also difficult. A solution to this can be using an ORM that provides a simple API to use sqlite3, but the currently available ORMs lack in terms of performance, they are too slow. A reliable solution to this problem is realm database, which is faster than raw sqlite3 and has really simple API for database operations. This blog explains the use of realm database for storing tweet search suggestions. Adding Realm database to Android project In project level build.gradle buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.3.3' classpath "io.realm:realm-gradle-plugin:3.3.1" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } }   And at the top of app/build.gradle "apply plugin: 'realm-android'"  is added. Using Realm Database Let’s start with a simple example. We have a Student class that has only two attributes name and age. To create the model for the database, the Student class is simply extended to RealmObject. public class Student extends RealmObject { private String name; private int age; // A constructor needs to be explicitly defined, be it an empty constructor public Student(String name, int age) { this.name = name; this.age = age; } // getters and setters }   To push data to the database, Java objects are created, a transaction is initialized, then copyToRealm method is used to push the data and finally the transaction is committed. But before all this, the database is initialized and a Realm instance is obtained. Realm.init(context); // Database initialized Realm realm = Realm.getDefaultInstance(); // realm instance obtained Student student = new Student("Rahul Dravid", 22); // Simple java object created realm.beginTransaction() // initialization of transaction realm.copyToRealm(student); // pushed to database realm.commitTransaction(); // transaction committed   copyToRealm takes only a single parameter, the parameter can be an object or an Iterable. Off course, the passed parameter should extend RealmObject. A List of Student can be passed as a parameter to copyToRealm to push multiple data into the database. The above way of inserting data is synchronous. Realm also supports asynchronous transactions, you guessed it right, you don’t have to depend on AsyncTaskLoader. The same operation can be performed asynchronously as realm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { Student student = new Student("Rahul Dravid",…

Continue ReadingRealm database in Loklak Wok Android for Persistent view