Auto Updating SUSI Android APK and App Preview on appetize.io

This blog will cover the way in which the SUSI Android APK is build automatically after each commit and pushed to “apk” branch in the github repo. Other thing which will be covered is that how the app preview on appetize.io can be updated after each commit. This is basically for the testers who wish to test the SUSI Android App. There are four ways to test the SUSI Android App. One is to simply download the alpha version of the app from the Google PlayStore. Here is the link to the app. Join the alpha testing and report bugs on the github issue tracker of the repo. Other way is to build the app from Android Studio but you may need to set the complete project. If you are looking to contribute in the project, this is the advised way to test the app. The other two ways are explained below. Auto Building of APK and pushing to “apk” branch We have written a script which does following steps whenever a PR is merged: Checks if the commit is of a PR or a commit to repo If not of PR, configures a user whose github account will be used to push the APKs. Clones the repo, generates the debug and release APK. Deletes everything in the apk branch. Commits and Pushes new changes to apk branch. This script is written for people or testers who do not have android studio installed in their computer and want to test the app. So, they can directly download the apk from the apk branch and install it in their phone. The APK is always updated after each commit. So, whenever a tester downloads the APK from apk branch, he will always get the latest app. if [[ $CIRCLE_BRANCH != pull* ]] then git config --global user.name "USERNAME" git config --global user.email "EMAIL" git clone --quiet --branch=apk https://USERNAME:$GITHUB_API_KEY@github.com/fossasia/susi_android apk > /dev/null ls cp -r ${HOME}/${CIRCLE_PROJECT_REPONAME}/app/build/outputs/apk/app-debug.apk apk/susi-debug.apk cp -r ${HOME}/${CIRCLE_PROJECT_REPONAME}/app/build/outputs/apk/app-release-unsigned.apk apk/susi-release.apk cd apk git checkout --orphan workaround git add -A git commit -am "[Circle CI] Update Susi Apk" git branch -D apk git branch -m apk git push origin apk --force --quiet > /dev/null fi Auto Updating of App Preview on appetize.io The APKs generated in the above step can now be used to set up the preview of the app on the appetize.io. Appetize.io is an online simulator to run mobile apps ( IOS and Android). Appetize.io provides a nice virtual mobile frame to run native apps with various options like screen size, mobile, OS version, etc. Appetize.io provides some API to update/publish the app. In SUSI, we once uploaded the app on appetize.io and now we are using the API provided by them to update the APK everytime a commit is pushed in the repository. API information (Derived from official docs of appetize.io): You may upload a new version of an existing app, or update app settings. Send an HTTP POST request to https://APITOKEN@api.appetize.io/v1/apps/PUBLICKEY Replace APITOKEN with your API token and…

Continue ReadingAuto Updating SUSI Android APK and App Preview on appetize.io

Implementing Skill Detail Section in SUSI Android App

