I can't find the files I uploaded to Google Drive - android

I'm trying to upload a file to Google Drive using Google Drive REST API v3. After the upload process is completed, it returns a status code of 200 (successful). But I can't find the files in my Google Drive. Please tell me what am I doing wrong? I will really appreciate if you provide a proper illustration or better still code snippet while helping me with this problem of mine. I am really anticipating your answers.
I have tried following the documentation but I am still getting the same error. I have searched everywhere online and stackoverflow, but none seems to provide the solution to my problem.
here is the code
private val AUTHORIZATION_PARAM = "Authorization"
private val BEARER_VAL = "Bearer "
private val CONTENT_TYPE_PARAM = "Content-Type: "
private val LINE_FEED = "\r\n"
private val APP_FOLDER_ID = "appDataFolder"
fun connectAndStartOperation() {
if (mAuthCode == null) {
signInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.requestProfile()
.requestScopes(Scope(Scopes.DRIVE_APPFOLDER))
.requestIdToken(resources.getString(R.string.gdrive_clientId))
.requestServerAuthCode(resources.getString(R.string.gdrive_clientId))
.build()
mGoogleSignInClient = GoogleSignIn.getClient(this, signInOptions!!)
startActivityForResult(mGoogleSignInClient?.signInIntent, CLOUD_STORAGE)
Log.i("mAuthCode", "false")
} else {
Log.i("mAuthCode", "true")
writeDbToDrive()
mNextGoogleApiOperation = INVALID;
}
}
fun disconnect() {
mGoogleSignInClient?.signOut()
mActivity = null
mNextGoogleApiOperation = INVALID
mAuthCode = null
mAccessToken = null
mTokenExpired = 0
}
override fun onDestroy() {
disconnect()
super.onDestroy()
}
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == CLOUD_STORAGE) {
val task = GoogleSignIn.getSignedInAccountFromIntent(data)
.addOnSuccessListener(this)
.addOnFailureListener(this)
}
}
override fun onSuccess(googleSignInAccount: GoogleSignInAccount?) {
Log.i("mAuthCode", "Success")
val scope = "oauth2:https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/userinfo.profile"
idTokenString = googleSignInAccount?.idToken
mAuthCode = googleSignInAccount?.serverAuthCode
mGoogleSignInAccount = googleSignInAccount
doAsync {
try {
mAccessToken = GoogleAuthUtil.getToken(this#SettingsActivity, mGoogleSignInAccount?.account, scope)
} catch (e: Exception) {
Log.i("Error AccessToken", "${e.message}")
e.printStackTrace()
}
uiThread {
Log.i("AccessTokenMy", "$mAccessToken")
}
}
}
override fun onFailure(p0: java.lang.Exception) {
Log.i("mAuthCode", "Failed")
p0.printStackTrace()
}
private fun writeDbToDrive() {
var conn: HttpURLConnection? = null
var os: OutputStream? = null
val accessToken = requestAccessToken(mGoogleSignInAccount!!)
if (accessToken == null)
return
try {
val boundary = "pb" + System.currentTimeMillis()
val url = URL("https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart")
conn = url.openConnection() as HttpURLConnection
conn.requestMethod = "POST"
conn.useCaches = false
conn.doOutput = true
conn.doInput = true
conn.connectTimeout = 5000
conn.setRequestProperty(AUTHORIZATION_PARAM, BEARER_VAL + accessToken!!)
conn.setRequestProperty("Content-Type", "multipart/related; boundary=$boundary")
Log.i("Action", "Parameter set for server")
/////// Prepare data
//val timestamp = SimpleDateFormat("yyyy-MM-dd_HH:mm:ss", Locale.getDefault()).format(Date())
// Prepare file metadata (Change your backup file name here)
val b = StringBuilder()
b.append('{')
.append("\"name\":\"").append(exportedFileName).append('\"')
.append(',')
.append("\"mimeType\":").append("\"text\\/csv\"")
.append(',') //"\"application\\/vnd.google-apps.unknown\""
.append("\"parents\":").append("[\"").append(APP_FOLDER_ID).append("\"]")
.append('}')
val metadata = b.toString()
val data = readFile(File(filePath))
/////// Calculate body length
var bodyLength = 0
// MetaData part
b.setLength(0)
b.append("--").append(boundary).append(LINE_FEED)
b.append(CONTENT_TYPE_PARAM).append("application/json; charset=UTF-8")
.append(LINE_FEED)
b.append(LINE_FEED)
b.append(metadata).append(LINE_FEED)
b.append(LINE_FEED)
b.append("--").append(boundary).append(LINE_FEED)
b.append(CONTENT_TYPE_PARAM).append("text/csv").append(LINE_FEED)
b.append(LINE_FEED)
val beforeFilePart = b.toString().toByteArray(charset("UTF_8"))
bodyLength += beforeFilePart.size
bodyLength += data.size // File
b.setLength(0)
b.append(LINE_FEED)
b.append("--").append(boundary).append("--")
val afterFilePart = b.toString().toByteArray(charset("UTF_8"))
bodyLength += afterFilePart.size
conn.setRequestProperty("Content-Length", bodyLength.toString())
//if (BuildConfig.DEBUG_MODE) DebugHelper.log("LENGTH", bodyLength)
/////// Write to socket
os = conn.outputStream
try {
os!!.write(beforeFilePart)
os!!.write(data)
os!!.write(afterFilePart)
os!!.flush()
} catch (e: Exception) {
e.printStackTrace()
}
val msg = conn.responseMessage
val code = conn.responseCode
if (code == 200) {
Log.i("writeDbToDrive", "Exported Successfully: $code $msg")
} else {
Log.i("writeDbToDrive", "Error: $code $msg")
}
} catch (e: Exception) {
e.printStackTrace()
Log.i("writeDbToDrive", e.message!!)
} finally {
if (os != null) {
try {
os!!.close()
} catch (e: IOException) {
}
}
conn?.disconnect()
}
}
#Throws(IOException::class)
private fun readFile(file: File): ByteArray {
val f = RandomAccessFile(file, "r")
try {
val longlength = f.length()
val length = longlength.toInt()
if (length.toLong() != longlength)
throw IOException("File size >= 10 Mb")
val data = ByteArray(length)
f.readFully(data)
return data
} finally {
f.close()
}
}
private fun requestAccessToken(mGoogleSignInAccount: GoogleSignInAccount): String? {
val scope = "oauth2:https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/userinfo.profile"
doAsync {
try {
mAccessToken = GoogleAuthUtil.getToken(this#SettingsActivity, mGoogleSignInAccount?.account, scope)
} catch (e: Exception) {
Log.i("Error AccessToken", "${e.message}")
e.printStackTrace()
}
uiThread {
Log.i("AccessTokenMy", "$mAccessToken")
}
}
return mAccessToken
}

After reading through this Files: create Documentation, I have finally fixed the problem. Unknown to me is that the files where being saved in the AppData folder created by my app. The AppData folder is hidden which can only be accessible by and through my app. For me to be able to save the file to My Drive folder, I removed the part of the metadata
` .append(',')
.append("\"parents\":").append("[\"").append(APP_FOLDER_ID).append("\"]")`
So the metadata part is now like this
val b = StringBuilder()
b.append('{')
.append("\"name\":\"").append(exportedFileName).append('\"')
.append(',')
.append("\"mimeType\":").append("\"text\\/csv\"")
.append('}')
val metadata = b.toString()
Every other thing remains the same

Related

How to call tokenURI() function in ERC721 using Web3j library?

I need to view NFT-image with all metadata. I decide to call tokenURI() function like it, but it's ain't working
private fun getNFTMetadata() = viewModelScope.launch(Dispatchers.IO){
//tokenURI -- by token ID
val web3j: Web3j = createWeb3j() ?: return#launch
var ids = listOf<Uint256>(Uint256.DEFAULT)
val function: org.web3j.abi.datatypes.Function = org.web3j.abi.datatypes.Function(
"tokenURI",
ids,
listOf()
)
val encodedFunction = FunctionEncoder.encode(function)
val response: EthCall = web3j.ethCall(
Transaction.createEthCallTransaction(WALLET_ADDRESS, CONTRACT_ADDRESS, encodedFunction),
LATEST
).sendAsync().get()
if (response.value != null){
state.value = response.value
} else {
state.value = "NAN"
}
}
private fun createWeb3j(): Web3j? {
val webSocketService = WebSocketService(WEB_SOCKET_URL, true)
try {
webSocketService.connect()
} catch (e: ConnectException) {
e.printStackTrace()
}
return Web3j.build(webSocketService)
}
I really don't know how to call that function rightly. Help me please!)
I found my mistake. I had change received parameters.
private fun getNFTMetadata() = viewModelScope.launch(Dispatchers.IO){
//tokenURI -- by token ID
val web3j: Web3j = createWeb3j() ?: return#launch
val big: Uint256 = Uint256(1)
val function: org.web3j.abi.datatypes.Function = org.web3j.abi.datatypes.Function(
"tokenURI",
listOf(big),
listOf(object : TypeReference<Utf8String>() {})
)
val encodedFunction = FunctionEncoder.encode(function)
val response: EthCall = web3j.ethCall(
Transaction.createEthCallTransaction(WALLET_ADDRESS, CONTRACT_ADDRESS, encodedFunction),
LATEST
).sendAsync().get()
if (response.value != null){
state.value = response.value
} else {
state.value = "NAN"
}
}

Uploading multiple files with Retrofit 2 to Spring Webflux backend

I get the following error when I try to upload multiple files with retrofit 2:
org.springframework.core.codec.DecodingException: Could not find first boundary
When I upload multiple files with postman on the same API endpoint, it works perfectly.
Server controller endpoint:
#PostMapping("{loveSpotId}/photos")
suspend fun uploadToSpot(
#PathVariable loveSpotId: Long,
#RequestPart("photos") filePartFlux: Flux<FilePart>
) {
loveSpotPhotoService.uploadToSpot(loveSpotId, filePartFlux.asFlow())
}
Retrofit API definition:
interface LoveSpotPhotoApi {
#Multipart
#POST("/lovespots/{loveSpotId}/photos")
fun uploadToLoveSpot(
#Path("loveSpotId") loveSpotId: Long,
#Part photos: List<MultipartBody.Part>
): Call<ResponseBody>
// ...
}
Reading photos on Android device:
if (activityResult.resultCode == Activity.RESULT_OK) {
val itemCount: Int = activityResult.data?.clipData?.itemCount ?: 0
val files = ArrayList<File>()
for (i in 0 until itemCount) {
val clipData = activityResult.data!!.clipData!!
val uri = clipData.getItemAt(i).uri
files.add(File(uri.path!!))
}
loveSpotPhotoService.uploadToLoveSpot(loveSpotId, files, this#LoveSpotDetailsActivity)
}
Client code using Retrofit:
suspend fun uploadToLoveSpot(loveSpotId: Long, photos: List<File>, activity: Activity) {
val loadingBarShower = LoadingBarShower(activity).show()
withContext(Dispatchers.IO) {
val parts: List<MultipartBody.Part> = photos.map { prepareFilePart(it) }
val call = loveSpotPhotoApi.uploadToLoveSpot(loveSpotId, parts)
try {
val response = call.execute()
loadingBarShower.onResponse()
if (response.isSuccessful) {
toaster.showToast(R.string.photo_uploaded_succesfully)
} else {
toaster.showResponseError(response)
}
} catch (e: Exception) {
loadingBarShower.onResponse()
toaster.showToast(R.string.photo_upload_failed)
}
}
}
private fun prepareFilePart(file: File): MultipartBody.Part {
// create RequestBody instance from file
val requestFile = RequestBody.create(
MediaType.get("image/*"),
file
)
// MultipartBody.Part is used to send also the actual file name
return MultipartBody.Part.createFormData("photos", file.name, requestFile)
}
Example headers logged on server when I upload with postman and it works:
[Authorization:"Bearer ...", User-Agent:"PostmanRuntime/7.30.0", Accept:"/", Postman-Token:"7ad875eb-2fe5-40ea-99f0-3ad34c3fa875", Host:"localhost:8090", Accept-Encoding:"gzip, deflate, br", Connection:"keep-alive", Content-Type:"multipart/form-data; boundary=--------------------------877409032202838734061440", content-length:"1555045"]
Example headers logged on server when I upload with retrofit client and it fails:
[Authorization:"Bearer ...", Content-Type:"multipart/form-data; boundary=c6177139-6b31-4d91-b66d-54772a51d963", Host:"192.168.0.143:8090", Connection:"Keep-Alive", Accept-Encoding:"gzip", User-Agent:"okhttp/3.14.9", content-length:"528"]
The problem was that I was not reading photos from the Android device properly. Here is my code that fixed that:
launcher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult ->
handlePhotoPickerResult(activityResult)
}
fun startPickerIntent(launcher: ActivityResultLauncher<Intent>) {
val intent = Intent()
intent.type = "image/*"
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
intent.action = Intent.ACTION_PICK
launcher.launch(intent)
}
private fun handlePhotoPickerResult(activityResult: ActivityResult) {
MainScope().launch {
if (activityResult.resultCode == RESULT_OK) {
val files = PhotoUploadUtils.readResultToFiles(activityResult, contentResolver)
Log.i(this#LoveSpotDetailsActivity::class.simpleName, "Starting upload")
val result: Boolean = if (photoUploadReviewId != null) {
loveSpotPhotoService.uploadToReview(
loveSpotId,
photoUploadReviewId!!,
files,
this#LoveSpotDetailsActivity
)
} else {
loveSpotPhotoService.uploadToLoveSpot(
loveSpotId,
files,
this#LoveSpotDetailsActivity
)
}
photosLoaded = !result
photosRefreshed = !result
photoUploadReviewId = null
if (result) {
Log.i(
this#LoveSpotDetailsActivity::class.simpleName,
"Upload finished, starting refreshing views."
)
startPhotoRefreshSequence()
}
} else {
toaster.showToast(R.string.failed_to_access_photos)
}
}
}
fun readResultToFiles(
activityResult: ActivityResult,
contentResolver: ContentResolver
): List<File> {
val itemCount: Int = activityResult.data?.clipData?.itemCount ?: 0
val files = ArrayList<File>()
for (i in 0 until itemCount) {
val clipData = activityResult.data!!.clipData!!
val uri = clipData.getItemAt(i).uri
Log.i("uri", "$uri")
addToFilesFromUri(uri, files, contentResolver)
}
return files
}
private fun addToFilesFromUri(
uri: Uri,
files: ArrayList<File>,
contentResolver: ContentResolver
) {
val projection = arrayOf(MediaStore.MediaColumns.DATA)
contentResolver.query(uri, projection, null, null, null)
?.use { cursor ->
if (cursor.moveToFirst()) {
val columnIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DATA)
Log.i("columnIndex", "$columnIndex")
val filePath = cursor.getString(columnIndex)
Log.i("filePath", " $filePath")
if (filePath != null) {
val file = File(filePath)
Log.i("file", "$file")
files.add(file)
}
}
}
}
Rest of the code was fine.

How to use okhttp to create a stream of data and write into device in android

What I am doing: I am using a connection to download file from server and write into a storage
What is happening: Code is working
What I am trying to do: How to achieve the same using okhttp
try {
val url = URL(DICTIONARY_FILE_URL)
val conection = url.openConnection()
conection.connect()
// getting file length
// input stream to read file - with 8k buffer
val input = BufferedInputStream(url.openStream(), 8192)
// Output stream to write file
val directoryPathName = Environment.getExternalStorageDirectory().absolutePath.plus("/CNX/dictionary/")
val dictionaryFileName = "DictionaryEN.quickdic"
var f = File(directoryPathName)
if(!f.isDirectory) {
f.mkdirs()
}
val output = FileOutputStream(directoryPathName.plus(dictionaryFileName))
val data = ByteArray(1024)
var count: Int? = 0
while ({ count = input.read(data);count }() != -1) {
output.write(data, 0, count!!)
}
// flushing output
output.flush()
// closing streams
output.close()
input.close()
isJobSuccess = true
//sharedPreferences[IS_DICTIONARY_DOWNLOADED] = true
} catch (e: Exception) {
Log.e("Exception",e.message)
isJobSuccess = false
//sharedPreferences[IS_DICTIONARY_DOWNLOADED] = false
}
Sample for Downloading a file with OkHttp asynchronously
fun downloadFileAsync(downloadUrl: String) {
val client = OkHttpClient();
val request = Request.Builder().url(downloadUrl).build();
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.e("fail", e.printStackTrace().toString())
}
override fun onResponse(call: Call, response: Response) {
if (!response.isSuccessful) {
Log.e("fail", "Download error")
}else{
// Output stream to write file
val directoryPathName = Environment.getExternalStorageDirectory().absolutePath.plus("/CNX/dictionary/")
val dictionaryFileName = "DictionaryEN.quickdic"
val f = File(directoryPathName)
if(!f.isDirectory) {
f.mkdirs()
}
val output = FileOutputStream(directoryPathName.plus(dictionaryFileName))
response.body?.let {
output.write(it.bytes())
}
// flushing output
output.flush()
// closing streams
output.close()
}
}
})
}
Sample for Downloading a file with OkHttp synchronously
fun downloadFileSync(downloadUrl: String) {
val client = OkHttpClient();
val request = Request.Builder().url(downloadUrl).build();
val response = client.newCall (request).execute();
if (!response.isSuccessful) {
Log.e("fail", "Failed to download file: " + response)
}else {
val directoryPathName = Environment.getExternalStorageDirectory().absolutePath.plus("/CNX/dictionary/")
val dictionaryFileName = "DictionaryEN.quickdic"
val f = File(directoryPathName)
if (!f.isDirectory) {
f.mkdirs()
}
val output = FileOutputStream(directoryPathName.plus(dictionaryFileName))
response.body?.let {
output.write(it.bytes())
}
// flushing output
output.flush()
// closing streams
output.close()
}
}
build.gradle
implementation("com.squareup.okhttp3:okhttp:4.5.0")

