Allow sending image in dms

This commit streamlines the broadcast flow to handle both text and image messages in dms.
This commit is contained in:
Ammar Githam 2020-08-16 18:21:09 +09:00
parent 066a453aa8
commit 650062646d
6 changed files with 553 additions and 125 deletions

View File

@ -2,11 +2,10 @@ package awais.instagrabber.activities.directmessages;
import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.OpenableColumns;
import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@ -16,8 +15,13 @@ import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -29,11 +33,17 @@ import awais.instagrabber.activities.PostViewer;
import awais.instagrabber.activities.ProfileViewer;
import awais.instagrabber.activities.StoryViewer;
import awais.instagrabber.adapters.MessageItemsAdapter;
import awais.instagrabber.asyncs.direct_messages.CommentAction;
import awais.instagrabber.asyncs.ImageUploader;
import awais.instagrabber.asyncs.direct_messages.DirectThreadBroadcaster;
import awais.instagrabber.asyncs.direct_messages.DirectThreadBroadcaster.BroadcastOptions;
import awais.instagrabber.asyncs.direct_messages.DirectThreadBroadcaster.ImageBroadcastOptions;
import awais.instagrabber.asyncs.direct_messages.DirectThreadBroadcaster.OnBroadcastCompleteListener;
import awais.instagrabber.asyncs.direct_messages.DirectThreadBroadcaster.TextBroadcastOptions;
import awais.instagrabber.asyncs.direct_messages.UserInboxFetcher;
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
import awais.instagrabber.databinding.ActivityDmsBinding;
import awais.instagrabber.interfaces.FetchListener;
import awais.instagrabber.models.ImageUploadOptions;
import awais.instagrabber.models.PostModel;
import awais.instagrabber.models.ProfileModel;
import awais.instagrabber.models.StoryModel;
@ -102,23 +112,12 @@ public final class DirectMessageThread extends BaseLanguageActivity {
};
private final View.OnClickListener clickListener = v -> {
if (v == dmsBinding.commentSend) {
if (Utils.isEmpty(dmsBinding.commentText.getText().toString())) {
final String text = dmsBinding.commentText.getText().toString();
if (Utils.isEmpty(text)) {
Toast.makeText(getApplicationContext(), R.string.comment_send_empty_comment, Toast.LENGTH_SHORT).show();
return;
}
final CommentAction action = new CommentAction(dmsBinding.commentText.getText().toString(), threadId);
action.setOnTaskCompleteListener(result -> {
if (!result) {
Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return;
}
dmsBinding.commentText.setText("");
dmsBinding.commentText.clearFocus();
directItemModels.clear();
messageItemsAdapter.notifyDataSetChanged();
new UserInboxFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
});
action.execute();
sendText(text);
return;
}
if (v == dmsBinding.image) {
@ -127,7 +126,6 @@ public final class DirectMessageThread extends BaseLanguageActivity {
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, getString(R.string.select_picture)), PICK_IMAGE);
}
};
@Override
@ -228,27 +226,13 @@ public final class DirectMessageThread extends BaseLanguageActivity {
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICK_IMAGE && resultCode == Activity.RESULT_OK) {
if (requestCode == PICK_IMAGE && resultCode == Activity.RESULT_OK) {
if (data == null || data.getData() == null) {
Log.w(TAG, "data is null!");
return;
}
Cursor cursor = null;
try {
final Uri uri = data.getData();
cursor = getContentResolver().query(uri, null, null, null, null);
if (cursor != null) {
final int contentLength = cursor.getColumnIndex(OpenableColumns.SIZE);
final InputStream inputStream = getContentResolver().openInputStream(uri);
// TODO Handle image upload
}
} catch (FileNotFoundException e) {
Log.e(TAG, "Error opening InputStream", e);
} finally {
if (cursor != null) {
cursor.close();
}
}
final Uri uri = data.getData();
sendImage(uri);
}
}
@ -271,4 +255,71 @@ public final class DirectMessageThread extends BaseLanguageActivity {
private void searchUsername(final String text) {
startActivity(new Intent(getApplicationContext(), ProfileViewer.class).putExtra(Constants.EXTRAS_USERNAME, text));
}
private void sendText(final String text) {
final TextBroadcastOptions options;
try {
options = new TextBroadcastOptions(text);
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "Error", e);
return;
}
broadcast(options, result -> {
if (result == null || result.getResponseCode() != HttpURLConnection.HTTP_OK) {
Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return;
}
dmsBinding.commentText.setText("");
dmsBinding.commentText.clearFocus();
directItemModels.clear();
messageItemsAdapter.notifyDataSetChanged();
new UserInboxFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
});
}
private void sendImage(final Uri imageUri) {
try {
final ParcelFileDescriptor fileDescriptor = getContentResolver().openFileDescriptor(imageUri, "r");
if (fileDescriptor == null) {
Log.e(TAG, "fileDescriptor is null!");
return;
}
final long contentLength = fileDescriptor.getStatSize();
final InputStream inputStream = getContentResolver().openInputStream(imageUri);
// Upload Image
final ImageUploader imageUploader = new ImageUploader();
imageUploader.setOnTaskCompleteListener(response -> {
if (response == null || response.getResponseCode() != HttpURLConnection.HTTP_OK) {
Toast.makeText(getApplicationContext(), R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
if (response != null && response.getResponse() != null) {
Log.e(TAG, response.getResponse().toString());
}
return;
}
final JSONObject responseJson = response.getResponse();
try {
final String uploadId = responseJson.getString("upload_id");
// Broadcast
final ImageBroadcastOptions options = new ImageBroadcastOptions(true, uploadId);
broadcast(options, onBroadcastCompleteListener -> {
directItemModels.clear();
messageItemsAdapter.notifyDataSetChanged();
new UserInboxFetcher(threadId, UserInboxDirection.OLDER, null, fetchListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
});
} catch (JSONException e) {
Log.e(TAG, "Error parsing json response", e);
}
});
final ImageUploadOptions options = ImageUploadOptions.builder(inputStream, contentLength).build();
imageUploader.execute(options);
} catch (FileNotFoundException e) {
Log.e(TAG, "Error opening InputStream", e);
}
}
private void broadcast(final BroadcastOptions broadcastOptions, final OnBroadcastCompleteListener listener) {
final DirectThreadBroadcaster broadcaster = new DirectThreadBroadcaster(threadId);
broadcaster.setOnTaskCompleteListener(listener);
broadcaster.execute(broadcastOptions);
}
}

View File

@ -0,0 +1,167 @@
package awais.instagrabber.asyncs;
import android.os.AsyncTask;
import android.util.Log;
import org.json.JSONObject;
import java.io.BufferedReader;
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.Random;
import java.util.UUID;
import awais.instagrabber.models.ImageUploadOptions;
import awais.instagrabber.utils.Utils;
public class ImageUploader extends AsyncTask<ImageUploadOptions, Void, ImageUploader.ImageUploadResponse> {
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;
try {
final ImageUploadOptions options = imageUploadOptions[0];
final Map<String, String> headers = new HashMap<>();
final String uploadId = String.valueOf(new Date().getTime());
final long random = LOWER + new Random().nextLong() * (UPPER - LOWER + 1);
final String name = String.format("%s_0_%s", uploadId, random);
final String contentLength = String.valueOf(options.getContentLength());
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);
Utils.setConnectionHeaders(connection, headers);
out = connection.getOutputStream();
byte[] buffer = new byte[1024];
int n;
inputStream = options.getInputStream();
while (-1 != (n = inputStream.read(buffer))) {
out.write(buffer, 0, n);
}
out.flush();
final int responseCode = connection.getResponseCode();
Log.d(TAG, "response: " + responseCode);
if (responseCode != HttpURLConnection.HTTP_OK) {
return new ImageUploadResponse(responseCode, null);
}
r = new BufferedReader(new InputStreamReader(connection.getInputStream()));
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();
}
}
return null;
}
@Override
protected void onPostExecute(final ImageUploadResponse response) {
if (listener != null) {
listener.onImageUploadComplete(response);
}
}
private Map<String, String> createPhotoRuploadParams(final ImageUploadOptions options, final String uploadId) {
final Map<String, Integer> 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<String, String> 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<String, String> 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;
}
}
}