SUSI Skills are rules that are defined in SUSI Skill Data repo which are basically the responses SUSI gives to the user queries. When a user queries something from the SUSI Android app, a query to SUSI Server is made which further fetches response from SUSI Skill Data and gives the response to the app. Similarly, when we need to list all skills, an API call is made to server to list all skills. The server then checks the SUSI Skill Data repo for the skills and then return all the required information to the app. Then the app displays all the information about the skill to user. User then can view details of each skill and then interact on the chat interface to use that skill. This process is similar to what SUSI Skill CMS does. The CMS is a skill wiki like interface to view all skills and then edit them. Though the app can not be currently used to edit the skills but it can be used to view them and try them on the chat interface. API Information For listing SUSI Skill groups, we have to call on /cms/getGroups.json This will give you all groups in SUSI model in which skills are present. Current response: { "session": {"identity": { "type": "host", "name": "14.139.194.24", "anonymous": true }}, "accepted": true, "groups": [ "Small Talk", "Entertainment", "Problem Solving", "Knowledge", "Assistants", "Shopping" ], "message": "Success: Fetched group list" } So, the groups object gives all the groups in which SUSI Skills are located. Next comes, fetching of skills. For that the endpoint is /cms/getGroups.json?group=GROUP_NAME Since we want all skills to be fetched, we call this api for every group. So, for example we will be calling http://api.susi.ai/cms/getSkillList.json?group=Entertainment for getting all skills in group “Entertainment”. Similarly for other groups as well. Sample response of skill: { "accepted": true, "model": "general", "group": "Shopping", "language": "en", "skills": {"amazon_shopping": { "image": "images/amazon_shopping.png", "author_url": "https://github.com/meriki", "examples": ["Buy a dress"], "developer_privacy_policy": null, "author": "Y S Ramya", "skill_name": "Shop At Amazon", "dynamic_content": true, "terms_of_use": null, "descriptions": "Searches items on Amazon.com for shopping", "skill_rating": null }}, "message": "Success: Fetched skill list", "session": {"identity": { "type": "host", "name": "14.139.194.24", "anonymous": true }} } It gives all details about skills: image author_url examples developer_privacy_policy author skill_name dynamic_content terms_of_use descriptions skill_rating Implementation in SUSI Android App Skill Detail Section UI of Google Assistant Skill Detail Section UI of SUSI SKill CMS Skill Detail Section UI of SUSI Android App The UI of skill detail section in SUSI Android App is the mixture of UI of Skill detail section in Google Assistant ap and SUSI Skill CMS. It displays details of skills in a beautiful manner with horizontal recyclerview used to display the examples. So, we have to display following details about the skill in Skill Detail Section: Skill Name Author Name Skill Image Try it Button Description Examples Rating Content type (Dynamic/Static) Terms of Use Developer’s Privacy policy Let’s see the implementation. 1. Whenever a skill Card View is clicked, showSkillDetailFragment()…

Continue ReadingImplementing Skill Detail Section in SUSI Android App

Implementing Change Password Feature in SUSI Android App using Custom Dialogs

