Installing the Loklak Search and Deploying it to Surge

The Loklak search creates a website using the Loklak server as a data source. The goal is to get a search site, that offers timeline search as well as custom media search, account and geolocation search. In order to run the service, you can use the API of http://api.loklak.org or install your own Loklak server data storage engine. Loklak_server is a server application which collects messages from various social media tweet sources, including Twitter. The server contains a search index and a peer-to-peer index sharing interface. All messages are stored in an elasticsearch index. The site of this repo is deployed on the GitHub gh-pages branch and automatically deployed here: http://loklak.org In this blog, we will talk about how to install Loklak_Search locally and deploying it to Surge (Static web publishing for Front-End Developers). How to clone the repository Sign up / Login to GitHub and head over to the Loklak_Search repository. Then follow these steps. Go ahead and fork the repository https://github.com/fossasia/loklak_search   Get the clone of the forked version on your local machine using git clone https://github.com/<username>/loklak_search.git   Add upstream to synchronize repository using git remote add upstream https://github.com/fossasia/loklak_search.git Getting Started The Loklak search application basically consists of the following : Angular-cli node --version >= 6 npm --version >= 3 First, we will need to install angular-cli by using the following command: npm install -g @angular/cli@latest 2. After installing angular-cli we need to install our required node modules, so we will do that by using the following command: npm install 3. Deploy locally by running this ng serve Go to localhost:4200 where the application will be running locally. How to Deploy Loklak Search on Surge : Surge is the technology which publishes or generates the static web-page demo link, which makes it easier for the developer to deploy their web-app. There are a lot of benefits of using surge over generating demo link using GitHub pages. We need to install surge on our machine. Type the following in your Linux terminal: npm install --global surge This installs the Surge on your machine to access Surge from the command line. In your project directory just run surge After this, it will ask you three parameters, namely Email Password Domain After specifying all these three parameters, the deployment link with the respective domain is generated. Auto deployment of Pull Requests using Surge : To implement the feature of auto-deployment of pull request using surge, one can follow up these steps: Create a pr_deploy.sh file The pr_deploy.sh file will be executed only after success of Travis CI i.e. when Travis CI passes by using command bash pr_deploy.sh #!/usr/bin/env bash if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo "Not a PR. Skipping surge deployment." exit 0 fi npm i -g surge export SURGE_LOGIN=test@example.co.in # Token of a dummy account export SURGE_TOKEN=d1c28a7a75967cc2b4c852cca0d12206 export DEPLOY_DOMAIN=https://pr-${TRAVIS_PULL_REQUEST}-fossasia-LoklakSearch.surge.sh surge --project ./dist --domain $DEPLOY_DOMAIN; Here, Travis CI is first installing surge locally by npm i -g surge  and then we are exporting the environment variables SURGE_LOGIN , SURGE_TOKEN and DEPLOY_DOMAIN.…

Continue ReadingInstalling the Loklak Search and Deploying it to Surge

Adding additional information to store listing page of Loklak apps site

