Monday, November 26, 2018

Android fingerprint sensor dialog translations

Date : 27 / 11 / 2018

HI,

As part of my project I was assigned to implement fingerprint authentication to android app. Since this app should support different countries I had to add the translations text to this dialog also. That's where I encountered a problem which took me more time than I expected. Although Setting title, subtitle and few other messages were easy, setting few strings were not easy as I thought.

This is the easy part.

 val biometricPromptInfo = BiometricPrompt.PromptInfo.Builder()  
         .setTitle("Main Title")  
         .setSubtitle("Here is subtitle")  
         .setDescription("This is the description")  
         .setNegativeButtonText(getString(R.string.cancel))  
         .build()  

Which gives us following dialog prompt.


We can surely add the translations for all the gives setters. But I couldn't get a setter for "Touch the fingerprint sensor". This is where I began to search how could I change this text and add translation for that text. Since there was no search result for this particular problem in internet, I searched in the android files. I found the string value for this TextView which is highlighted, in a generated file located in
C:\Users\yourName\AppName\app\build\generated\not_namespaced_r_class_sources\debug\processDebugResources\r\androidx\biometric.



so the rest was easy. I have added string with same id with desired values  in mu strings.xml files.

Later I also found this link on github which could be useful.

Hope this is useful to someone.

Happy coding :) ......




Wednesday, November 14, 2018

Andorid - Getting error response body with retrofit and moshi using kotlin

Date : 15 / 11 / 2018

Hi,

Recently I was assigned to get a response error object using retrofit. Although there were some artciles and SO questions regard to this, I hardly find a usage  of  Kotlin, Retrofit and Moshi combination. Therefore I am writing this blog how to use this combination.
(I'm showing my actual working code here and the code should be self-explanatory. I am putting notes under every code snippet where necessary and explain a bit more)

So this is the response which I get when something goes wrong.


Here is the error response class which is reusable.

 @JsonClass(generateAdapter = true)  
 data class BaseResponse<T>(  
     @Json(name = "statusCode") val statusCode: Int,  
     @Json(name = "result") val result: T? = null,  
     @Json(name = "errorCode") val errorCode: String? = null  
 )  

Here is the JsonParser class which is responsible for json converting.

 class JsonParser {  
   fun moshi() = Moshi.Builder().build()  
   inline fun <reified T> toBaseResponseError(json: BufferedSource): BaseResponse<T>? {  
     return BaseResponseJsonAdapter<T>(moshi(), arrayOf(T::class.java)).fromJson(json)  
   }  
  // other json converters  
 }  

Here is how to use that error response converter object.

 Service(this, Utils().getAccessToken(this)).getUser(  
       object : retrofit2.Callback<BaseResponse<HealthPersonInfo>> {  
         override fun onFailure(call: Call<BaseResponse<HealthPersonInfo>>, t: Throwable) {  
           hideProgress()  
         }  
         override fun onResponse(  
           call: Call<BaseResponse<HealthPersonInfo>>,  
           response: Response<BaseResponse<HealthPersonInfo>>  
         ) {  
           hideProgress()  
           if (response.isSuccessful) {  
             response.body()?.let {  
                // do what you need when the call is success  
             }  
           } else {  
             response.errorBody()?.let {  
               val errorResponse = JsonParser().toBaseResponseError<HealthPersonInfo>(it.source())  
               // now you have an error object(errorResponse ) and you can use it's properties as follows  
               val code = errorResponse?.errorCode  
             }  
           }  
         }  
       })  

Note : The "Service" class is responsible for creating retrofit builder. It has methods like "getUser" which constructs get user url and callback. "HealthPersonInfo" is a class which is used when we have a successful return.


Hope you can get something helpful. Happy coding!!!



Wednesday, October 17, 2018

How to get frame rate (FPS) and number of frames programmatically from a video in Android

17th of October, 2018.

Yesterday I was assigned to get metadata (width, height, frame rate and no of frames)  of a video recorded by camera. I have gone through many articles and stackoverflow answers, but none of them have fully covered what I want. After playing with android framework I manged to complete my assignment. I will show you what I  have learned.

What I need was either number of frames or frame rate. If I have one then I can calculate other because I have the video length.
 First I tried to use MediaPlayer class. There were 2 promising methods in this class.

 val mediaPlayer: MediaPlayer = MediaPlayer.create(context, uri)  
 val frames = mediaPlayer.metrics.get(MediaPlayer.MetricsConstants.FRAMES)  

However this method requires api level 28. My minimum api level was 21 so this was not going to work. There was also another method call,

 val frameRate = mediaPlayer.syncParams.frameRate  

However this always returns null for me. I wasn't eager to chase this around because I had another promising class which named MediaMetadataRetriever.

 val retriever = MediaMetadataRetriever()  
 val frameRate = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE)  

However this method also requires android api level 23 and also it returns me null.

Finally I found MediaExtractor class which is compatible with android api level 16 and gives me what I want.

 private fun getVideoMetaData(uri: Uri) : VideoMetaData{  
     val mediaExtractor = MediaExtractor()  
     mediaExtractor.setDataSource(uri.path)  
     val format = mediaExtractor.getTrackFormat(0)  
     mediaExtractor.release()  
     return if(format.containsKey(MediaFormat.KEY_FRAME_RATE)){  
       val frameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE)  
       val frames = frameRate * (format.getLong(MediaFormat.KEY_DURATION)/ 1000000)  
       VideoMetaData(format.getInteger(MediaFormat.KEY_WIDTH).toString(),  
           format.getInteger(MediaFormat.KEY_HEIGHT).toString(),  
           frames.toString(),  
           frameRate.toString())  
     }else {  
       VideoMetaData(format.getInteger(MediaFormat.KEY_WIDTH).toString(), format.getInteger(MediaFormat.KEY_HEIGHT).toString())  
     }  
   }  

** VideoMetaData is a simple data class

So that's it. Hope this will be a help for someone.
:-)