3

I have a HTTP post request, it takes a JSON with the following content as request body:

{
  "UserName":"ApiService", 
  "Password":"BandarAndroid",
  "AndroidId":"15df3b3a90XXXXX",
  "ContentId":"704",
  "frame":"1"
}

After requesting to the server, I get only one image in response(Instead of anything (like JSON)). The appropriate image is created on request and has no web address. my service Address is :

http://xyz.website.com/api/DownloadFileForAndroid

and my response header is :

cache-control →no-cache
content-length →29837
content-type →image/png
date →Mon, 09 Sep 2019 08:42:23 GMT
expires →-1
pragma →no-cache
server →Microsoft-IIS/8.5
x-aspnet-version →4.0.30319
x-powered-by →ASP.NET

I don't know whether to use retrofit or Picasso to get this photo,

in Picasso: I can't send the amount of JSON in the request body.

in retrofit: I can't get the photo without the url (Address to point to the photo like www.site.com/a.jpg)

Roaim
  • 2,298
  • 2
  • 11
  • 23
sajjad Yosefi
  • 190
  • 2
  • 12
  • I think you can do it with `Picasso` if you add an `OkHttpDownloader` https://stackoverflow.com/questions/49673110/passing-header-to-picasso-while-viewing-the-image-using-url-android – coroutineDispatcher Sep 09 '19 at 08:08
  • Unless I don't understand your question, but what is the type of response your are expecting? Your `content-type` response is `image/png`, and it is normal. – Harry Coder Sep 09 '19 at 10:00
  • I don't know whether to use retrofit or Picasso to get this photo, @HarryCoder – sajjad Yosefi Sep 09 '19 at 11:59
  • picasso doesn't understand that i want the post and It gives an error 405 method not allowed get . @coroutineDispatcher – sajjad Yosefi Sep 09 '19 at 12:13
  • Can you provide a valid url with valid request parameters to check and find a solution for you? – Roaim Sep 09 '19 at 16:26
  • The above information with the changes below : "AndroidId":"15df3b3a90dc5688", and this url : http://shop.atiafkar.ir/api/DownloadFileForAndroid @Roaim – sajjad Yosefi Sep 09 '19 at 19:25
  • Use Glide for dowloading image from url – Navin Kumar Sep 10 '19 at 09:14

2 Answers2

1

As you requested, I am converting my previous solution (Kotlin) to Java

Example 1: Picasso

public void loadImageWithPicasso(ImageView imageView) {
    Picasso.Builder builder = new Picasso.Builder(imageView.getContext());
    RequestCreator picassoImageLoader = createPicassoLoader(
            builder,
            ImageRequest.DEFAULT_JSON_BODY,
            "http://shop.atiafkar.ir/api/DownloadFileForAndroid"
    );
    picassoImageLoader.into(imageView);
}

public RequestCreator createPicassoLoader(Picasso.Builder builder, String body, String url) {
    return builder.downloader(new OkHttp3Downloader(createPicassoCallFactory(body)))
            .build()
            .load(url);
}

private okhttp3.Call.Factory createPicassoCallFactory(String jsonBody) {
    final OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .build();
    final RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), jsonBody);
    return new okhttp3.Call.Factory() {
        @Override
        public okhttp3.Call newCall(Request request) {
            Request.Builder builder = request.newBuilder();
            builder.post(requestBody);
            builder.addHeader("Content-Type", "application/json");
            return okHttpClient.newCall(builder.build());
        }
    };
}

Example 2: Retrofit

public void loadImageWithRetrofit(ImageView imageView) {
        final RetrofitImageLoader imageLoader = new RetrofitImageLoader(imageView);
        RemoteApi api = RemoteApi.Factory.create();

        api.getImage(ImageRequest.DEFAULT_BODY).enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                ResponseBody body = response.body();
                if (response.isSuccessful() && body != null) {
                    imageLoader.execute(body.byteStream());
                } else {
                    Log.d(TAG, "Retrofit onResponse(): CODE = [" + response.code() + "], MESSAGE = [" + response.message() + "]");
                }
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                Log.d(TAG, "Retrofit onFailure(): t = [" + t + "]");
            }
        });
}