Loklak apps site has now got a completely functional store listing page where users can find all relevant information about the app which they want to view. The page has a left side bar which shows various categories to switch between, a right sidebar for suggesting similar kind of apps to users and a middle section to provide users with various important informations about the app like getting started, use of app, promo images, preview images, test link and various other details. In this blog I will be describing how the bottom section of the middle column has been created (related issue: #209). The bottom section The bottom section provides various informations like updated, version, app source, developer information, contributors, technology stack, license. All these informations has to be dynamically loaded for each selected app. As I had previously mentioned here, no HTML content can be hard coded in the store listing page. So how do we show the above mentioned informations for the different apps? Well, for this we will once again use the app.json of the corresponding app like we had done for the middle section here. At first, for a given app we need to define some extra fields in the app.json file as shown below. "appSource": "https://github.com/fossasia/apps.loklak.org/tree/master/MultiLinePlotter", "contributors": [{"name": "djmgit", "url": "http://djmgit.github.io/"}], "techStack": ["HTML", "CSS", "AngularJs", "Morris.js", "Bootstrap", "Loklak API"], "license": {"name": "LGPL 2.1", "url": "https://www.gnu.org/licenses/old-licenses/lgpl-2.1"}, "version": "1.0", "updated": "June 10,2017", The above code snippet shows the new fields included in app.json. The fields are as described below. appSource: Stores link to the source code of the app. Contributors: Stores a list containing objects. Each object stores name of the contributor and an url corresponding to that contributor. techStack: A list containing names of the technologies used. License: Name and link of the license. Version: The current version of the app. Updated: Date on which the app was last updated. These fields provide the source for the informations present in the bottom section of the app. Now we need to render these information on the store listing page. Let us take an example. Let us see how version is rendered. <div ng-if="appData.version !== undefined && appData.version !== ''" class="col-md-4 add-info"> <div class="info-type"> <h5 class="info-header"> <strong>Version</strong> </h5> </div> <div class="info-body"> {{appData.version}} </div> </div> We first check if version field is defined and version is not empty. Then we print a header (Version in this case) and then we print the value. This is how updated, appSource and license are also displayed. What about technology stack and contributors? Technology stack is basically an list and it may contain quite a number of strings(technology names). If we display all the values at once the bottom section will get crowded and it may degrade the UI of the page.To avoid this a popup dialog has been used. When user clicks on the technology stack label, a popup dialogue appears which shows the various technologies used in the app. <div class="info-body"> <div class="dropdown"> <div class="dropdown-toggle" type="button" data-toggle="dropdown"> View technology stack </div>…

Continue ReadingAdding additional information to store listing page of Loklak apps site

Loklak Timeline Using Sphinx Extension In Yaydoc

In Yaydoc, I decided to add option, to show show the twitter timeline which showthe latest twitter feed. But I wanted to implement it using loklak instead of twitter embedded plugin. I started to search for an embedded plugin that exists for loklak. There is no such plugin, hence I built my own plugin. You can see the source code here. Now that I have the plugin, the next phase is to add the plugin to the documentation. Adding the plugin by appending the plugin code to HTML is not viable. Therefore I decided to make Directive for Sphinx which adds a timeline based on the query parameter which user provides. In order to make a Directive, I had to make a Sphinx extension which creates a timeline Directive. The Directive has to look like this .. timeline :: fossasia from docutils import nodes from docutils.parsers import rst class timeline(nodes.General, nodes.Element): pass def visit(self, node): tag=u''' ''' .format(node.display_name) self.body.append(tag) self.visit_admonition(node) def depart(self, node): self.depart_admonition(node) class TimelineDirective(rst.Directive): name = 'timeline' node_class = timeline has_content = True required_argument = 1 optional_argument = 0 final_argument_whitespace = False option_spec = {} def run(self): node = self.node_class() node.display_name = self.content[0] return [node] def setup(app): app.add_javascript("https://cdn.rawgit.com/fossasia/loklak-timeline-plugin/master/plugi n.js") app.add_node(timeline, html=(visit, depart)) app.add_directive('timeline', TimelineDirective) We have to create an empty class for Nodes that inherits`Node.General` and `Node.Elements`. This class is used for storing the value which will be passed by the directive. I wrote a `Visit` function which executes when sphinx visits the `timeline` directive. `Visit` function basically appends the necessary html code needed to render the twitter timeline. Then I created TimelineDirective class which inherits rst.Directive. In that class, I defined a run method which read the argument from the directive and passed it to the node. Finally I defined a setup method which adds the loklak-timeline-plugin js to the render html node, and directive to the sphinx. Setup function has to be defined, in order to detect module as an extension by the sphinx. Resources: Sphinx official extension tutorial Better Documentation Through Automation: Creating Sphinx Extension - Doug Hellmann

Continue ReadingLoklak Timeline Using Sphinx Extension In Yaydoc

Improving Loklak apps site

In this blog I will be describing some of the recent improvements made to the Loklak apps site. A new utility script has been added to automatically update the loklak app wall after a new app has been made. Invalid app query in app details page has been handled gracefully. A proper message is shown when a user enters an invalid app name in the url of the details page. Tests has been added for details page. Developing updatewall script This is a small utility script to update Loklak wall in order to expose a newly created app or update an existing app. Before moving into the working of this script let us discuss how Loklak apps site tracks all the apps and their details. In the root of the project there is a file names apps.json. This file contains an aggregation of all the app.json files present in the individual apps. Now when the site is loaded, index.html loads the Javascript code present in app_list.js. This app_list.js file makes an ajax call to root apps.json files, loads all the app details in a list and attaches this list to the AngularJS scope variable. After this the app wall consisting of various app details is rendered using html. So whenever a new app is created, in order to expose the app on the wall, the developer needs to copy the contents of the application’s app.json and paste it in the root apps.json file. This is quite tedious on the part of the developer as for making a new app he will first have to know how the site works which is not all directly related to his development work. Next, whenever he updates the app.json of his app, he needs to again update apps.json file with the new data. This newly added script (updatewall) automates this entire process. After creating a new app all that the developer needs to do is run this script from within his app directory and the app wall will be updated automatically. Now, let us move into the working of this script. The basic workflow of the updatewall script can be described as follows. The script loads the json data present in the app.json file of the app under consideration. Next it loads the json data present in the root apps.json file. if __name__ == '__main__': #open file containg json object json_list_file = open(PATH_TO_ROOT_JSON, 'r') #load json object json_list = json.load(json_list_file, object_pairs_hook=OrderedDict) json_list_file.close() app_json_file = open(PATH_TO_APP_JSON, 'r') app_json = json.load(app_json_file, object_pairs_hook=OrderedDict) app_json_file.close() #method to update Loklak app wall expose_app(json_list, app_json) When we are loading the json data we are using object_pairs_hook in order to load the data into an OrderedDict rather than a normal python dictionary. We are doing this so that the order of the dictionary items are maintained. Once the data is loaded we invoke the expose method. def expose_app(json_list, app_json): #if app is already present in list then fetch that app app = getAppIfPesent(json_list, app_json) #if app is not present then add…

Continue ReadingImproving Loklak apps site

Using Protractor for UI Tests in Angular JS for Loklak Apps Site

Loklak apps site’s home page and app details page have sections where data is dynamically loaded from external javascript and json files. Data is fetched from json files using angular js, processed and then rendered to the corresponding views by controllers. Any erroneous modification to the controller functions might cause discrepancies in the frontend. Since Loklak apps is a frontend project, any bug in the home page or details page will lead to poor UI/UX. How do we deal with this? One way is to write unit tests for the various controller functions and check their behaviours. Now how do we test the behaviours of the site. Most of the controller functions render something on the view. One thing we can do is simulate the various browser actions and test site against known, accepted behaviours with Protractor. What is Protractor Protractor is end to end test framework for Angular and AngularJS apps. It runs tests against our app running in browser as if a real user is interacting with our browser. It uses browser specific drivers to interact with our web application as any user would. Using Protractor to write tests for Loklak apps site First we need to install Protractor and its dependencies. Let us begin by creating an empty json file in the project directory using the following command. echo {} > package.json Next we will have to install Protractor. The above command installs protractor and webdriver-manager. After this we need to get the necessary binaries to set up our selenium server. This can be done using the following. ./node_modules/protractor/bin/webdriver-manager update ./node_modules/protractor/bin/webdriver-manager start Let us tidy up things a bit. We will include these commands in package.json file under scripts section so that we can shorten our commands. Given below is the current state of package.json { "scripts": { "start": "./node_modules/http-server/bin/http-server", "update-driver": "./node_modules/protractor/bin/webdriver-manager update", "start-driver": "./node_modules/protractor/bin/webdriver-manager start", "test": "./node_modules/protractor/bin/protractor conf.js" }, "dependencies": { "http-server": "^0.10.0", "protractor": "^5.1.2" } } The package.json file currently holds our dependencies and scripts. It contains command for starting development server, updating webdriver and starting webdriver (mentioned just before this) and command to run test. Next we need to include a configuration file for protractor. The configuration file should contain the test framework to be used, the address at which selenium is running and path to specs file. // conf.js exports.config = { framework: "jasmine", seleniumAddress: "http://localhost:4444/wd/hub", specs: ["tests/home-spec.js"] }; We have set the framework as jasmine and selenium address as http://localhost:4444/wd/hub. Next we need to define our actual file. But before writing tests we need to find out what are the things that we need to test. We will mostly be testing dynamic content loaded by Javascript files. Let us define a spec. A spec is a collection of tests. We will start by testing the category name. Initially when the page loads it should be equal to All apps. Next we test the top right hand side menu which is loaded by javascript using topmenu.json file. it("should have a category name", function()…

Continue ReadingUsing Protractor for UI Tests in Angular JS for Loklak Apps Site

Adding download feature to LoklakWordCloud app on Loklak apps site

One of the most important and useful feature that has recently been added to LoklakWordCloud app is enabling the user to download the generated word cloud as a png/jpeg image. This feature will allow the user to actually use this app as a tool to generate a word cloud using twitter data and save it on their disks for future use. All that the user needs to do is generate the word cloud, choose an image type (png or jpeg) and click on export as image, a preview of the image to be downloaded will be displayed. Just hit enter and the word cloud will be saved on your disk. Thus users will not have to use any alternative process like taking a screenshot of the word cloud generated, etc. Presently the complete app is hosted on Loklak apps site. How does it work? What we are doing is, we are exporting a part of the page (a div) as image and saving it. Apparently it might seem that we are taking a screenshot of a particular portion of a page and generating a download link. But actually it is not like that. The word cloud that is being generated by this app via Jqcloud is actually a collection of HTML nodes. Each node contains a word (part of the cloud) as a text content with some CSS styles to specify the size and color of that word. As user clicks on export to image option, the app traverses the div containing the cloud. It collects information about all the HTML nodes present under that div and creates a canvas representation of the entire div. So rather than taking a screenshot of the div, the app recreates the entire div and presents it to us. This entire process is accomplished by a lightweight JS library called html2canvas. Let us have a look into the code that implements the download feature. At first we need to create the UI for the export and download option. User should be able to choose between png and jpeg before exporting to image. For this we have provided a dropdown containing the two options. <div class="dropdown type" ng-if="download"> <div class="dropdown-toggle select-type" data-toggle="dropdown"> {{imageType}} <span class="caret"></span></div> <ul class="dropdown-menu"> <li ng-click="changeType('png', 'png')"><a href="">png</a></li> <li ng-click="changeType('jpeg', 'jpg')"><a href="">jpeg</a></li> </ul> </div> <a class="export" ng-click="export()" ng-if="download">Export as image</a> In the above code snippet, firstly we create a dropdown menu with two list items, png and jpeg. With each each list item we attach a ng-click event which calls changeType function and passes two parameters, image type and extension. The changeType function simply updates the current image type and extension with the selected ones. $scope.changeType = function(type, ext) { $scope.imageType = type; $scope.imageExt = ext; } The ‘export as image’ on clicking calls the export function. The export function uses html2canvas library’s interface to generate the canvas representation of the word cloud and also generates the download link and attaches it to the modal’s save button (described below). After everything is…

Continue ReadingAdding download feature to LoklakWordCloud app on Loklak apps site

Live Feeds in loklak Media wall using ‘source=twitter’

Loklak Server provides pagination to provide tweets from Loklak search.json API in divisions so as to improve response time from the server. We will be taking advantage of this pagination using parameter `source=twitter` of the search.json API on loklak media wall. Basically, using parameter ‘source=twitter’ in the API does real time scraping and provides live feeds. To improve response time, it returns feeds as specified in the count (default is 100). In the blog, I am explaining how implemented real time pagination using ‘source = twitter’ in loklak media wall to get live feeds from twitter. Working First API Call on Initialization The first API call needs to have high count (i.e. maximumRecords = 20) so as to get a higher number of feeds and provide a sufficient amount of feeds to fill up the media wall. ‘source=twitter’ must be specified so that real time feeds are scraped and provided from twitter. http://api.loklak.org/api/search.json?q=fossasia&callback=__ng_jsonp__.__req0.finished&minified=true&source=twitter&maximumRecords=20&timezoneOffset=-330&startRecord=1   If feeds are received from the server, then the next API request must be sent after 10 seconds so that server gets sufficient time to scrap the data and store it in the database. This can be done by an effect which dispatches WallNextPageAction(‘’) keeping debounceTime equal to 10000 so that next request is sent 10 seconds after WallSearchCompleteSuccessAction(). @Effect() nextWallSearchAction$ = this.actions$ .ofType(apiAction.ActionTypes.WALL_SEARCH_COMPLETE_SUCCESS) .debounceTime(10000) .withLatestFrom(this.store$) .map(([action, state]) => { return new wallPaginationAction.WallNextPageAction(''); }); Consecutive Calls To implement pagination, next consecutive API call must be made to add new live feeds to the media wall. For the new feeds, count must be kept low so that no heavy pagination takes place and feeds are added one by one to get more focus on new tweets. For this purpose, count must be kept to one. this.searchServiceConfig.count = queryObject.count; this.searchServiceConfig.maximumRecords = queryObject.count;return this.apiSearchService.fetchQuery(queryObject.query.queryString, this.searchServiceConfig) .takeUntil(nextSearch$) .map(response => { return new wallPaginationAction.WallPaginationCompleteSuccessAction(response); }) .catch(() => of(new wallPaginationAction.WallPaginationCompleteFailAction(''))); });   Here, count and maximumRecords is updated from queryObject.count which varies between 1 to 5 (default being 1). This can be updated by user from the customization menu. Next API request is as follows: http://api.loklak.org/api/search.json?q=fossasia&callback=__ng_jsonp__.__req2.finished&minified=true&source=twitter&maximumRecords=1&timezoneOffset=-330&startRecord=1   Now, as done above, if some response is received from media wall, next request is sent after 10 seconds after WallPaginationCompleteSuccess() from an effect by keeping debounceTime equal to 10000. In the similar way, new consecutive calls can be made by keeping ‘source = twitter’ and keeping count low for getting a proper focus on new feed. Reference Loklak API Documentation: http://loklak.org/api.html Inroduction to Ngrx Effects: https://github.com/ngrx/effects/blob/master/docs/intro.md Documentation on JsonP requests: https://stackoverflow.com/questions/36289495/how-to-make-a-simple-jsonp-asynchronous-request-in-angular-2

Continue ReadingLive Feeds in loklak Media wall using ‘source=twitter’

Posting Tweet from Loklak Wok Android

Loklak Wok Android is a peer harvester that posts collected tweets to the Loklak Server. Not only it is a peer harvester, but also provides users to post their tweets from the app. Images and location of the user can also be attached in the tweet. This blog explains Adding Dependencies to the project 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 User first authorize the application, so that they are able to post tweet from the app. For posting tweet statuses/update API endpoint of twitter is used and for attaching images with tweet media/upload API endpoint is used. As, photos and location can be attached in a tweet, for Android Marshmallow and above we need to ask runtime permissions for camera, gallery and location. The related permissions are mentioned in Manifest file first <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> // for location <uses-feature android:name="android.hardware.location.gps"/> <uses-feature android:name="android.hardware.location.network"/>   If, the device is using an OS below Android Marshmallow, there will be no runtime permissions, the user will be asked permissions at the time of installing the app. Now, runtime permissions are asked, if the user had already granted the permission the related activity (camera, gallery or location) is started. For camera permissions, onClickCameraButton is called @OnClick(R.id.camera) public void onClickCameraButton() { int permission = ContextCompat.checkSelfPermission( getActivity(), Manifest.permission.CAMERA); if (isAndroidMarshmallowAndAbove && permission != PackageManager.PERMISSION_GRANTED) { String[] permissions = { Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE }; requestPermissions(permissions, CAMERA_PERMISSION); } else { startCameraActivity(); } }   To start the camera activity if the permission is already granted, startCameraActivity method is called private void startCameraActivity() { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); File dir = getActivity().getExternalFilesDir(Environment.DIRECTORY_PICTURES); mCapturedPhotoFile = new File(dir, createFileName()); Uri capturedPhotoUri = getImageFileUri(mCapturedPhotoFile); intent.putExtra(MediaStore.EXTRA_OUTPUT, capturedPhotoUri); startActivityForResult(intent, REQUEST_CAPTURE_PHOTO); }   If the user decides to save the photo clicked from camera activity, the photo should be saved by creating a file and its uri is required to display the saved photo. The filename is created using createFileName method private String createFileName() { String timeStamp = new SimpleDateFormat("ddMMyyyy_HHmmss").format(new Date()); return "JPEG_" + timeStamp + ".jpg"; }   and uri is obtained using getImageFileUri private Uri getImageFileUri(File file) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { return Uri.fromFile(file); } else { return FileProvider.getUriForFile(getActivity(), "org.loklak.android.provider", file); } }   Similarly, for the gallery, onClickGalleryButton method is implemented to ask runtime permissions and launch gallery activity if the permission is already granted. @OnClick(R.id.gallery) public void onClickGalleryButton() { int permission = ContextCompat.checkSelfPermission( getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE); if (isAndroidMarshmallowAndAbove && permission != PackageManager.PERMISSION_GRANTED) { String[] permissions = { Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE }; requestPermissions(permissions, GALLERY_PERMISSION); } else { startGalleryActivity(); } }   For starting the gallery activity, startGalleryActivity is used private void startGalleryActivity() { Intent intent = new Intent(); intent.setType("image/*"); intent.setAction(Intent.ACTION_GET_CONTENT); intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); startActivityForResult( Intent.createChooser(intent, "Select images"), REQUEST_GALLERY_MEDIA_SELECTION); }   And finally for location onClickAddLocationButton…

Continue ReadingPosting Tweet from Loklak Wok Android

Implementing 3 legged Authorization in Loklak Wok Android for Twitter

Loklak Wok Android is a peer harvester that posts collected tweets to the Loklak Server. Not only it is a peer harvester, but also provides users to post their tweets from the app. Posting tweets from the app requires users to authorize the Loklak Wok app, the client app created https://apps.twitter.com/ . This blog explains in detail about the authorization process. Adding Dependencies to the project 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' }   Steps of Authorization Step 1: Create client app in Twitter Create a twitter client app at https://apps.twitter.com/. Provide the mandatory entries and also Callback url (would be used in next steps). Then go to “Keys and Access Token” and save your consumer key and consumer secret. In case you want to use Twitter API for yourself, click on “Create my access token”, which provides access token and access token secret. Step 2: Obtaining a request token Using the “consumer key” and “consumer secret” request token is obtained by sending a POST request to oauth/request_token. As Twitter API are Oauth1 based the sent request needs to be signed by generating oauth_signature. The oauth_signature is generated by intercepting the network request sent by retrofit rest API client, the oauth interceptor used in Loklak Wok Android is a modified version of this snippet. The retrofit TwitterAPI interface is defined public interface TwitterAPI { String BASE_URL = "https://api.twitter.com/"; @POST("/oauth/request_token") Observable<ResponseBody> getRequestToken(); @FormUrlEncoded @POST("/oauth/access_token") Observable<ResponseBody> getAccessTokenAndSecret(@Field("oauth_verifier") String oauthVerifier); }   And the retrofit REST client is implemented in TwitterRestClient. createTwitterAPIWithoutAccessToken method returns a twitter API client which can be called without providing access keys, this is used as we don’t have access tokens right now. public static TwitterAPI createTwitterAPIWithoutAccessToken() { if (sWithoutAccessTokenRetrofit == null) { sLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); // uncomment to debug network requests // sWithoutAccessTokenClient.addInterceptor(sLoggingInterceptor); sWithoutAccessTokenRetrofit = sRetrofitBuilder .client(sWithoutAccessTokenClient.build()).build(); } return sWithoutAccessTokenRetrofit.create(TwitterAPI.class); }   So, getRequestToken method is used to obtain the request token, if the request is successful oauth_token is returned. @OnClick(R.id.twitter_authorize) public void onClickTwitterAuthorizeButton(View view) { mTwitterApi.getRequestToken() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(this::parseRequestTokenResponse, this::onFetchRequestTokenError); }   Step 3: Redirecting the user Using the oauth_token obtained in Step 2, the user is redirected to login page using WebView. private void setAuthorizationView() { ... webView.setVisibility(View.VISIBLE); webView.loadUrl(mAuthorizationUrl); }   A WebView client is created by extending WebViewClient, this is used to keep track of which webpage is opened by overriding shouldOverrideUrlLoading. @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.contains("github")) { String[] tokenAndVerifier = url.split("&"); mOAuthVerifier = tokenAndVerifier[1].substring(tokenAndVerifier[1].indexOf('=') + 1); getAccessTokenAndSecret(); return true; } return false; }   As the link provided in callback url while creating our twitter app is a github page. The WebViewClient checks if it is a github page or not. If yes, then it parses the oauth_verifier from the github url. Step 4: Converting the request token to an access token A…

Continue ReadingImplementing 3 legged Authorization in Loklak Wok Android for Twitter

Enhancing LoklakWordCloud app present on Loklak apps site

LoklakWordCloud app is presently hosted on loklak apps site. Before moving into the content of this blog, let us get a brief overview of the app. What does the app do? The app generates a word cloud using twitter data returned by loklak based on the query word provided by the user. The user enters a word in the input field and presses the search button. After that a word cloud is created using the content (text body, hashtags and mentioned) of the various tweets which contains the user provided query word. In my previous post I wrote about creating the basic functional app. In this post I will be describing the next steps that have been implemented in the app. Making the word cloud clickable This is one of the most important and interesting features added to the app. The words in the cloud are now clickable.Whenever an user clicks on a word present in the cloud, the cloud is replaced by the word cloud of that selected word. How do we achieve this behaviour? Well, for this we use Jqcloud’s handler feature. While creating the list of objects for each word and its frequency, we also specify a handler corresponding to each of the word. The handler is supposed to handle a click event. Whenever a click event occurs, we set the value of $scope.tweet to the selected word and invoke the search function, which calls the loklak API and regenerates the word cloud. for (var word in $scope.wordFreq) { $scope.wordCloudData.push({ text: word, weight: $scope.wordFreq[word], handlers: { click: function(e) { $scope.tweet = e.target.textContent; $scope.search(); } } }); } As it can be seen in the above snippet, handlers is simply an JavaScript object, which takes a function for the click event. In the function we pass the word selected as value of the tweet variable and call search method. Adding filters to the app Previously the app generated word cloud using the entire tweet content, that is, hashtags, mentions and tweet body. Thus the app was not flexible. User was not able to decide on which field he wants his word cloud to be generated. User might want to generate his  word cloud using only the hashtags or the mentions or simply the tweet body. In order to make this possible, filters have been introduced. Now we have filters for hashtags, mentions, tweet body and date. <div class="col-md-6 tweet-filters"> <strong>Filters</strong> <hr> <div class="filters"> <label class="checkbox-inline"><input type="checkbox" value="" ng-model="hashtags">Hashtags</label> <label class="checkbox-inline"><input type="checkbox" value="" ng-model="mentions">Mentions</label> <label class="checkbox-inline"><input type="checkbox" value="" ng-model="tweetbody">Tweet body</label> </div> <div class="filter-all"> <span class="select-all" ng-click="selectAll()"> Select all </span> </div> </div> We have used checkboxes for the individual filters and have kept an option to select all the filters at once. Next we require to hook this HTML to AngularJS code to make the filters functional. if ($scope.hashtags) { tweet.hashtags.forEach(function (hashtag) { $scope.filteredWords.push("#" + hashtag); }); } if ($scope.mentions) { tweet.mentions.forEach(function (mention) { $scope.filteredWords.push("@" + mention); }); } In the above snippet, before adding the hashtags to the list…

Continue ReadingEnhancing LoklakWordCloud app present on Loklak apps site