Recently a new servlet was implemented on the SUSI Server about changing the password of the logged in user. This feature comes in handy to avoid unauthorized usage of the SUSI Account. Almost all the online platforms have this feature to change the password to avoid notorious user to unethical use someone else’s account. In SUSI Android app this new API was used with a nice UI to change the password of the user. The process is very simple and easy to grasp. This blog will try to cover the API information and implementation of the Change Password feature in the android client. API Information For changing the password of SUSI Account of the user, we have to call on  /aaa/changepassword.json We have to provide three parameters along with this api call: changepassword:  Email of user (type string) using which user is logged in. password:  Old password (type string with min length of 6) of the user. newpassword: New password (type string with min length of 6) of the user. access_token: An encrypted access_token indicating user is logged in. Sample Response (Success) { "session": {"identity": { "type": "email", "name": "YOUR_EMAIL_ADDRESS", "anonymous": false }}, "accepted": true, "message": "Your password has been changed!" } Error Response (Failure). This happens when user is not logged in: HTTP ERROR 401 Problem accessing /aaa/changepassword.json. Reason: Base user role not sufficient. Your base user role is 'ANONYMOUS', your user role is 'anonymous' Implementation in SUSI Android App The change password option is located in Settings Activity and displayed only when user is logged in. So, if a logged in user wants to change the password of his/her SUSI AI account, he/she can simply go to the Settings and click on the option. Clicking on the options open up a dialog box with 3 input layouts for: Current Password New Password Confirm New Password So, user can simply add these three inputs and click “Ok”. This will change the password of their account. Let’s see some code explanation. When user clicks on the “reset password” option from the settings, the showResetPasswordAlert() method is called which displays the dialog. And when user clicks on the “OK” button the resetPassword method() in the presenter is called passing input from the three input layout as parameters. settingsPresenter.resetPassword(password.editText?.text.toString(), newPassword.editText?.text.toString(), conPassword.editText?.text.toString()) fun showResetPasswordAlert() { val builder = AlertDialog.Builder(activity) val resetPasswordView = activity.layoutInflater.inflate(R.layout.alert_reset_password, null) password = resetPasswordView.findViewById(R.id.password) as TextInputLayout newPassword = resetPasswordView.findViewById(R.id.newpassword) as TextInputLayout conPassword = resetPasswordView.findViewById(R.id.confirmpassword) as TextInputLayout builder.setView(resetPasswordView) builder.setTitle(Constant.CHANGE_PASSWORD) .setCancelable(false) .setNegativeButton(Constant.CANCEL, null) .setPositiveButton(getString(R.string.ok), null) resetPasswordAlert = builder.create() resetPasswordAlert.show() setupPasswordWatcher() resetPasswordAlert.getButton(AlertDialog.BUTTON_POSITIVE)?.setOnClickListener { settingsPresenter.resetPassword(password.editText?.text.toString(), newPassword.editText?.text.toString(), conPassword.editText?.text.toString()) } } In the resetPassword method, all details about the passwords are checked like: If passwords are not empty. If passwords’ lengths are greater than 6. If new password and confirmation new password matches     When all the conditions are satisfied and all the inputs are valid, resetPassword() in model is called which makes network call to change password of the user. settingModel.resetPassword(password,newPassword,this) override fun resetPassword(password: String, newPassword: String, conPassword: String) { if (password.isEmpty()) { settingView?.invalidCredentials(true,…

Continue ReadingImplementing Change Password Feature in SUSI Android App using Custom Dialogs

Implementing Skill Listing in SUSI Android App using Nested RecyclerViews

SUSI Skills are rules that are defined in SUSI Skill Data repo which are basically the responses SUSI gives to the user queries. When a user queries something from the SUSI Android app, a query to SUSI Server is made which further fetches response from SUSI Skill Data and gives the response to the app. Similarly, when we need to list all skills, an API call is made to server to list all skills. The server then checks the SUSI Skill Data repo for the skills and then return all the required information to the app. Then the app displays all the information about the skill to user. User then can view details of each skill and then interact on the chat interface to use that skill. This process is similar to what SUSI Skill CMS does. The CMS is a skill wiki like interface to view all skills and then edit them. Though the app can not be currently used to edit the skills but it can be used to view them and try them on the chat interface. API Information For listing SUSI Skill groups, we have to call on  /cms/getGroups.json This will give you all groups in SUSI model in which skills are present. Current response: { "session": {"identity": { "type": "host", "name": "14.139.194.24", "anonymous": true }}, "accepted": true, "groups": [ "Small Talk", "Entertainment", "Problem Solving", "Knowledge", "Assistants", "Shopping" ], "message": "Success: Fetched group list" } So, the groups object gives all the groups in which SUSI Skills are located. Next comes, fetching of skills. For that the endpoint is /cms/getGroups.json?group=GROUP_NAME Since we want all skills to be fetched, we call this api for every group. So, for example we will be calling http://api.susi.ai/cms/getSkillList.json?group=Entertainment for getting all skills in group “Entertainment”. Similarly for other groups as well. Sample response of skill: { "accepted": true, "model": "general", "group": "Shopping", "language": "en", "skills": {"amazon_shopping": { "image": "images/amazon_shopping.png", "author_url": "https://github.com/meriki", "examples": ["Buy a dress"], "developer_privacy_policy": null, "author": "Y S Ramya", "skill_name": "Shop At Amazon", "dynamic_content": true, "terms_of_use": null, "descriptions": "Searches items on Amazon.com for shopping", "skill_rating": null }}, "message": "Success: Fetched skill list", "session": {"identity": { "type": "host", "name": "14.139.194.24", "anonymous": true }} } It gives all details about skills: image author_url examples developer_privacy_policy author skill_name dynamic_content terms_of_use descriptions skill_rating Implementation in SUSI Android App Skill Listing UI of Google Assistant Skill Listing UI of SUSI SKill CMS Skill Listing UI of SUSI Android App The UI of skill listing in SUSI Android App is the mixture of UI of Skill listing in Google Assistant ap and SUSI Skill CMS. It displays skills in a beautiful manner with horizontal recyclerview nested in vertical recyclerview. So, for implementing horizontal recyclerview inside vertical recyclerview, you need two viewholders and two adapters (one each for a recyclerview). Let’s see the implementation. 1. First task is to fetch the information of groups in which skills are located. This line calls method in SkillListModel which then makes an API call to fetch groups. skillListingModel.fetchGroups(this) 2.…

Continue ReadingImplementing Skill Listing in SUSI Android App using Nested RecyclerViews

Implementing Feedback Feature in SUSI Android App

Recently, on SUSI Server, a new servlet was added which is used to rate SUSI Skills either positive or negative. The server stores the rating of a particular skill in a JSON file. These ratings help in improving answers provided by SUSI. So, the server part is done and it was required to implement this in the SUSI Android App. In this blog, I will cover the topic of implementation of the Rating or Feedback feature in SUSI Android App. This will including all the cases when feedback should be sent, when it should not be sent, when to send positive feedback, when to send negative feedback, etc. API Information For rating a SUSI Skill, we have to call on  /cms/rateSkill.json providing 5 parameters which are: model: The model of SUSI Skill. (String) group: The Group under the model in which that particular skill resides. (String) language: The language of skill. (String) skill: This is skill name. (String) rating: This can be two strings, either “positive” or “negative”. String) Basically, in the SUSI Skill Data repo (in which all the skills are stored), models, groups language etc are part of folder structure. So, if a skill is located here https://github.com/fossasia/susi_skill_data/blob/master/models/general/Knowledge/en/news.txt This would mean model = general group = Knowledge language = en skill = news rating = positive/negative Implementation in SUSI Android App      So, when the like button on a particular skill is clicked, a positive call is made and when the dislike button is clicked, a negative call is made. Let’s see example when the thumbs up button or like button is clicked. There can be three cases possible: None of Like button or dislike button is clicked already: In this case, initially, both like and dislike button will be transparent/hollow. So, when like button is clicked, the like button will be colored blue and a call will be made with positive feedback. Like button is already clicked: In this case, like button is already clicked. So, it will already be blue. So, when user clicks again on positive button, it should get back to normal/hollow indicating rating which was sent is cancelled and a a call will be made with negative feedback thus cancelling or neutralizing the earlier, positive feedback. Dislike button is already clicked: In this case, the dislike button is already blue, indicating a negative call is already made. So, now when the like button is clicked, we need to cancel the earlier negative feedback call and sending another negative feedback call. Thus, sending two negative feedback calls. And after that coloring dislike button as blue. Look at the code below. It is self explanatory. There are three if-else conditions covering all the above mentioned three cases. thumbsUp.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { thumbsUp.setImageResource(R.drawable.thumbs_up_solid); if(!model.isPositiveRated() && !model.isNegativeRated()) { rateSusiSkill(Constant.POSITIVE, model.getSkillLocation(), context); setRating(true, true); } else if(!model.isPositiveRated() && model.isNegativeRated()) { setRating(false, false); thumbsDown.setImageResource(R.drawable.thumbs_down_outline); rateSusiSkill(Constant.POSITIVE, model.getSkillLocation(), context); sleep(500); rateSusiSkill(Constant.POSITIVE, model.getSkillLocation(), context); setRating(true, true); } else if (model.isPositiveRated() && !model.isNegativeRated()) { rateSusiSkill(Constant.NEGATIVE, model.getSkillLocation(), context);…

Continue ReadingImplementing Feedback Feature in SUSI Android App

Introduction To Kotlin in SUSI Android App

Lately, we wrote some of the code of SUSI Android App in Kotlin. Kotlin is a very similar language to Java but with much more advantages than Java. It is easy to adapt and learn. There is no doubt that Kotlin is better than Java but with the announcement of Kotlin Support in Google IO’17 for Android development, Kotlin seems a decent way to write code for an Android App. Advantages of Kotlin over Java Reduce Boilerplate Code: It helps making development of app faster as it reduces more than 20 percent of boilerplate code. Writing long statements again and again is a headache for developers. Kotlin comes to rescue in that situation. Removes Null Pointer Exception: Once a large company faced millions of dollars of loss due to null pointer exception. It causes crashes of apps more often than anything else. Thus Kotlin helps in Null checks and makes app free from Null pointer Exceptions. Interoperable with Java: Kotlin code and Java code are interoperable. Which means you can write half your code in kotlin and half in Java and it will work like a charm. You can call java methods from Kotlin code and vice versa. So, you can simply move your existing Java based app to Kotlin slowly making your app always running. Lambda and Inline functions: Yes, Kotlin also has functionalities from functional programming languages. Mainly and most widely used feature of those languages is Lambda functions. Direct Reference of Views by Id: You do not need to write findViewById(R.id.view_name) or use any other library like Butterknife for view binding. You can simply use the view by its id. No semicolon:  Last but not the least, you do not need to add a semicolon after each statement. In fact, you do not need to add semicolon at all. Setting up Android Studio to work with Kotlin If you have latest Android Studio Canary Version, there is already a build support for Kotlin in it. You need not do anything in that case. But if you don't have the Canary version, you can add Kotlin Plugin in your Android Studio. Follow the below steps to do that. Install the Kotlin Plugin: Android Studio → Preferences… →Plugins → Browse Repository → type “Kotlin” in search box → install Restart your Android Studio and Rebuild the project. Everything else is already set up in SUSI Android App but if you want to do it for your other apps, follow this link. Implementation in SUSI Android App So, I am not going to give unnecessary code but will point out specific things where Kotlin helped a lot to reduce unnecessary code and made the code compact. 1. Listeners: Earlier with Java Button signup = (Button) findViewById(R.id.sign_up); signup.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(LoginActivity.this, SignUpActivity.class)); } }); Now, with Kotlin fun signUp() { sign_up.setOnClickListener { startActivity(Intent(this@LoginActivity, SignUpActivity::class.java)) } } 2. Models With Java public class MapData { private double latitude; private double longitude; private double zoom; public…

Continue ReadingIntroduction To Kotlin in SUSI Android App

Auto Deployment of SUSI Server using Kubernetes on Google Cloud Platform

Recently, we auto deployed SUSI Server on Google Cloud Platform using Kubernetes and Docker Images after each commit in the GitHub repo with the help of Travis Continuous Integration. So, basically, whenever a new commit is added to the repo, during the Travis build, we build the docker image of the server and then use it to deploy the server on Google Cloud Platform. We use Kubernetes for deployment since it is very easy to scale up the Project when traffic on the server is increased and Docker because using it we can easily build docker images which then can be used to update the deployment. This schematic will make things more clear what exactly is the procedure. Prerequisites You must be signed in to your Google Cloud Console and have enabled billing and must have credits left in your account. You must have a docker account and a repo in it. If you don’t have one, make it now. You should have enabled Travis on your repo and have a Travis.yml file in your repo. You must already have a project in Google Cloud Console. Make a new one if you don’t have. Pre Deployment Steps You will be needed to do some work on Google Cloud Platform before actually starting the auto deployment process. Those are: Creating a new Cluster. Adding and Formatting Persistence Disk Adding a Persistent Volume CLaim (PVC) Labeling a node as primary. Check out this documentation on how to do that. It may help. Implementation Img src: https://cloud.google.com/solutions/continuous-delivery-with-travis-ci 1. The first step is simply to add this line in Travis.yml file and create an empty deploy.sh, file mentioned below. after_success: - bash kubernetes/travis/deploy.sh Now we’ll be moving line by line and adding commands in the empty deploy.sh file that we created in the previous step. 2. Next step is to remove obsolete Google Cloud files and install Google Cloud SDK and kubectl command. Use following lines to do that. echo ">>> Removing obsolete gcoud files" sudo rm -f /usr/bin/git-credential-gcloud.sh sudo rm -f /usr/bin/bq sudo rm -f /usr/bin/gsutil sudo rm -f /usr/bin/gcloud echo ">>> Installing new files" curl https://sdk.cloud.google.com | bash; source ~/.bashrc gcloud components install kubectl 3. In this step you will be needed to download a JSON file which contains your Google Cloud Credentials, then copy that file to your repo and encrypt it using Travis encryption keys. Follow https://youtu.be/7U4jjRw_AJk this video to see how to do that. 4. So, now you have added your encrypted credentials.json files in your repo and now you need to use those credentials to login into your google cloud account. So, use below lines to do that. echo ">>> Decrypting credentials and authenticating gcloud account" # Decrypt the credentials we added to the repo using the key we added with the Travis command line tool openssl aes-256-cbc -K $encrypted_YOUR_key -iv $encrypted_YOUR_iv -in ./kubernetes/travis/Credentials.json.enc -out Credentials.json -d gcloud auth activate-service-account --key-file Credentials.json export GOOGLE_APPLICATION_CREDENTIALS=$(pwd)/Credentials.json #add gcoud project id gcloud config set project YOUR_PROJECT_ID gcloud container clusters get-credentials YOUR_CONTAINER…

Continue ReadingAuto Deployment of SUSI Server using Kubernetes on Google Cloud Platform

Encoding and Saving Images as Strings in Preferences in SUSI Android App

In this blog post, I’ll be telling about how to store images in preferences by encoding them into Strings and also how to retrieve them back. Many a times, you need to store an image in preferences for various purposes and then need to retrieve it back when required. In SUSI Android App, we need to store an image in preference to set the chat background. We just simply select image from gallery, convert image to a byte array, then do a Base 64 encoding to string, store it in preferences and later decode it and set the chat background. Base64 Encoding-Decoding in Java You may already know what Base 64 is but still here is a link to Wikipedia article explaining it. So, how to do a Base64 encoding-decoding in java? For that java has a class with all such methods already present. https://docs.oracle.com/javase/8/docs/api/java/util/Base64.html According to the docs: This class consists exclusively of static methods for obtaining encoders and decoders for the Base64 encoding scheme. The implementation of this class supports the following types of Base64 as specified in RFC 4648 and RFC 2045. Basic URL and Filename safe MIME So, you may just use Base64.encode to encode a byte array and Base64.decode to decode a byte array. Implementation 1. Selecting image from gallery      Start Image Picker Intent and pick an image from gallery. After selecting you may also start a Crop Intent to crop image also. After selecting and cropping image, you will get a URI of the image. override fun openImagePickerActivity() { val i = Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI) startActivityForResult(i, SELECT_PICTURE) } val thePic = data.extras.getParcelable<Bitmap>("data") val encodedImage = ImageUtils.Companion.cropImage(thePic) chatPresenter.cropPicture(encodedImage) 2. Getting image from the URI using inputstream Next step is to get the image from file using URI from the previous step and InputStream class and store it in a BitMap variable. val imageStream: InputStream = context.contentResolver.openInputStream(selectedImageUri) val selectedImage: Bitmap val filePathColumn = arrayOf(MediaStore.Images.Media.DATA) val cursor = context.contentResolver.query(getImageUrl(context.applicationContext, selectedImageUri), filePathColumn, null, null, null) cursor?.moveToFirst() selectedImage = BitmapFactory.decodeStream(imageStream) 3. Converting the bitmap to ByteArray Now, just convert the Bitmap thus obtained to a ByteArray using below code. val baos = ByteArrayOutputStream() selectedImage.compress(Bitmap.CompressFormat.JPEG, 100, baos) val b = baos.toByteArray() 4. Base64 encode the ByteArray and store in preference Encode the the byte array obtained in last step to a String and store it in preferences. val encodedImage = Base64.encodeToString(b, Base64.DEFAULT) //now you have a string. You can store it in preferences 5. Decoding the String to image Now whenever you want, you can just decode the stored Base64 encoded string to a byte array and then from byte array to a bitmap and use wherever you want. fun decodeImage(context: Context, previouslyChatImage: String): Drawable { val b = Base64.decode(previouslyChatImage, Base64.DEFAULT) val bitmap = BitmapFactory.decodeByteArray(b, 0, b.size) return BitmapDrawable(context.resources, bitmap) } Summary So, the main aim of this blog was to give an idea about how can you store images in preferences. There is no way to store them directly. So, you have to convert them to String by encoding…

Continue ReadingEncoding and Saving Images as Strings in Preferences in SUSI Android App

Implementation of Text to Speech alongside Hotword Detection in SUSI Android App

In this blog post, we’ll be learning about how to implement Text to speech. Now you may be wondering that what is so difficult in implementing text to speech. One can easily find many tutorials on that and can easily look at the official documentation of TTS but there’s a catch here. In this blog post I’ll be telling about how to implement Text to Speech alongside Hotword Detection. Let me give you a rough idea about how hotword detection works in SUSI Android App. For more details, read my other blog here on Hotword Detection. So, there is a constantly running background recording thread which detects when hotword is detected. Now, you may be thinking why do we need to stop that thread for text to speech. Well there are 2 reasons to do that: Recording while playing causing problems with mic and may crash the app. Suppose we even implement that but what will happen if the answer contains word “susi” in it. Now, the hotword will be detected because the speech output contained word “susi” in it (which is our hotword). So, to avoid these problems we had to come up a way to stop hotword detection only for that particular time when SUSI is giving speech output and resume it back immediately when speech output is finished. Let’s see how we did that. Implementation Check out this video to see how this work in the app https://youtu.be/V9N6K4SzpXw Initiating the TTS engine The first task is to initiate the Text to speech engine. This process takes some time. So, it is done in the starting of app in a new handler. new Handler().post(new Runnable() { @Override public void run() { textToSpeech = new TextToSpeech(getApplicationContext(), new TextToSpeech.OnInitListener() { @Override public void onInit(int status) { if (status != TextToSpeech.ERROR) { Locale locale = textToSpeech.getLanguage(); textToSpeech.setLanguage(locale); } } }); } }); Check Audio Focus The next step is to check whether audio focus is granted. Suppose there is some music playing in the background, in that case we won’t be able to give voice output. So, we check audio focus using below code. final AudioManager audiofocus = (AudioManager) getSystemService(Context.AUDIO_SERVICE); int result = audiofocus.requestAudioFocus(afChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { //DO WORK HERE } Using OnAudioFocusChangeListener, we keep a track of when we have access to give speech output and when we don’t. private AudioManager.OnAudioFocusChangeListener afChangeListener = new AudioManager.OnAudioFocusChangeListener() { public void onAudioFocusChange(int focusChange) { if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT) { textToSpeech.stop(); } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) { // Resume playback } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) { textToSpeech.stop(); } } }; Converting the given text to speech Now we have audio focus, we just have to convert given text to speech. Use method textToSpeech.speak(). private void voiceReply(final String reply) { Handler handler = new Handler(); handler.post(new Runnable() { @Override public void run() { textToSpeech.speak(spokenReply, TextToSpeech.QUEUE_FLUSH, ttsParams); } } }); } } Abandon Audio Focus Now we are done with speech output, it’s time we abandon audio focus. audiofocus.abandonAudioFocus(afChangeListener); TTS…

Continue ReadingImplementation of Text to Speech alongside Hotword Detection in SUSI Android App

Search Functionalities in SUSI Android App Using Android SearchView Widget

Searching is a common feature that is required in most applications. But the problem in implementing searching functionality is that there is no common way to do that. People fight over whose way is best to implement search functionality. In this blog post we’ll be looking at how search functionality works in SUSI Android App and how is it implemented. We have used Android’s SearchView widget to do that. There are many other ways to do so but this one is best suited for our requirements. Let’s see how it works. UI Components used for Searching 1. Search icon (magnifying glass icon) In the action bar, you can see a small icon. Clicking on the icon initiates search. 2. Edit text An Obvious requirement is an edit test to enter search query. 3. Up and Down arrow keys Required to search through the whole app. Simply use the up and down arrow keys to navigate through the app and find out each occurrence of the word you want to search.               4. Cross Button Last but not the least, a close or cross button to close the search action. Implementation We have used Android’s inbuilt Widget SearchView. According to official android documentation “A widget that provides a user interface for the user to enter a search query and submit a request to a search provider. Shows a list of query suggestions or results, if available, and allows the user to pick a suggestion or result to launch into.” This widget makes searching a lot easier. It provides all methods and listeners which are actually required for searching. Let’s cover them one by one. Starting the search: searchView.setOnSearchClickListener Listener simply activates when a user clicks on search icon in the toolbar. Do all your work which needs to be done at the starting of the search like, hiding some other UI elements of doing an animation inside the listener searchView.setOnSearchClickListener({ chatPresenter.startSearch() }) Stop the Search: searchView.setOnCloseListener Listener gets activated when a user clicks on the cross icon to close the search. Add all the code snippet you want which is needed to be executed when the search is closed inside this like maybe notify the adapter about data set changes or closing the database etc. searchView.setOnCloseListener({ chatPresenter.stopSearch() false })  Searching a query:  searchView.setOnQueryTextListener Listener overrides 2 methods: 3.1 onQueryTextSubmit: As the name suggests, this method is called when the query to be searched is submitted. 3.2 onQueryTextChange: This method is called when query you are writing changes. We, basically wanted same thing to happen if user has submitted the query or if he is still typing and that is to take the query at that particular moment, find it in database and highlight it. So, chatPresenter.onSearchQuerySearched(query) this method is called in both onQueryTextSubmit and onQueryTextSubmit  to do that. searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String): Boolean { //Handle Search Query chatPresenter.onSearchQuerySearched(query) recyclerAdapter.query = query return false } override fun onQueryTextChange(newText: String): Boolean { if…

Continue ReadingSearch Functionalities in SUSI Android App Using Android SearchView Widget