RetrofitImageLoader class

public class RetrofitImageLoader extends AsyncTask<InputStream, Integer, Bitmap> {
        private ImageView imageView;

        private RetrofitImageLoader(ImageView imageView) {
            this.imageView = imageView;
        }

        @Override
        protected Bitmap doInBackground(InputStream... inputStreams) {
            return BitmapFactory.decodeStream(inputStreams[0]);
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            imageView.setImageBitmap(bitmap);
        }
}

RemoteApi interface

public interface RemoteApi {
    @Streaming  // Important
    @POST("/api/DownloadFileForAndroid")
    Call<ResponseBody> getImage(@Body ImageRequest body);

    class Factory {
        private static RemoteApi mInstance;

        public static RemoteApi create() {
            if (mInstance == null) {
                mInstance = new Retrofit.Builder()
                        .addConverterFactory(GsonConverterFactory.create())
                        .baseUrl("http://shop.atiafkar.ir")
                        .build()
                        .create(RemoteApi.class);
            }
            return mInstance;
        }
    }
}

ImageRequest model class

public class ImageRequest{
    public static final ImageRequest DEFAULT_BODY;
    public static final String DEFAULT_JSON_BODY;

    static {
        DEFAULT_BODY = new ImageRequest();
        DEFAULT_BODY.setAndroidId("15df3b3a90dc5688");
        DEFAULT_BODY.setContentId("704");
        DEFAULT_BODY.setFrame("1");
        DEFAULT_BODY.setPassword("BandarAndroid");
        DEFAULT_BODY.setUserName("ApiService");

        DEFAULT_JSON_BODY = new Gson().toJson(DEFAULT_BODY, ImageRequest.class);
    }

    @SerializedName("UserName")
    private String userName;
    @SerializedName("ContentId")
    private String contentId;
    @SerializedName("AndroidId")
    private String androidId;
    @SerializedName("Password")
    private String password;
    @SerializedName("frame")
    private String frame;

    public void setUserName(String userName){
        this.userName = userName;
    }
    public void setContentId(String contentId){
        this.contentId = contentId;
    }
    public void setAndroidId(String androidId){
        this.androidId = androidId;
    }
    public void setPassword(String password){
        this.password = password;
    }
    public void setFrame(String frame){
        this.frame = frame;
    }
}
Roaim
  • 2,298
  • 2
  • 11
  • 23
0

I don't know whether to use retrofit or Picasso to get this photo

It is better to use Picasso otherwise you have to write a lot of codes to load images efficiently if you download them using Retrofit.

You would be happy to know that you can use both Retrofit and Picasso depending on your choice to load images from that API.


Before going ahead with example, I want to clear one thing up that you had a misconception that you needed to send the above-mentioned JSON data as header but after playing around with the API I figured out that it takes the JSON as request body.


Examples

RemoteApi.kt

interface RemoteApi {
    // Retrofit
    @Streaming  // Important
    @POST("/api/DownloadFileForAndroid")
    @Throws(Exception::class)
    suspend fun getImage(@Body body: ImageRequest): ResponseBody?

    companion object {
        // Retrofit
        fun create(): RemoteApi {
            val retrofit = Retrofit.Builder()
                    .addConverterFactory(GsonConverterFactory.create())
                    .baseUrl("http://shop.atiafkar.ir")
                    .build()
            return retrofit.create(RemoteApi::class.java)
        }
        // Picasso
        fun getPicassoImageRequest(
                builder : Picasso.Builder,
                body: String,
                url: String = "http://shop.atiafkar.ir/api/DownloadFileForAndroid"
        ) = builder.downloader(OkHttp3Downloader(getPicassoCallFactory(body)))
                        .build()
                        .load(url)
        // Picasso
        private fun getPicassoCallFactory(jsonBody : String): Call.Factory {
            return Call.Factory { request ->
                OkHttpClient().run {
                    RequestBody.create(MediaType.parse("application/json"), jsonBody).let {
                        newCall(request.newBuilder()
                                .post(it)
                                .addHeader("Content-Type", "application/json")
                                .build()
                        )
                    }
                }
            }
        }
    }
}

