package awais.instagrabber.asyncs; import android.graphics.Bitmap; import android.os.AsyncTask; import android.util.Log; import org.json.JSONObject; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.UUID; import awais.instagrabber.models.ImageUploadOptions; import awais.instagrabber.utils.NetworkUtils; import awais.instagrabber.utils.NumberUtils; public class ImageUploader extends AsyncTask { private static final String TAG = "ImageUploader"; private static final long LOWER = 1000000000L; private static final long UPPER = 9999999999L; private OnImageUploadCompleteListener listener; protected ImageUploadResponse doInBackground(final ImageUploadOptions... imageUploadOptions) { if (imageUploadOptions == null || imageUploadOptions.length == 0 || imageUploadOptions[0] == null) { return null; } HttpURLConnection connection = null; OutputStream out = null; InputStream inputStream = null; BufferedReader r = null; ByteArrayOutputStream baos = null; try { final ImageUploadOptions options = imageUploadOptions[0]; final Bitmap bitmap = options.getBitmap(); baos = new ByteArrayOutputStream(); final boolean compressResult = bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); if (!compressResult) { Log.e(TAG, "Compress result was false!"); return null; } final byte[] bytes = baos.toByteArray(); final String contentLength = String.valueOf(bytes.length); final Map headers = new HashMap<>(); final String uploadId = String.valueOf(new Date().getTime()); final long random = NumberUtils.random(LOWER, UPPER + 1); final String name = String.format("%s_0_%s", uploadId, random); final String waterfallId = options.getWaterfallId() != null ? options.getWaterfallId() : UUID.randomUUID().toString(); headers.put("X-Entity-Type", "image/jpeg"); headers.put("Offset", "0"); headers.put("X_FB_PHOTO_WATERFALL_ID", waterfallId); headers.put("X-Instagram-Rupload-Params", new JSONObject(createPhotoRuploadParams(options, uploadId)).toString()); headers.put("X-Entity-Name", name); headers.put("X-Entity-Length", contentLength); headers.put("Content-Type", "application/octet-stream"); headers.put("Content-Length", contentLength); headers.put("Accept-Encoding", "gzip"); final String url = "https://www.instagram.com/rupload_igphoto/" + name + "/"; connection = (HttpURLConnection) new URL(url).openConnection(); connection.setRequestMethod("POST"); connection.setUseCaches(false); connection.setDoOutput(true); NetworkUtils.setConnectionHeaders(connection, headers); out = new BufferedOutputStream(connection.getOutputStream()); out.write(bytes); out.flush(); final int responseCode = connection.getResponseCode(); Log.d(TAG, "response: " + responseCode); final String responseCodeString = String.valueOf(responseCode); final InputStream responseInputStream = responseCodeString.startsWith("4") || responseCodeString.startsWith("5") ? connection.getErrorStream() : connection.getInputStream(); r = new BufferedReader(new InputStreamReader(responseInputStream)); final StringBuilder builder = new StringBuilder(); for (String line = r.readLine(); line != null; line = r.readLine()) { if (builder.length() != 0) { builder.append("\n"); } builder.append(line); } return new ImageUploadResponse(responseCode, new JSONObject(builder.toString())); } catch (Exception ex) { Log.e(TAG, "Image upload error:", ex); } finally { if (r != null) { try { r.close(); } catch (IOException ignored) {} } if (inputStream != null) { try { inputStream.close(); } catch (IOException ignored) {} } if (out != null) { try { out.close(); } catch (IOException ignored) {} } if (connection != null) { connection.disconnect(); } if (baos != null) { try { baos.close(); } catch (IOException ignored) {} } } return null; } @Override protected void onPostExecute(final ImageUploadResponse response) { if (listener != null) { listener.onImageUploadComplete(response); } } private Map createPhotoRuploadParams(final ImageUploadOptions options, final String uploadId) { final Map retryContext = new HashMap<>(); retryContext.put("num_step_auto_retry", 0); retryContext.put("num_reupload", 0); retryContext.put("num_step_manual_retry", 0); final String retryContextString = new JSONObject(retryContext).toString(); final Map params = new HashMap<>(); params.put("retry_context", retryContextString); params.put("media_type", "1"); params.put("upload_id", uploadId); params.put("xsharing_user_ids", "[]"); final Map imageCompression = new HashMap<>(); imageCompression.put("lib_name", "moz"); imageCompression.put("lib_version", "3.1.m"); imageCompression.put("quality", "80"); params.put("image_compression", new JSONObject(imageCompression).toString()); if (options.isSidecar()) { params.put("is_sidecar", "1"); } return params; } public void setOnTaskCompleteListener(final OnImageUploadCompleteListener listener) { if (listener != null) { this.listener = listener; } } public interface OnImageUploadCompleteListener { void onImageUploadComplete(ImageUploadResponse response); } public static class ImageUploadResponse { private int responseCode; private JSONObject response; public ImageUploadResponse(int responseCode, JSONObject response) { this.responseCode = responseCode; this.response = response; } public int getResponseCode() { return responseCode; } public JSONObject getResponse() { return response; } } }