I'm trying to download audio and video files from the Dropbox API into my Android app using the Retrofit 2 library but I'm getting a JSON error because I'm not mapping the response correctly, but since the response is apparently the actual file I'm not sure how I can handle it.
Here is my code:
Media class
data class Media(
#Expose val id: String,
#Expose #SerializedName("name") val title: String,
var position: Int,
var uri: String
)
API interface
#POST("2/files/download")
fun download(#Header("Dropbox-API-Arg") pathJson: MediaRequest): Call<Media>
I added an interceptor to my client that adds the authorisation header to all calls and it's working because calls to other endpoints are receiving their responses properly.
MediaRequest class
data class MediaRequest(val path: String)
This class, when sent on the request, is serialised to JSON just as expected by the Dropbox API documentation, like the example below:
{ "path": "id:abcd1234" }
API call
val request = MediaRequest(media.id)
downloadApi.download(request).enqueue(object : Callback<Media> {
override fun onResponse(call: Call<Media>, response: Response<Media>) {
if (response.isSuccessful) {
// handle response
} else {
onError()
}
}
override fun onFailure(call: Call<Media>, t: Throwable) {
onError(t)
}
})
I also checked the exact same call with Postman and there are no problems with the call itself because I'm getting the response, but from what I saw in the Postman response, I should expect to receive the file itself as a response because when I tried downloading an MP3 audio file the response was an encoded string beginning with ID3, which means it's an MP3 file, as the screenshot below shows:
I already know that mapping the response with that Media class I created is not the right approach but I don't know how I can handle that response correctly in order to have the downloaded file, so any help will be much appreciated.
PS: I don't want to use the Dropbox SDK because I feel it would be quite an overkill since that's the only reason I'm calling their API.
The way I solved my problem was by mapping the response as a ResponseBody object and then calling response.body().byteStream() to get the contents as a byte stream and then decode them into a proper file.
Source: https://gldraphael.com/blog/downloading-a-file-using-retrofit/
Related
Background
I'm using Google Play Install Referrer Library in my native Android app to track some UTM parameters. To test my implementation, I use Google Play URL Builder to generate test URLs for my app (which is on Internal Testing by the way).
Problem
On the ReferrerDetails object, getting installReferrer returns an incomplete URL i.e. a URL with only the query parameters and nothing else e.g. utm_source=google&utm_medium=cpc&utm_term=myUtmTerm&utm_content=myUtmContent&utm_campaign=myCampaignName&anid=admob. I cannot really call Uri.parse() to convert this into a URI and get the query parameters directly (the query parameters are null if I try to do that since it's not a valid URI). My other option is to use a custom parser built just for this that I'm trying to avoid for my convenience. Is there another way around it?
Here is the code block that I am using to get this UTM data.
//other code
...
referrerClient = InstallReferrerClient.newBuilder(context).build()
referrerClient.startConnection(object : InstallReferrerStateListener {
override fun onInstallReferrerSetupFinished(responseCode: Int) {
when (responseCode) {
InstallReferrerClient.InstallReferrerResponse.OK -> {
val response: ReferrerDetails = referrerClient.installReferrer
val referrerUrl: String = response.installReferrer
referrerClient.endConnection()
}
...
}
}
I am using exoplayer 2.7.3 and when i am trying to play some url i get below error:
E/ExoPlayerImplInternal: Source error.
com.google.android.exoplayer2.upstream.HttpDataSource$InvalidResponseCodeException: Response code: 401
I can able to play same url in some other place in app. But at this place this issue happens.
Can anyone help me what might be wrong?
First, try to play the streaming on some web browser, if it fails, the problem is with this link.
The error 401 means that you not have permission to access this content.
Because you haven't set token for your data source. Set token like this :
// Create a data source factory.
val headersMap: MutableMap<String, String> = HashMap()
headersMap["Authorization"] = "Bearer ${"your token"}"
val dataSourceFactory: DataSource.Factory =
DefaultHttpDataSource.Factory().setDefaultRequestProperties(headersMap)
So I've an API which allows me to upload two different photos. A full image and a cropped image. Those two photos are in the form of ByteArray's.
This is the body of the API call
{
fullImage: ByteArray,
croppedImage: ByteArray,
imageName: String
}
My question here is, how do I upload/send this API request in Android via retrofit?
This is what I've come up with and it doesn't work. I believe it's because of #Query parameter.
#POST("v2/selfie")
fun uploadSelfie(#Query("fullImage") fullImage: ByteArray,
#Query("croppedImage") croppedImage: ByteArray,
#Query("imageName") hash: String): Completable
Does Android SDK support online viewing PDF file like watching online video?
We are using this sdk in Android client, but it takes too long to download big PDF files that like more than 50MB or larger. does newest SDK support viewing PDF file online? does not need download whole file then can viewing it??
I would suggest you to reach their support directly, they offer a blazing fast assistance and the questions are handled directly by the Android team: https://pspdfkit.com/support/request/
PSPDFKit framework offers an extensive set of API and covers multiple platforms (iOS, macOS, Android, Windows, and Web), they'll surely find a solution that works for you.
Yes they do, but not out of the box. You can easily create a custom DataProvider that achieves this:
private class UrlDataProvider(private val url: String) : InputStreamDataProvider() {
private val httpUrl = URL(url)
override fun getSize(): Long {
var connection: HttpURLConnection? = null
return try {
connection = httpUrl.openConnection() as HttpURLConnection
connection.requestMethod = "HEAD"
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
connection.contentLength.toLong()
else
connection.contentLengthLong
} catch (t: Throwable) {
E(t)
-1L
} finally {
connection?.disconnect()
}
}
override fun getUid() = url
override fun getTitle() = File(url).name.nullIfEmpty
override fun openInputStream(): InputStream = httpUrl.openStream()
}
Keep in mind annotation and form filling won't work properly in this case.
I want to send a PDF image to the server in Android.
The server spec needs I should use Base64 encoding.
So I should convert the PDF Image file to Base64 String.
Below is the HTTP POST request:
<?xml version="1.0" encoding="UTF-8"?>
<request>
<auth>
<!-- auth info -->
</auth>
<imagefile>
<filename>Test Attachment</filename>
<type>pdf</type>
<data>"HERE IS BASE64 String"</data>
</imagefile>
</request>
And I used below code, it is just Http post request code.
fun post(url: String, xml: String) = Observable.defer {
val request = Request.Builder()
.url(url)
.post(RequestBody.create(contentType, xml))
.build()
okHttpClient.newCall(request).execute()
}
In case of the small pdf file, it's ok. (Works fine)
But when I used the large pdf file(over 20MB), out of memory occurs.
So I tried another way like below.
fun post(url: String, xmlFile: File) = Observable.defer {
val request = Request.Builder()
.url(url)
.post(RequestBody.create(contentType, xmlFile))
.build()
okHttpClient.newCall(request).execute()
}
The difference is that I don't use String, but File.
I created a file using the Base64 encoded text with XML syntax.
But it is failed too.
How can I send the large data in Android without OOM?
You're going to need to find a streaming base64 encoder and use that to write a RequestBody that streams to the server. There is no such encoder built-in to OkHttp or Okio but you can use the one in Android itself. Use wrap on the OutputStream that you'll get in the writeTo method of your custom RequestBody.
https://developer.android.com/reference/java/util/Base64.Encoder