ImageRepository.kt

class ImageRepository(private val api: RemoteApi = RemoteApi.create()) {
    companion object {
        fun get() = ImageRepository()
    }
    // Retrofit
    suspend fun downloadImage(body : ImageRequest = ImageRequest.default): Bitmap? {
        return api.getImage(body)?.run {
            withContext(Dispatchers.IO) {
                bytes().let {
                    // to load bitmap efficiently follow the guideline provided by -
                    //  https://developer.android.com/topic/performance/graphics/load-bitmap
                    // otherwise you may experience OutOfMemoryException
                    BitmapFactory.decodeByteArray(it, 0, it.size)
                }
            }
        }
    }
    // Picasso
    fun getPicassoImageLoader(
            builder : Picasso.Builder,
            body: ImageRequest = ImageRequest.default
    ) = RemoteApi.getPicassoImageRequest(builder, body.toJson())
}

ImageViewModel.kt

class ImageViewModel(private val repository: ImageRepository) : ViewModel() {
    private val _progress = MutableLiveData<Boolean>()
    val progress = _progress
    // Retrofit
    val liveImage = liveData {
        _progress.value = true
        emit(repository.downloadImage())
        _progress.value = false
    }
    // Picasso
    fun getPicassoImageLoader(builder: Picasso.Builder) = repository.getPicassoImageLoader(builder)
}

Finally,

ImageActivity.kt

class ImageActivity : AppCompatActivity() {
    private lateinit var dataBinding : ActivityImageBinding
    private val imageViewModel by lazy { ViewModelProviders.of(this, ImageViewModelFactory()).get(ImageViewModel::class.java) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_image)
        dataBinding.lifecycleOwner = this
        dataBinding.viewModel = imageViewModel
        // Retrofit
        imageViewModel.liveImage.observe(this, Observer {
            dataBinding.imageView.setImageBitmap(it)
        })
        // Picasso
        imageViewModel.getPicassoImageLoader(Picasso.Builder(this)).into(dataBinding.imageView2)
    }
}

ImageRequest.kt

data class ImageRequest(
        @field:SerializedName("UserName")
        val userName: String? = null,
        @field:SerializedName("ContentId")
        val contentId: String? = null,
        @field:SerializedName("AndroidId")
        val androidId: String? = null,
        @field:SerializedName("Password")
        val password: String? = null,
        @field:SerializedName("frame")
        val frame: String? = null
) {
    companion object {
        val default = ImageRequest(
                "ApiService",
                "704",
                "15df3b3a90dc5688",
                "BandarAndroid",
                "1"
        )
    }
}

fun ImageRequest.toJson() : String {
    return Gson().toJson(this, ImageRequest::class.java)
}
Roaim
  • 2,298
  • 2
  • 11
  • 23
  • @Farid Thank you for your advice. I am sorry for writing a long answer. I will try to make it concise and remember that in the future. But I felt disappointed when you said my answer is irrelevant as a whole. Basically, I thought it would help him if I gave him the complete code so that he can just copy and run and at the same time breaking the code into several pieces so that it is easily understood. But I think I am wrong. – Roaim Sep 10 '19 at 09:17
  • Yes I can. Can you list down the functions you are facing difficult to understand that want me to convert to java? Or do you want me to convert everything? – Roaim Sep 10 '19 at 13:57
  • I don't know anything about Kotlin ,I don't understand the codes @Roaim – sajjad Yosefi Sep 10 '19 at 17:26
  • @sajjadYosefi I have submitted another solution in Java. Please have a look and let me know if it solves your problem? – Roaim Sep 10 '19 at 20:44