Handle no internet connection error of retrofit 2.6 with kotlin coroutines

I'm using retrofit 2.6 with kotlin coroutines to make API call without block the UI thread, I got it work but the app crashes when I switch off the internet connection. The logcat error is: E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-1
Here is my code:
private fun handleIntent(slug: String) {
val service = UtilityMethods.migrationTimeService()
UtilityMethods.showView(loading_view)
UtilityMethods.hideView(network_error_msg)
CoroutineScope(Dispatchers.IO).launch {
val res = service.getPostBySlug(slug)
try {
withContext(Dispatchers.Main) {
//Do something with response e.g show to the UI.
val post = res.body()!!.first()
UtilityMethods.hideView(loading_view)
val title = post.title?.rendered
val content = post.content?.rendered
val imageUrl = post.jetPackFeaturedMediaUrl
title_txtView.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
Html.fromHtml(title, Html.FROM_HTML_MODE_COMPACT).toString()
else
Html.fromHtml(title).toString()
content_txtView.loadData(content.toString(), "text/html", "UTF-8")
Picasso.get().load(imageUrl).fit().centerCrop().into(thumbnail_imgview)
}
} catch (e: HttpException) {
UtilityMethods.showView(network_error_msg)
} catch (e: Throwable) {
Toast.makeText(this#PostContentActivity, "Ooops: Something else went wrong", Toast.LENGTH_LONG)
}
}
}
I've got the code working, the new code is:
private fun handleIntent(slug: String) = GlobalScope.launch(Dispatchers.Main) {
val service = UtilityMethods.migrationTimeService()
UtilityMethods.showView(loading_view)
UtilityMethods.hideView(network_error_msg)
try {
val res = withContext(Dispatchers.IO) {
service.getPostBySlug(slug)
}
//Do something with response e.g show to the UI.
val post = res.body()!!.first()
UtilityMethods.hideView(loading_view)
val title = post.title?.rendered
val content = post.content?.rendered
val imageUrl = post.jetPackFeaturedMediaUrl
title_txtView.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
Html.fromHtml(title, Html.FROM_HTML_MODE_COMPACT).toString()
else
Html.fromHtml(title).toString()
content_txtView.loadData(content.toString(), "text/html", "UTF-8")
Picasso.get().load(imageUrl).fit().centerCrop().into(thumbnail_imgview)
}
catch (e: HttpException) {
Toast.makeText(this#PostContentActivity, "Exception ${e.message}", Toast.LENGTH_LONG).show()
}catch (e: IOException) {
UtilityMethods.hideView(loading_view)
UtilityMethods.showView(network_error_msg)
} catch (e: Throwable) {
Toast.makeText(this#PostContentActivity, "Ooops: Something else went wrong ${e.message}", Toast.LENGTH_LONG).show()
}
}
So while looking into stacktrace I found that ConnectException is thrown when network is unavailable
And that's how I do it in kotlin and it works for me,
suspend fun<T: Any> safeAPICall(call: suspend () -> Response<T>) : T{
val response = try {
call.invoke()
}
catch (e:java.lang.Exception){
e.printStackTrace()
val message = if( e is ConnectException) "Connection Error" else "Something went wrong. Please try again."
throw IOException(ResponseError(message, 500).convertToJsonString())
}
// When connection is OK
if(response.isSuccessful){
return response.body()!!
}else{
val error = response.errorBody()?.string()
error?.let{
val message = JSONObject(it).optString("message", "Something went wrong")
val responseError = ResponseError(message, response.code())
throw IOException(responseError.convertToJsonString())
}
throw IOException(ResponseError("Something went wrong. Please try again.", 500).convertToJsonString())
}
}
The data class that I use
data class ResponseError(val message:String, val errorCode:Int)
Usage:
try {
val response = safeAPICall {APIClient.planner.viewSites(view.context.authToken)}
}
catch (e:Exception){
view.snack(e.message?.toModel<ResponseError>()?.message?: unspecified_error)
}
Bonus:
inline fun <reified T> JSONObject.toModel(): T? = this.run {
try {
Gson().fromJson<T>(this.toString(), T::class.java)
}
catch (e:java.lang.Exception){ e.printStackTrace(); null }
}
inline fun <reified T> String.toModel(): T? = this.run {
try {
JSONObject(this).toModel<T>()
}
catch (e:java.lang.Exception){ null }
}
Instead of this:
CoroutineScope(Dispatchers.IO).launch {
val res = service.getPostBySlug(slug)
try {
withContext(Dispatchers.Main) {
Try this one:
CoroutineScope(Dispatchers.Main).launch {
val res = service.getPostBySlug(slug)
withContext(Dispatchers.IO) {
try {
wrap your 'try and catch' block code within Dispatchers.IO instead of wraping your Dispatchers.IO with in yout try block

How to Integrate Alexa Voice Service to Custom Android App

I am planning to develop an Android App with Alexa Voice Service integration to develop an app like Reverb. Below is what I have tried...
Checked AVS Device SDK, could get a proper guide to implement it in Android.
Checked https://github.com/willblaschko/AlexaAndroid , wasnt able to get it work.
Planned to implement myself, below is what I have done.
a. Integrated Login framework, was able to successfully login and get the token.
b. Created a sound recorder, was able to record and playback locally.
c. Created request to sent audio to https://avs-alexa-eu.amazon.com/v20160207/events
[UPDATED]
After changing the shortArray to Byte array I am getting the response but now the problem is that MediaPlayer is unable to play the response mp3, it gives error at prepare
package com.example.anoopmohanan.alexaandroid
import android.content.Context
import android.media.*
import android.media.AudioFormat.ENCODING_PCM_16BIT
import android.media.AudioFormat.CHANNEL_CONFIGURATION_MONO
import android.os.Environment
import android.os.Environment.getExternalStorageDirectory
import java.io.*
import com.example.anoopmohanan.alexaandroid.ResponseParser.getBoundary
import okhttp3.*
import org.jetbrains.anko.doAsync
import org.json.JSONObject
import java.nio.file.Files.exists
import okhttp3.OkHttpClient
import java.net.HttpURLConnection
import android.os.Looper
import android.os.PowerManager
import android.util.Log
import okio.BufferedSink
import okhttp3.RequestBody
import org.apache.commons.io.FileUtils
import org.jetbrains.anko.Android
import org.jetbrains.anko.runOnUiThread
import org.jetbrains.anko.toast
import java.util.*
import okhttp3.ResponseBody
import okio.Buffer
import com.example.anoopmohanan.alexaandroid.SoundRecorder.LoggingInterceptor
import android.media.MediaDataSource
class SoundRecorder(context: Context) {
private var appcontext: Context? = null
private var recording = false
val MEDIA_JSON = MediaType.parse("application/json; charset=utf-8")
val MEDIA_TYPE_AUDIO = MediaType.parse("application/octet-stream")
var accessToken = ""
private var mediaPlayer: MediaPlayer? = null
private var streamToSend:ByteArray? = null
private val client = OkHttpClient.Builder()
.addInterceptor(LoggingInterceptor())
.build()
init {
this.appcontext = context
}
fun startRecording(accessToken: String){
this.accessToken = accessToken
doAsync {
startRecord()
}
}
fun stopRecording(){
doAsync {
stopRecord()
}
}
fun playRecording(){
doAsync {
playRecord()
}
}
private fun stopRecord(){
recording = false
//val file = File(Environment.getExternalStorageDirectory(), "test.pcm")
//sendAuio(file)
}
fun playRecord() {
val file = File(Environment.getExternalStorageDirectory(), "speech2.mp3")
val mplayer = MediaPlayer()
mplayer.setDataSource(file.path)
mplayer.prepare()
mplayer.start()
// val file = File(Environment.getExternalStorageDirectory(), "test.pcm")
//
// val shortSizeInBytes = java.lang.Short.SIZE / java.lang.Byte.SIZE
//
// val bufferSizeInBytes = (file.length() / shortSizeInBytes).toInt()
// val audioData = ByteArray(bufferSizeInBytes)
//
// try {
// val inputStream = FileInputStream(file)
// val bufferedInputStream = BufferedInputStream(inputStream)
// val dataInputStream = DataInputStream(bufferedInputStream)
//
// var i = 0
// while (dataInputStream.available() > 0) {
// audioData[i] = dataInputStream.readByte()
// i++
// }
//
// dataInputStream.close()
//
// val audioTrack = AudioTrack.Builder()
// .setAudioAttributes(AudioAttributes.Builder()
// .setUsage(AudioAttributes.USAGE_MEDIA)
// .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
// .build())
// .setAudioFormat(AudioFormat.Builder()
// .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
// .setSampleRate(16000)
// .setChannelMask(AudioFormat.CHANNEL_OUT_MONO).build())
// .setBufferSizeInBytes(bufferSizeInBytes)
// .setTransferMode(AudioTrack.MODE_STREAM)
// .build()
//
// audioTrack.play()
// audioTrack.write(audioData, 0, bufferSizeInBytes)
//
//
// } catch (e: FileNotFoundException) {
// e.printStackTrace()
// } catch (e: IOException) {
// e.printStackTrace()
// }
}
private fun startRecord() {
val file = File(Environment.getExternalStorageDirectory(), "test.pcm")
try {
file.createNewFile()
val outputStream = FileOutputStream(file)
val bufferedOutputStream = BufferedOutputStream(outputStream)
val dataOutputStream = DataOutputStream(bufferedOutputStream)
val minBufferSize = AudioRecord.getMinBufferSize(16000,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT)
val audioData = ByteArray(minBufferSize)
val audioRecord = AudioRecord(MediaRecorder.AudioSource.MIC,
16000,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
800)
if (audioRecord.recordingState != AudioRecord.RECORDSTATE_STOPPED){
this.appcontext?.runOnUiThread {
toast("No recording source available")
}
return
}
recording = true
audioRecord.startRecording()
if (audioRecord.recordingState != AudioRecord.RECORDSTATE_RECORDING){
this.appcontext?.runOnUiThread {
toast("Someone is still recording")
}
recording = false
audioRecord.stop()
audioRecord.release()
return
}
this.appcontext?.runOnUiThread {
toast("Recording started hurray")
}
while (recording) {
audioRecord.read(audioData, 0, minBufferSize)
dataOutputStream.write(audioData,0,minBufferSize)
// for (i in 0 until numberOfShort) {
// dataOutputStream.writeShort(audioData[i].toInt())
// }
}
audioRecord.stop()
audioRecord.release()
dataOutputStream.close()
sendAuio(file)
} catch (e: IOException) {
e.printStackTrace()
}
}
fun sendAuio(audio: File){
streamToSend = audio.readBytes()
val requestBody = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("metadata","metadata",RequestBody.create(MEDIA_JSON, generateSpeechMetadata()))
.addFormDataPart("audio", "test.pcm",
RequestBody.create(MEDIA_TYPE_AUDIO, streamToSend))
.build()
val request = Request.Builder()
.url("https://avs-alexa-eu.amazon.com/v20160207/events")
.addHeader("Authorization","Bearer $accessToken")
.post(requestBody)
.build()
print (request.body().toString())
client.newCall(request).execute().use({ response ->
if (!response.isSuccessful()) throw IOException("Unexpected code $response")
val items = if (response.code() == HttpURLConnection.HTTP_NO_CONTENT)
AvsResponse()
else
ResponseParser.parseResponse(response.body()!!.byteStream(), getBoundary(response))
if (items.size > 0){
handle(items)
}
System.out.println("[TRACE]"+response.body()!!.string())
})
}
#Throws(IOException::class)
fun toByteArray(`in`: InputStream): ByteArray {
val out = ByteArrayOutputStream()
var read = 0
val buffer = ByteArray(1024)
while (read != -1) {
read = `in`.read(buffer)
if (read != -1)
out.write(buffer, 0, read)
}
out.close()
return out.toByteArray()
}
private fun generateSpeechMetadata(): String {
val messageId = UUID.randomUUID().toString();
val dialogId = UUID.randomUUID().toString();
return "{\"event\": {\"header\": {\"namespace\": \"SpeechRecognizer\",\"name\": \"Recognize\",\"messageId\": \"$messageId\",\"dialogRequestId\": \"$dialogId\"},\"payload\": {\"profile\": \"CLOSE_TALK\", \"format\": \"AUDIO_L16_RATE_16000_CHANNELS_1\"}},\"context\": [{\"header\": {\"namespace\": \"AudioPlayer\",\"name\": \"PlaybackState\"},\"payload\": {\"token\": \"\",\"offsetInMilliseconds\": 0,\"playerActivity\": \"FINISHED\"}}, {\"header\": {\"namespace\": \"SpeechSynthesizer\",\"name\": \"SpeechState\"},\"payload\": {\"token\": \"\",\"offsetInMilliseconds\": 0,\"playerActivity\": \"FINISHED\"}}, { \"header\" : { \"namespace\" : \"Alerts\", \"name\" : \"AlertsState\" }, \"payload\" : { \"allAlerts\" : [ ], \"activeAlerts\" : [ ] } }, {\"header\": {\"namespace\": \"Speaker\",\"name\": \"VolumeState\"},\"payload\": {\"volume\": 25,\"muted\": false}}]}"
// return "{\n" +
// "\"messageHeader\": {\n" +
// "\"deviceContext\": [\n" +
// "{\n" +
// "\"name\": \"playbackState\",\n" +
// "\"namespace\": \"AudioPlayer\",\n" +
// "\"payload\": {\n" +
// "\"streamId\": \"\",\n" +
// "\"offsetInMilliseconds\": \"\",\n" +
// "\"playerActivity\": \"IDLE\"\n" +
// "}\n" +
// "}\n" +
// "]\n" +
// "},\n" +
// "\"messageBody\": {\n" +
// "\"profile\": \"doppler-scone\",\n" +
// "\"locale\": \"en-us\",\n" +
// "\"format\": \"audio/L16; rate=16000; channels=1\"\n" +
// "}\n" +
// "}"
}
private val audioRequestBody = object : RequestBody() {
override fun contentType(): MediaType? {
return MediaType.parse("application/octet-stream")
}
#Throws(IOException::class)
override fun writeTo(sink: BufferedSink?) {
//while our recorder is not null and it is still recording, keep writing to POST data
sink!!.write(streamToSend)
}
}
inner class LoggingInterceptor : Interceptor {
#Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val t1 = System.nanoTime()
Log.d("OkHttp", String.format("--> Sending request %s on %s%n%s", request.url(), chain.connection(), request.headers()))
val requestBuffer = Buffer()
request.body()?.writeTo(requestBuffer)
Log.d("OkHttp", requestBuffer.readUtf8())
val response = chain.proceed(request)
val t2 = System.nanoTime()
Log.d("OkHttp", String.format("<-- Received response for %s in %.1fms%n%s", response.request().url(), (t2 - t1) / 1e6, response.headers()))
val contentType = response.body()?.contentType()
val content = response.body()?.string()
Log.d("OkHttp", content)
val wrappedBody = ResponseBody.create(contentType, content)
return response.newBuilder().body(wrappedBody).build()
}
}
private fun getMediaPlayer(): MediaPlayer? {
if (mediaPlayer == null) {
mediaPlayer = MediaPlayer()
}
return mediaPlayer
}
fun handle(items:AvsResponse){
for (item in items){
handle(item)
}
}
fun handle(item: AvsItem){
if (item is AvsSpeakItem){
}else{
return
}
//cast our item for easy access
//write out our raw audio data to a file
val path = File(Environment.getExternalStorageDirectory(), "speech.mp3")
//path.deleteOnExit()
//val path = File(appcontext!!.getCacheDir(), System.currentTimeMillis().toString() + ".mp3")
//var fos: FileOutputStream? = null
try {
// fos = FileOutputStream(path)
// fos!!.write(item.audio)
// fos.close()
path.createNewFile()
path.writeBytes(item.audio)
// var ds = ByteArrayMediaDataSource(item.audio)
// val fis = FileInputStream(path)
// //play our newly-written file
// val mplayer = MediaPlayer()
// mplayer.setDataSource(path.path)
// mplayer.prepare()
// mplayer.start()
// getMediaPlayer()?.setDataSource(fis.fd)
// getMediaPlayer()?.prepare()
// getMediaPlayer()?.start()
} catch (e: IOException) {
e.printStackTrace()
} catch (e: IllegalStateException) {
e.printStackTrace()
}
}
inner class ByteArrayMediaDataSource(private val data: ByteArray?) : MediaDataSource() {
init {
assert(data != null)
}
#Throws(IOException::class)
override fun readAt(position: Long, buffer: ByteArray, offset: Int, size: Int): Int {
System.arraycopy(data, position.toInt(), buffer, offset, size)
return size
}
#Throws(IOException::class)
override fun getSize(): Long {
return data?.size?.toLong()!!
}
#Throws(IOException::class)
override fun close() {
// Nothing to do here
}
}
}
RESPONSE FROM ALEXA
--------abcde123
Content-Type: application/json; charset=UTF-8
{"directive":{"header":{"namespace":"SpeechSynthesizer","name":"Speak","messageId":"d58c83fe-377f-4d1d-851b-a68cf5686280","dialogRequestId":"d83b8496-e6a5-4fc5-b07b-32f70acd1f15"},"payload":{"url":"cid:2aed5305-081d-4624-b2ba-ef51eba6aa32_1353236331","format":"AUDIO_MPEG","token":"amzn1.as-ct.v1.Domain:Application:Knowledge#ACRI#2aed5305-081d-4624-b2ba-ef51eba6aa32"}}}
--------abcde123
Content-ID: <2aed5305-081d-4624-b2ba-ef51eba6aa32_1353236331>
Content-Type: application/octet-stream
ID3#TSSELavf57.71.100dUC\[QwwD뻝q \[D*An. .`>X>>#|L?&|>4=A?w'<<Ɨd"_h{[M̢8j8ǙbA
#=$T2bX2LM~NBPp˧Eh$`hoV
ԁh3}ɧ2/_74g挺`;*
W`dq՗HY)#mN<fG?l74(
˚z
BD)jy7uӹ\ۭc|>)qcsZQN} QppҜ{_]>iWw$Yd!.VEi]
<ƹYK.l6$̄)uLPGⰞRTFK>cr{\UK.|-EWGWȍ{3UN6]1tX#jr`5ka5/d V
WzLٺt?Aɬg%Я;la N%
A`r:ۙJSD3\´P y%eWOG5As
۪<*qfom
d !JjadR"=2vg?ZzrKZrV*Y2ش!XFOI EP99
Fdmy;ꡐ^JJ5С/\ _-LuafVMFZT-TUadRwO(A.2>sʳ!Y&)h`x<RR<AC|pTi"`k9#ɱX*%AP0CR7u+<VmFq ,ł)EhH굫.<hd#%[6h$kRO'IZ.VMX>!fqi+`:'j-Z6X *C
0S'9)y􋎚d&ĽX+)d Ӳ#Xz3 M xNgV9Vc׿?:ot\w}&dZk)b.`C$w1*\y?O՗ql\6d\&R=bcQt]
r*U{ztUT-| b%BN'<^潦P2Dtc1dھ]KN!*gxxv[dp0 ЈaR'id \#G_=f:fř6~pdcg.k/_E0lY
(XvoR.w*0U>/9_`ra1ANo^8&Ո񻊊{2d5Y{қ|Xo6`J| !mcx".~_Da_,êJgt7,xkdO,Ӭ4e 葅*J
wd0FsKb[g#1TN*ydJBhJ .p}䁤`%JHj~"kJl`P_Q#&6_F!Ίrw|Ȇ%vs 0$F P8n*c#eWO# dEM/dZY4N[j\Y]t~b3ݚKJo4Qff.|RSxLVo%$ߟ;I"XjRRC׾YK#PĹ?&w3B\4 D#\Jη!dk>Do
C5 0*6G7`Y4םke.Sv(J SIH$&pf|fP!rې3 %o#%gN`cM,E/}PV*(d}aڠNц3!qɿhinvhֹ`,+?_hEAi*Uu)LB)̈j{Rs$yTYQ*)_
]M4Pa6dĒLJyw*)3x÷/#s.Æ
(J%{i􄉽whNi^hAyW+į8SɜJg )%8Wa6uaZ gZT vWUdğד]:C
'52CON.kYp,c9HO#JZ3T|{8+Q䫚YYkytơʫ6tݼt$V#z9O*7`BqdįʸT?Tc=YP&c:PLB~Ȧ&3r*kg9CY$pJMN鱆cz,eM0V!I7uD8_c9odĿKɖy3|Z+r`w-{c
ww*nej5h8DLHO-
}g['cT
U :$5WTGVNo+ٚ#v7j-!G[ZOdondê099E ڀn҂c#R4&#fEkSvGxժI+7Jѐ>L)u8:
Fv*$ܷ{s4ğ7! ddҤ6
\a`dvFSeT#"uVVhbԧRarEr\]C(rMfarw\j7n[Xon*1x i
J?mL0Kh.Rsd&CtVWxxny:fN
ǃ>T
.)":`pUÂȆ^#z#1·fRu:;KN
sN< |zXqs<nWP&O3qēXx-FTd,јϐxE$LUf{%dHĤK4RX[9g^fY7"הv6
#t*u-#PhySi犂>v#KDan
F[0pxԐÍL)1YlG
ÄdĨ#1i.%u7_shnA A,G)KJL8HEgc_X}Sr6E;#_%9#!
aSi#5dF E#'Z=&XrPMH4<f7/T"vdĞ!`JFL0ʊS2TKαYiFgƞ`C'?(Lt bW$"ᾇ0V5)ǗqWX}(-H*=[bi3(ܷdĚ!SJɎtoZd`$c("2ݩEUSJjzBׂLߍA%a#d'dZ(ʺ?j}\C;8 gjV
oz3/`dĠN`dė^L)Xc 8, 7>xzMTT| ZRBe#M7X7"(8n2f Nc#AJ3" 0A
# 3qxf;CAA7db8##eps
EdĬ52_E S0/i
[M94<#١n2("6k97L+ƪHSjW]Z6Uoi??3967ī?_K1NDN9dX3<6`>Vn#"jM(^BN GEc:29.XnOB(3qNyjH$~s˧~8ԯ&ˬ<\(.:s%5(p3:yt
e^;DGY'd~T1GP1kkDs#bj /R!r0PU
fRϪ\)p٢4l*%:H'zmᠯ<`NjNe%l\y2k(7Rm%fDd⺲T0G%z;*&e_2֏SLZvv$_զ76!#r?mc2Up]gU;zgǝCTY jēnJ BQ,$#ŶZxr$Q,.d+9VI\^)[pPXh*#z8vYáY"$Ǹ*
üK;㡯5*ƈ#A`sJ*]_"M=L\ǖ֗d'×^y2uR[DdA!`
aLQ$HQJOe,ڰP*x=]J%. "!hI&NX2T>rp4\0P%*D(wT%~nZLAMdW~#I\E3.99.5LAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUULAME3.99.5UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUd|HUUUUUUU0aAۇL>!*d2XqD ` 1 #l|9P $+t0\F f0N_6b18a`1HT:a`d|H#wp#0
0:pLOwfĨ.fV\O`,1qMLKBȰ#- LJ91G{`/#UT1FSj-m3C}bSd99AU
3#^d0]sP3ƀDVd1G8Na&a?(vfPc|\hQMdqD0iA yQH 6DRH\f*0BO
ܾhl\ERdĚ5k_*]eJ,e#0h(:Ko&ɲi&QU>
Zn#ET&I]"r[SScv#yQG#ڭ% XiOiȵ4tjyѵdf{dG_8Ϧ˪Os1Ӫ~b}=#
uEH͋(|a}%ڮ[lr\ʢobx
Ç~>
[2MwۙrjdW>N<8#ZT})1sB
zȀs뫳AAsK#\R:Zh"BOR¯bw..\jk!,G6Ʉ%Bbh+|ۚfsN\8`\ЖL cCdh.t9xڽww1LeJ-OgBݑCH2(D`3S c...DaD4>-5i;t8cmr7`o-А aI~_*d1${ڴE0jpA?D1ypr|H|JAQ˲8-2;[J22Ȁݝu|;DdE}^$C) 87ԬfS!nA{o1BJEId" 6E!ܻǜ2VwnqQy90I(Цa3]]]t0 _NgWI
eV3khuv[OcGyEMYP1중ƻր)~= &]d!>k
ߟ$Ѻ>
YaH
o>G]-կW+?fԬok!DXY,I(SrFH#$Ʌ2:2\Ŵ&(
<K2d4>*6l+_~5M
AAy'X84wSYƍ brTrʈ
eMw7N#ƭV]o!{ǹL"b]Bρh16dDPRJS_\n6aOPi,zL
:;%P5(ۍ9/ 6qoD֏X01}p\u#TIKc`,R+L$YLOdS2^xľw8ɯiKR/QDsf}zo{MA% gbU\}
Ⴠʨ-':#qt>$(QM{Hɱ"]<~O_^dc>zGHX].S
ۘ%VFr(#Q,p#mSfO{u9 *}u}ִOt#ws>qd$Ttޢ,(ld{TɊ*kJ3r_RU˶6~*^ba..-:C٦]}?*6O*8WBFlHU1.st/dČ\
Ix )v1S3m{P.:6\>Q2t̰kQ6ȿ[8tˎ!ڑ*]O.''#vZZZ,In>5u*P*pNc0Wbi(
c4 iCdĕ
ž8X6O5!HH%TGZDoYJ_r
-7{
.D2YaWoU]JG+d;*5A.{9S`#VX
8cLML?dģ!#]zYyAHLH:VؠT<jN!YSX]E`Y־WzP>̺>ӿr'V+`;pJ)A6mA#RJ&Qdġ!W[#"Cn
X&X4"To;2gr3m#0iȖ
Ϙ*ǚִn|?򶪏^$y<1^w-R3 ,7ASFqKCdWOWUrdĝ5Mx)O>>7
ŷ3}_5ky+1yC7Y^qD\<<9l#5K("YʕItj"M51ĐfbtL8nxAdH,t:hOG_AJ$.Bj^j#-yL)#!,
rD>VGVvL-k&/Z̒OJMK&f|ţ֟P$G"g }Q
Idƾ~\C1Xөbfd8&u>nXAGۨO`CDrTPE)B>_OE-ݤ ?jd&^oW$Q;Wmad C~OBOazRp0TF0}޿Eҳ'ݷduĎQs9ḨawWH]dRXԬ~ZXH%MKg)DRd2ܲ)~o:ԷwFld"sl$DLSR?ӷ
YdI8 U8%sY %~p̎]kYSkz-1#pMܟډ9$䳳To4d#bdm[0dϺjjy(תj^_6ݷؤĮ
"_F(]$P0P#U/fFȠv68%atvaː^#=80pse$]L%# 1dM/:`lTږI\
hNhv
L`-4/oִWTʂ"ȧ~p($':|0403['4yP5*=zi֕#Wf\ysacd!#6+#Y\qoU,\|pa9aQ<M ѱkXCmuk$EqAP8$#ozo׋Zo׋&8yOohHiVq9dz~(s%WisOPjWIg9/5T,jD-XeQ#H҆!6>LJ%i^zPY|jݢlew<b6#V-K0rՠօLd#ڥtEɟʲ&<ܻN$U-Gӎ?H -/sÀ֑#MjWBi9x%<'Bqb [LP d2r^HPNB"Q`PPqN.A"R!`AqqsvOSQՃTRMB3M(|&e^L4<6bإA=Qt#1gBP_dC^
OS;OYȏ4yϿaIJG g橣vs۩[44&?IGJ%"Lη/;_߶e!Þ_j! YSyQ5VIk #q`dO4;^n 'ÃzL$v"4_I.yKvQɕ㥕Vy'ͱtvb1얰Qp#iң;j}){|n[V%FCf-}r:]G3eG&0PQ
Ddm+ZY_fwkljbFzGP9Rjr(T :
V2
2/nܳPZ\oj>gԲiʆ*~,*)c1RT:Z%qoZvnޞg{e VIdH֐>,B뤋i^v>+vTL#
C᤬бaXeDM,gGR'_n<|5bH:ࣃ\Un
Umm^C۩Cj#4e39
ق2dėɒZy~=LՈ
ՊC7WQ{+ۤH0\
e6kΜcGe}K]u^;Tk 6tmbD8sM Ʒb?
Wmi 8dĦ%YZȟ``9#(I xP9,(Lܲy#M2z
08bh`C,[ckZv7OU'6:Brm){ׁ,#_dēI>XXO^1JrmLe3I}syL͂%NgFZ{Lؕ}T|xE$]K.U{ !# 3m?]lGjV{0Ɏ|\w-W!3ݠ]qoXdĨq6Dc)B_ݕfK<U&2nYݵ<",XQ*b8w%&:&X5IesLBsh{IAC0ݲ⯩^ ̨0:/rpcdĽ>348D;r؍ӯ*\s0}B% PyT6n*y(MUuj$1?=\)Ît^q,gg1l)d"KC 8%s(E2 ʁF t2!<:
:0<k¤5\!JSv ΅JDI$0ŤSIB!7_'"k"XHAyԪU4Qh^<=Od"{>{;ُO>Da Ly7\F
z`A!2Is ޿s6 ՌHq;"r>QH-Fz)Q|B1t߬*_)Ld%c:L J^ ۧ+65d
a|:Ppr]is2=V;nfDkŮdh\%h%#!0`vc򱟙JͲ?cj_d{ݞyB^o2q\:G
)E0)иzH_Ff(=Z1<Â/òPm[#8!(NCViE#6+kXmΙ*SL7
ƵUi_S?+D5sd!ɅLd
a3m6
hDdj$؉տ}/g#Z
C測AC"
(-13~71&8I*Hh.ͬY4td\
Z#$$"]MMv,7{zbToShI&&)%uvmk
n"35j_KO֖Cfwf;C) dR&d=k6rKu3s2%Cȝ/ɻ>X%·CJ6<
IXkYj=dN
CA`hyVX%
x8Wo MvV͊SD^)W*0V0RXR+dĆ9jOX,tj˚àQ̆iQãAJnV6tΑc
(Äj~֥_ۛ'9ĬgPL,֯q)e7\PG
>Ċ#۾6'w}MEdĔJpSL(A=#s`e0pd09<R
fny5bhO.
)\!:]"XZge;!/4sD"LSH%ԍQsuNOR]p\M$\bdl67dħ5._ݐZihN uN'fFP
erE0Im=s
=rD3SgY,VqVE5u+mc:n
0<CH Gq-T0XNfbۤMV_QmdS!f؀htYkcd_M*4IP11v8/ՄFA*JNknhc9)J _G
96ఀܡٌIB`'$AJikӈ5rAjR4dO":f^Bʢ堠xHY?zTюp.>L((h
F#
08.|#t +sik
MY&'j`*X'% (dIE**bDX9i[}Q?Msru^<\3];X7!ʔfR(os,2S is
ƋUY4"~]t=~YMoyU`GhdYR^˪p"ㄠl2;X8&zwBjJyAwjB^&-D$8gߘpbj["oQٹKjV[t)Bc,mO?սJke
ciT~ndj>DҶ+`-a[V?+uBrGjKjаw%R?% odxD6[;g+p&/`Tf\S7CULOoҖ nku+3""J* #s-dyj:~yF%#ADBB"#:o
_MޔBDsܓ$pc #;g|F:܊b#Ë;$maZE$jdĊc^C?z?׹BNthH\G#A:#CQ
H &gJ
fmֽUSddsI5k2PUffg-KoRfga{ffݙ_j$dė:h$;L4'ݸpT'<+ܨ\IG-tvǶFq;[JQ}KRZx5h <0`2:|P8[RV#dĥ:2v;pM!9IP&P
ډsCCSzDIg_hO=9 B co|meb"ϐe)fQuJ7fr]ꚶjQdĵG{%PRA'VJy^;PYD\*
\31RߘkF`/( EJfHX\uMNH" xj\9Vϫ9*0:5V2Q\Md2D\Õu,Y)T6;cp4]8\[gE\FF!;uJݼ
T3%OeńIhտb +MEDCÿ2_x{^BeدOd9.
fMHS?]+˼ϙ`,y
!{OC5*(i:V.f{9M\}#?I7#Nݸzx<X`襳9;Fa(*d"HHD%(,(F8*H廑S/L!
GS܋ 8h69[>!r!Т$H(V<S.Z%' I
;<87)֊
dI6F(P{')?nQ<( P+b
huiUDǕCG#b }p1ކ/C?iuc/}NuY6TףsH1ů'$2ʓpB+f&Q7tReZVd!Vw׫:!LfH,q-*i"bA
GEەI:IK.Qңs_n׹[yzWv5B>V˗+ašd V,|;ݙJ)XFCX4;-PZCƁ%
E#+4G<vExlj מ%?M8poz9[GVr%S7_?d +DؘBc;YXC^ei$`hYR<eX7
d*dm5g>uPzlROtZ ]r Җ}BIF̼SR4OԽd ╖ʸ6#3)}KF?BΨa1+UGejLxuPH Lem#mUWKsN{7BexhxuxULq})^iNO"oD,[=6nY_d ]J\'ZߔtX|ONYGaHNa*#-HwAVŷזAysR^ISׯOon1V(cyF6"ڬ5~C]dz_)jϋ#Kd .6X_Qɬn7u3NF_gIۻK$g=4;0t
J9gˎa:!X`D*J&w.S*5EZHdsw"`W9d-:ɞ
Zw}$PqbYįb<U9T(!Llȴͧ]{4sP:vԅ!~1YRa]+Ri#T>]0mHI]dĴ'|V
jRq%
vpbAr}4&ىg'X:40KbIh瑏XA"}F9b?̀*8T,#Ta$,Ya"h_dę$#
~EHkv1]֥ӱ1yZxXqy-4֏K:Ճ:TNDP{ScR,QF"'F`.R *RATsq܀d±ݬdċ!+^lZ;:T,1j
f3V˹ݻ?YuW>5(Jrf3tABG$Ot"Ψo̴3d-0#t{oJ *7mq37<0Ddĉ>M#OҬ4I6aNTyTsWi[!̥d,oYJL]A\; Ti_5$Ua!'L]#r57,yi4"hoMuӵ(_?
#P9gdĝzD#5^X=v;jD0k-XΏF&gR|3[_׼MYnRVន%Es?e#܅BiSXWo(qlܖQmukݙBU^ "+10dĪXC{BUf'*']REJ,]WX~[-hH2
C#\ꄶ&#i{0a1nC98G.>dĹX]X*#:P|O<lԴW|
Pe%r!A<r|f 1,B`H, 8FLAMu9udC_[d<bUdҊ豋d5:_*)ZFInqՁFM$L-ɀ7
LՑ}>+<?H/dӠrJdDj/jZ~s2^͉u>.[5qWM zvXu*sE&&B0VͯoZ'KABr#dwن0/7"qǚ0K`9kft));9V侗'7
[d*2H3MS{;&hx蟑=
2%'^٢,c.!Y1bj6[dĆD*6O}:hT#XX>KzĹV
&?N e8 %
QtcR-s__+"!ùۅJ^N]Q=M2Ѥu-ZpbdČ#D6R+`4ڷ ʛ+,P|GO+
dEL1\~+F}*C#L
q3
:
&*(?cPƘ&s,b=gQ5O$ny% }kh1dĂ!^|Ka5WC-n-HK5g55&
Rnǝ'I,!̡Ӈg]rەzKB?##fwǀaXg15)/dNDVcbW0jח4c_Ϭ{G)x+[gHʝ
HB
#^7"#wx{bkSt1X#.?#`a[\9{/ 9#T;)mS!Ⱥj$TTXdē6xzs۹&0Eg4VvTkн=c#lj9ڢ2OwH3X.c]IFKk것³΋-[B.*P;dĤĢ)ZEs]IS_5hC_][
?1){܊E4:9F<MvsU˹X#/+R()Šq:F_9Ns2eCD\IRdj|$DĐQOdĴN
:c)>Tm}ۦWov{zU)Y|twQmxߨj>GeRyl1u"</wbr2򖹭--*o*hs(Scь
[:<_}[dľ#y9oVv4V
fKM#P82VuX}Vh8FaUD$v0ceAT>WNՂyg3=ncu<T*
D#֠ds6
D,
N2VY*yen5Xt"(1HAar%*LMzDfW99FCnRAqzp`hÊxA1C ѫViAqd޷H̼Gp^_N+NUxvX;ҫJALb92ܵhgo>fYNj5={Z3ӉbYw&PEI(\6o5D2d IaIvgrQs~by!")|Di:RdžlE%s#|E#\9d3H3zfPhlsL&et3d)D3q"Z*GVHܖf(P98#z A`b|(0| H;-}!!df>܅rNV1jV7dN,;*d!xöޕy{ }QCS"21jP:FvVSEnjyۘ/%h/MpuzϚ0}n`WʁlVv#\qoO͏5+9߉e`4$WPՅd لm&óf-(}n
;CRȶu| 1xٚx-T^dBQ=k
#,:*]~L"8\OzeLBN{
H^EM]bV FoʸAnNSܡrdi),:[ {*Jd{$V
Êܒn*BlFFTd]r)
lYυs~NѦ_?<j7eH##}4iNv!DidJ6ѣ}d!֔'xzu:
H!0`8CC` ƀLAD0/?&Sy!U^a<YR+#E0,2XF͡jJS(KZw1i1V8ҋN^Qd.aKuDˈNY J&#
C(kM8V8`e
($V܌9#\8#ɟb*өcIiU%hÒYWP(bI!d#Rbhʼ.<`cy
`-w|/ӄ_n098'.0ؠ|SJP{K "z߸u!6TQҥ|M2'bw꾔>'evסsd!:bd-, o]~jZՂ.1)ħ#c pphXp-`Q
"DXI{ێˑsD!P:T#YGhFC CU`uL2w߬V/מz-gvdz`(ļ8tnKOC{&\(Cle4[I%xPli !mxlc3/rV諐.JO=r<*gޛ
̢zUN;
P{N3k
P!Lo&Cn'~DRd"d\8WQs{ohtN.Tk#[r/K_3"q꽍plSU4fba&p*sNyq՞ Yצ)׾TM*d;L6]xZrz9E:ae,ZÆ1:;k;<[JS4צ/Ǧ?=󿿛_WS~"=FPɩ*>ĩ\k\LBe/Zdă4d:xs+"VIԨ
ƃx*;\&,2wAd7j9%}G4~5YOhDMζ!.ڧbfʱ3R4!ipL8^AsG2-Q!d4)sZyP<8,:VeDA̝EpY8SBOِCռ]\]ClWcgQf.BPA~ga9wٻD2^c:xfXdPP˵ƈildr#Khn+īo=g 0^h)dob#cLDD!<麱_K1"cS0YHAB{(
!o=g(^E$%ERYdHA)'iL
pʧ8X&/T|;SȎTsj3203N_4l?|3_'TwHÞ;|>JxVlj+1-#K}$iLw]iDad%~a^IiXGܼwB2ʼn!Ͱȧ2YhY;?" *EǂN7FXpC$,~`K=xwmqDTDh.ufW+K:}DbH5~gj#Ȣ>Bd4Kj0JU]9[}#'򪶭I[=]>R~ߩDžG#CTF&jNwge]kqR䋌,,?K&*eH
Hp3tUDsWje(Z=01dEH;e4+JJݷk[e`S$X[ewxݵ(Li!6EM
26`'q9Ăn֫SoeOGQqdU2
FԓgJt nnLRkF"Veߜ
}WpcrNQ{K͛{lK$/p<w!#I#)$Kޱb;b'R%dgF]O0xwS<j2*!L5hT9I'%ʮO
J;ZT4PCd2m{22Rj
R2611r;%$4PmDY2ENe"Nwdz3T1cȢL##ކ
$B<#ȩ:$2ELHDJzɥU1.
,A0""]!eQ&9U>2
(.YVfUST)KJgKGoRRd-{1b1jRJR)t1ig1))JR1gR/C~`E(
LAME3.99.5dBHLAME3.99.5d|HLAME3.99.5d|HLAME3.99.5d|HLAME3.99.5d|HLAME3.99.5d|HLAME3.99.5d|HLAME3.99.5d|HLAME3.99.5d|HLAME3.99.5d|HLAME3.99.5d|H
--------abcde123--
I figured it out, I was using a LoggingInterceptor for OKHttp and it use to convert the bytes to string and log it and due to some reason, it is unable to recognize some characters and it gives some junk values for those characters and due to some reason, those values persisted back in the byte array also... which used to give malformed mp3 file. After removing the interceptor...it started working... so step 1 is done...more to go.

Categories

Resources