View File

@ -1,84 +0,0 @@
package awais.instagrabber.asyncs.direct_messages;
import android.os.AsyncTask;
import android.util.Log;
import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.UUID;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class CommentAction extends AsyncTask<Void, Void, Boolean> {
private final String text;
private final String threadId;
private OnTaskCompleteListener listener;
public CommentAction(String text, String threadId) {
this.text = text;
this.threadId = threadId;
}
protected Boolean doInBackground(Void... lmao) {
boolean ok = false;
final String url2 = "https://i.instagram.com/api/v1/direct_v2/threads/broadcast/text/";
final String cookie = settingsHelper.getString(Constants.COOKIE);
try {
final HttpURLConnection urlConnection2 = (HttpURLConnection) new URL(url2).openConnection();
urlConnection2.setRequestMethod("POST");
urlConnection2.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
urlConnection2.setUseCaches(false);
final String commentText = URLEncoder.encode(text, "UTF-8")
.replaceAll("\\+", "%20").replaceAll("\\%21", "!").replaceAll("\\%27", "'")
.replaceAll("\\%28", "(").replaceAll("\\%29", ")").replaceAll("\\%7E", "~");
final String cc = UUID.randomUUID().toString();
final String urlParameters2 = Utils.sign("{\"_csrftoken\":\"" + cookie.split("csrftoken=")[1].split(";")[0]
+ "\",\"_uid\":\"" + Utils.getUserIdFromCookie(cookie)
+ "\",\"__uuid\":\"" + settingsHelper.getString(Constants.DEVICE_UUID)
+ "\",\"client_context\":\"" + cc
+ "\",\"mutation_token\":\"" + cc
+ "\",\"text\":\"" + commentText
+ "\",\"thread_ids\":\"[" + threadId
+ "]\",\"action\":\"send_item\"}");
urlConnection2.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
urlConnection2.setRequestProperty("Content-Length", "" + Integer.toString(urlParameters2.getBytes().length));
urlConnection2.setDoOutput(true);
DataOutputStream wr2 = new DataOutputStream(urlConnection2.getOutputStream());
wr2.writeBytes(urlParameters2);
wr2.flush();
wr2.close();
urlConnection2.connect();
Log.d("austin_debug", urlConnection2.getResponseCode() + " " + urlParameters2 + " " + cookie);
if (urlConnection2.getResponseCode() == HttpURLConnection.HTTP_OK) {
ok = true;
}
urlConnection2.disconnect();
} catch (Throwable ex) {
Log.e("austin_debug", "dm send: " + ex);
}
return ok;
}
@Override
protected void onPostExecute(final Boolean result) {
if (listener != null) {
listener.onTaskComplete(result);
}
}
public void setOnTaskCompleteListener(final OnTaskCompleteListener listener) {
if (listener != null) {
this.listener = listener;
}
}
public interface OnTaskCompleteListener {
void onTaskComplete(boolean ok);
}
}

