Hi all,
I am trying to extend the Twitter API client to be able to post updates with media IDs (using the statuses/update endpoint) to attach images to a tweet (note: I am aware of the Tweet Composer, but for several reasons it’s not suitable for me, so I would prefer to be able to extend the Statuses Service update method to include media IDs, as we know that it’s possible with the REST API). However, when I try to post with my method, I get 400 bad request error, even when I post without any media ID (so basically the same way as in the built-in update method, I actually copied the Retrofit fields from that). I don’t know what’s going wrong, as my request seems to be correctly formed.
Here are some code snippets:
Custom API client:
public interface StatusWithMediaService {
@FormUrlEncoded
@POST(value="/1.1/statuses/update.json")
void update(@Field(value="status")
java.lang.String status,
@Field(value="in_reply_to_status_id")
java.lang.Long inReplyToStatusId,
@Field(value="possibly_sensitive")
java.lang.Boolean possiblySensitive,
@Field(value="lat")
java.lang.Double latitude,
@Field(value="long")
java.lang.Double longitude,
@Field(value="place_id")
java.lang.String placeId,
@Field(value="display_cooridnates")
java.lang.Boolean displayCoordinates,
@Field(value="trim_user")
java.lang.Boolean trimUser,
@Field(value ="media_ids")
String mediaIds,
com.twitter.sdk.android.core.Callback<Tweet> cb);
}
public class MyTwitterApiClient extends TwitterApiClient {
public MyTwitterApiClient(TwitterSession session)
{
super(session);
}
public StatusWithMediaService getStatusWithMediaService() {
return getService(StatusWithMediaService.class);
}
}
The part where I’m trying to tweet (note that I am using the ID of a recently manually uploaded picture as also the media uploading does not work for me for some reason, but let’s stick to one issue at a time
)
final TwitterSession session = Twitter.getSessionManager().getActiveSession();
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://api.twitter.com")
.setLogLevel(RestAdapter.LogLevel.FULL).setLog(new AndroidLog("testapp retrofit"))
.setRequestInterceptor(new RequestInterceptor() {
@Override
public void intercept(RequestFacade request) {
request.addHeader("Authorization", createSignature(session));
}
})
.build();
StatusWithMediaService mediaService = restAdapter.create(StatusWithMediaService.class);
mediaService.update("@tkl_testi " + eventtype + ": \n" + comment.getText().toString(), null, false, mypos.latitude, mypos.longitude, null, true, false, "628825606512316416", new Callback<Tweet>() {
@Override
public void success(Result<Tweet> tweetResult) {
Toast.makeText(getActivity(), "Raportin lähettäminen onnistui.", Toast.LENGTH_SHORT).show();
}
@Override
public void failure(TwitterException e) {
Log.e("testapp", e.getMessage());
}
});
Creating the header:
public static String sha1(String s, String keyString) throws
UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
SecretKeySpec key = new SecretKeySpec((keyString).getBytes("UTF-8"), "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(key);
byte[] bytes = mac.doFinal(s.getBytes("UTF-8"));
return Base64.encodeToString(bytes, Base64.URL_SAFE);
}
public String createSignature(TwitterSession session)
{
byte[] b = new byte[32];
new Random().nextBytes(b);
String randomBytes = null;
try
{
randomBytes = URLEncoder.encode(String.valueOf(b), "UTF-8");
}
catch (UnsupportedEncodingException e)
{
Log.e("encoding error", e.getMessage());
}
long currentTimeInMillis = System.currentTimeMillis();
TwitterAuthToken authToken = session.getAuthToken();
String token = session.getAuthToken().token;
String signature = String.format("oauth_consumer_key=%s&oauth_nonce=%s&oauth_signature_method=HMAC-SHA1&oauth_timestamp=%s&oauth_token=%s&oauth_version=1.0", MainActivity.TWITTER_KEY, randomBytes, currentTimeInMillis, token);
String finalSignature = null;
try
{
finalSignature = sha1(signature, MainActivity.TWITTER_SECRET + "&" +authToken.secret);
}
catch (UnsupportedEncodingException e)
{
Log.e("encoding error", e.getMessage());
}
catch (NoSuchAlgorithmException e)
{
Log.e("algorithm error", e.getMessage());
}
catch (InvalidKeyException e)
{
Log.e("key error", e.getMessage());
}
String header = String.format("OAuth oauth_consumer_key=\"%s\", oauth_nonce=\"%s\", oauth_signature=\"%s\", " +
"oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"%s\", oauth_token=\"%s\", oauth_version=\"1.0\")", MainActivity.TWITTER_KEY, randomBytes, finalSignature, currentTimeInMillis, token);
return header;
}
When I run the app and log the request and response, I see this:
08-05 11:13:04.193 22497-23558/com.example.test.testapp D/testapp retrofit﹕ ---> HTTP POST https://api.twitter.com/1.1/statuses/update.json
08-05 11:13:04.193 22497-23558/com.example.test.testapp D/testapp retrofit﹕ Authorization: OAuth oauth_consumer_key="XXXXX", oauth_nonce="XXXXX", oauth_signature="XXXXX", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1438762384184", oauth_token="XXXXX", oauth_version="1.0")
08-05 11:13:04.193 22497-23558/com.example.test.testapp D/testapp retrofit﹕ Content-Type: application/x-www-form-urlencoded; charset=UTF-8
08-05 11:13:04.193 22497-23558/com.example.test.testapp D/testapp retrofit﹕ Content-Length: 135
08-05 11:13:04.194 22497-23558/com.example.test.testapp D/testapp retrofit﹕ status=%40tkl_testi+Onnettomuus%3A+%0A&possibly_sensitive=false&lat=61.4892668&long=23.7791515&display_cooridnates=true&trim_user=false
08-05 11:13:04.194 22497-23558/com.example.test.testapp D/testapp retrofit﹕ ---> END HTTP (135-byte body)
08-05 11:13:05.097 22497-23558/com.example.test.testapp D/testapp retrofit﹕ <--- HTTP 400 https://api.twitter.com/1.1/statuses/update.json (902ms)
08-05 11:13:05.097 22497-23558/com.example.test.testapp D/testapp retrofit﹕ : HTTP/1.0 400 Bad Request
08-05 11:13:05.097 22497-23558/com.example.test.testapp D/testapp retrofit﹕ content-length: 0
08-05 11:13:05.097 22497-23558/com.example.test.testapp D/testapp retrofit﹕ date: Wed, 05 Aug 2015 08:13:05 GMT
08-05 11:13:05.097 22497-23558/com.example.test.testapp D/testapp retrofit﹕ server: tsa_b
08-05 11:13:05.097 22497-23558/com.example.test.testapp D/testapp retrofit﹕ X-Android-Received-Millis: 1438762385096
08-05 11:13:05.097 22497-23558/com.example.test.testapp D/testapp retrofit﹕ X-Android-Response-Source: NETWORK 400
08-05 11:13:05.097 22497-23558/com.example.test.testapp D/testapp retrofit﹕ X-Android-Sent-Millis: 1438762384860
08-05 11:13:05.099 22497-23558/com.example.test.testapp D/testapp retrofit﹕ x-connection-hash: 9c60ff82dad92388344567a09da22e65
08-05 11:13:05.099 22497-23558/com.example.test.testapp D/testapp retrofit﹕ <--- END HTTP (0-byte body)
Does anyone have any idea what is wrong? I am completely lost right now 
UPDATE:
Instead of using the REST adapter, I tried instantiating my custom service through my custom client. So the update part now looks like this:
MyTwitterApiClient myclient = new MyTwitterApiClient(Twitter.getSessionManager().getActiveSession());
StatusWithMediaService mediaService = myclient.getStatusWithMediaService();
mediaService.update("@tkl_testi " + eventtype + ": \n" + comment.getText().toString(), null, false, mypos.latitude, mypos.longitude, null, true, false, null, new Callback<Tweet>() {
@Override
public void success(Result<Tweet> tweetResult) {
Toast.makeText(getActivity(), "Raportin lähettäminen onnistui.", Toast.LENGTH_SHORT).show();
}
@Override
public void failure(TwitterException e) {
TwitterApiException apiException = (TwitterApiException) e;
Log.e("testapp", apiException.getErrorMessage());
Log.e("testapp", apiException.getMessage());
}
});
With this now I can successfully post without media IDs. When I try to post with a media ID, however, I get the following error message:
The validation of media ids failed.
I can post the same status using twurl without any problem, so I kind of doubt the image size would be the problem (42 KB, 800x533).
By the way, I uploaded the image using twurl and that’s what I get back (just in case it helps you find anything problematic):
{"media_id":629232743633842180,"media_id_string":"629232743633842180","size":41860,"expires_after_secs":86400,"image":{"image_type":"image\/jpeg","w":800,"h":533}}
I always make a new upload before attempting to tweet, so duplicate ID should not be a problem.
Do you have any idea what the problem could be?