View File

@ -0,0 +1,207 @@
package awais.instagrabber.asyncs.direct_messages;
import android.os.AsyncTask;
import android.util.Log;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.Utils;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class DirectThreadBroadcaster extends AsyncTask<DirectThreadBroadcaster.BroadcastOptions, Void, DirectThreadBroadcaster.DirectThreadBroadcastResponse> {
private static final String TAG = "DirectThreadBroadcaster";
private final String threadId;
private OnBroadcastCompleteListener listener;
public DirectThreadBroadcaster(String threadId) {
this.threadId = threadId;
}
@Override
protected DirectThreadBroadcastResponse doInBackground(final BroadcastOptions... broadcastOptionsArray) {
if (broadcastOptionsArray == null || broadcastOptionsArray.length == 0 || broadcastOptionsArray[0] == null) {
return null;
}
final BroadcastOptions broadcastOptions = broadcastOptionsArray[0];
final String cookie = settingsHelper.getString(Constants.COOKIE);
final String cc = UUID.randomUUID().toString();
final Map<String, String> form = new HashMap<>();
form.put("_csrftoken", cookie.split("csrftoken=")[1].split(";")[0]);
form.put("_uid", Utils.getUserIdFromCookie(cookie));
form.put("__uuid", settingsHelper.getString(Constants.DEVICE_UUID));
form.put("client_context", cc);
form.put("mutation_token", cc);
form.putAll(broadcastOptions.getFormMap());
form.put("thread_ids", String.format("[%s]", threadId));
form.put("action", "send_item");
final String message = new JSONObject(form).toString();
final String content = Utils.sign(message);
final String url = "https://i.instagram.com/api/v1/direct_v2/threads/broadcast/" + broadcastOptions.getItemType().getValue() + "/";
HttpURLConnection connection = null;
DataOutputStream outputStream = null;
BufferedReader r = null;
try {
connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("User-Agent", Constants.I_USER_AGENT);
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
if (content != null) {
connection.setRequestProperty("Content-Length", "" + content.getBytes().length);
}
connection.setUseCaches(false);
connection.setDoOutput(true);
outputStream = new DataOutputStream(connection.getOutputStream());
outputStream.writeBytes(content);
outputStream.flush();
final int responseCode = connection.getResponseCode();
if (responseCode != HttpURLConnection.HTTP_OK) {
Log.d(TAG, responseCode + ": " + content + ": " + cookie);
return new DirectThreadBroadcastResponse(responseCode, null);
}
r = new BufferedReader(new InputStreamReader(connection.getInputStream()));
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 DirectThreadBroadcastResponse(responseCode, new JSONObject(builder.toString()));
} catch (Exception e) {
Log.e(TAG, "Error", e);
} finally {
if (r != null) {
try {
r.close();
} catch (IOException ignored) {
}
}
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException ignored) {
}
}
if (connection != null) {
connection.disconnect();
}
}
return null;
}
@Override
protected void onPostExecute(final DirectThreadBroadcastResponse result) {
if (listener != null) {
listener.onTaskComplete(result);
}
}
public void setOnTaskCompleteListener(final OnBroadcastCompleteListener listener) {
if (listener != null) {
this.listener = listener;
}
}
public interface OnBroadcastCompleteListener {
void onTaskComplete(DirectThreadBroadcastResponse response);
}
public enum ItemType {
TEXT("text"),
IMAGE("configure_photo");
private final String value;
ItemType(final String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
public static abstract class BroadcastOptions {
private final ItemType itemType;
public BroadcastOptions(final ItemType itemType) {
this.itemType = itemType;
}
public ItemType getItemType() {
return itemType;
}
abstract Map<String, String> getFormMap();
}
public static class TextBroadcastOptions extends BroadcastOptions {
private final String text;
public TextBroadcastOptions(String text) throws UnsupportedEncodingException {
super(ItemType.TEXT);
this.text = URLEncoder.encode(text, "UTF-8")
.replaceAll("\\+", "%20").replaceAll("%21", "!").replaceAll("%27", "'")
.replaceAll("%28", "(").replaceAll("%29", ")").replaceAll("%7E", "~");
}
@Override
Map<String, String> getFormMap() {
return Collections.singletonMap("text", text);
}
}
public static class ImageBroadcastOptions extends BroadcastOptions {
final boolean allowFullAspectRatio;
final String uploadId;
public ImageBroadcastOptions(final boolean allowFullAspectRatio, final String uploadId) {
super(ItemType.IMAGE);
this.allowFullAspectRatio = allowFullAspectRatio;
this.uploadId = uploadId;
}
@Override
Map<String, String> getFormMap() {
final Map<String, String> form = new HashMap<>();
form.put("allow_full_aspect_ratio", String.valueOf(allowFullAspectRatio));
form.put("upload_id", uploadId);
return form;
}
}
public static class DirectThreadBroadcastResponse {
private int responseCode;
private JSONObject response;
public DirectThreadBroadcastResponse(int responseCode, JSONObject response) {
this.responseCode = responseCode;
this.response = response;
}
public int getResponseCode() {
return responseCode;
}
public JSONObject getResponse() {
return response;
}
}
}

View File

@ -0,0 +1,76 @@
package awais.instagrabber.models;
import java.io.InputStream;
public class ImageUploadOptions {
private InputStream inputStream;
private long contentLength;
private boolean isSidecar;
private String waterfallId;
public static class Builder {
private InputStream inputStream;
private long contentLength;
private boolean isSidecar;
private String waterfallId;
public Builder(final InputStream inputStream, final long contentLength) {
this.inputStream = inputStream;
this.contentLength = contentLength;
}
public Builder setInputStream(final InputStream inputStream) {
this.inputStream = inputStream;
return this;
}
public Builder setContentLength(final long contentLength) {
this.contentLength = contentLength;
return this;
}
public Builder setIsSidecar(final boolean isSidecar) {
this.isSidecar = isSidecar;
return this;
}
public Builder setWaterfallId(final String waterfallId) {
this.waterfallId = waterfallId;
return this;
}
public ImageUploadOptions build() {
return new ImageUploadOptions(inputStream, contentLength, isSidecar, waterfallId);
}
}
public static Builder builder(final InputStream inputStream, final long contentLength) {
return new Builder(inputStream, contentLength);
}
private ImageUploadOptions(final InputStream inputStream,
final long contentLength,
final boolean isSidecar,
final String waterfallId) {
this.inputStream = inputStream;
this.contentLength = contentLength;
this.isSidecar = isSidecar;
this.waterfallId = waterfallId;
}
public InputStream getInputStream() {
return inputStream;
}
public long getContentLength() {
return contentLength;
}
public boolean isSidecar() {
return isSidecar;
}
public String getWaterfallId() {
return waterfallId;
}
}

View File

@ -57,6 +57,7 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;
import javax.crypto.Mac;
@ -101,6 +102,7 @@ import static awais.instagrabber.utils.Constants.FOLDER_PATH;
import static awais.instagrabber.utils.Constants.FOLDER_SAVE_TO;
public final class Utils {
private static final String TAG = "Utils";
public static LogCollector logCollector;
public static SettingsHelper settingsHelper;
public static DataBox dataBox;
@ -1179,19 +1181,19 @@ public final class Utils {
public static String sign(final String message) {
try {
Mac hasher = Mac.getInstance("HmacSHA256");
final Mac hasher = Mac.getInstance("HmacSHA256");
hasher.init(new SecretKeySpec(Constants.SIGNATURE_KEY.getBytes(), "HmacSHA256"));
byte[] hash = hasher.doFinal(message.getBytes());
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < hash.length; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
final StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
final String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return "ig_sig_key_version="+Constants.SIGNATURE_VERSION+"&signed_body=" + hexString.toString() + "." + message;
}
catch (Throwable e) {
Log.e("austin_debug", "sign: ", e);
catch (Exception e) {
Log.e(TAG, "Error signing", e);
return null;
}
}
@ -1360,4 +1362,13 @@ public final class Utils {
return null;
}
public static void setConnectionHeaders(final HttpURLConnection connection, final Map<String, String> headers) {
if (connection == null || headers == null || headers.isEmpty()) {
return;
}
for (Map.Entry<String, String> header : headers.entrySet()) {
connection.setRequestProperty(header.getKey(), header.getValue());
}
}
}