Upload de imagens para a API do Tumblr a partir do Android

Supôs-se usando oAPI do Tumblr fazer upload de imagens seria fácil. Não é. (EDITAR É agora, vejaEditar 2 no final desta entrada)

Meu aplicativo deve carregar uma imagem paratumblr. Eu preferiria fazer isso de um serviço, mas por enquanto eu uso uma atividade que se fecha assim que o upload é feito. EmOnCreate() o usuário é autenticado:

consumer = new CommonsHttpOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);

// It uses this signature by default
// consumer.setMessageSigner(new HmacSha1MessageSigner());

provider = new CommonsHttpOAuthProvider(REQUEST_TOKEN_URL,ACCESS_TOKEN_URL,AUTH_URL);

String authUrl;
try 
{
authUrl = provider.retrieveRequestToken(consumer, CALLBACK_URL);
Log.d(TAG, "Auth url:" + authUrl);

startActivity(new Intent("android.intent.action.VIEW", Uri.parse(authUrl)));

} 

Isso abre uma atividade do navegador onde o usuário pode adicionar nome de usuário e senha e, em seguida, o aplicativo retorna à atividade (é também por isso que preciso usar uma atividade, não sei como fazer isso de um serviço)

Retornando do navegador, os dados são extraídos:

Uri uri = context.getIntent().getData();
if (uri != null && uri.toString().startsWith(CALLBACK_URL)) 
{  
    Log.d(TAG, "uri!=null");
    String verifier = uri.getQueryParameter("oauth_verifier");  
    Log.d(TAG, "verifier"+verifier);
    try 
    {
        provider.setOAuth10a(true);
        provider.retrieveAccessToken(consumer, verifier);
        Log.d(TAG, "try");
    } 
    catch (Exception e) 
    {
        Log.e(TAG, e.toString());
        e.printStackTrace();
    } 
OAUTH_TOKEN = consumer.getToken();
OAUTH_SECRET = consumer.getTokenSecret();

A maioria desses dois trechos eu tenhodaqui e eles funcionam bem.

Com esses tokens agora posso tentar colocar dados no tumblr. Quando tento adicionar o texto isso funciona bem usando este método:

private void createText()
{
    if(!OAUTH_TOKEN.equals(""))
    {

        HttpContext context = new BasicHttpContext();
        HttpPost request = new HttpPost("http://api.tumblr.com/v2/blog/" + blogname + ".tumblr.com/post");

        List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
        nameValuePairs.add(new BasicNameValuePair("type", "text")); 
        nameValuePairs.add(new BasicNameValuePair("body", "this is just a test"));  

        try 
        {
        request.setEntity(new UrlEncodedFormEntity(nameValuePairs));
        } 
        catch (UnsupportedEncodingException e1) 
        {
            Log.e(TAG, e1.toString());
            e1.printStackTrace();
        }

        if (consumer == null)
        {
            consumer = new CommonsHttpOAuthConsumer(OAuthConstants.TUMBR_CONSUMERKEY, OAuthConstants.TUMBR_SECRETKEY);
        }
        if (OAUTH_TOKEN == null || OAUTH_SECRET == null)
        {
            Log.e(TAG, "Not logged in error");
        }
        consumer.setTokenWithSecret(OAUTH_TOKEN, OAUTH_SECRET);

        try 
        {
            consumer.sign(request);
        } 
        catch (OAuthMessageSignerException e) 
        {

        } 
        catch (OAuthExpectationFailedException e) 
        {
        } 
        catch (OAuthCommunicationException e) 
        {
        }
        HttpClient client = new DefaultHttpClient();
        //finally execute this request
        try 
        {
            HttpResponse response = client.execute(request, context);
            HttpEntity responseEntity = response.getEntity(); 
            if (responseEntity != null) 
            { 
                Log.d(TAG, "responseEntety!=null");
                try 
                {
                    Log.d(TAG, EntityUtils.toString(responseEntity));
                } 
                catch (ParseException e) 
                {
                    e.printStackTrace();
                    Log.e(TAG, e.toString());
                } 
                catch (IOException e) 
                {
                    e.printStackTrace();
                    Log.e(TAG, e.toString());
                } // gives me {"meta":{"status":401,"msg":"Not Authorized"},"response":[]} when I try to upload a photo
            }
            else
            {
                Log.d(TAG, "responseEntety==null");
            }
        } 
        catch (ClientProtocolException e) 
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
        catch (IOException e) 
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    PostToTumblr.this.finish();
}

Como você pode ver aquihttp://www.tumblr.com/blog/snapnowandroid (pelo menos a partir deste momento) o texto "este é apenas um teste" é publicado.

No entanto, quando tento postar imagens, fica estranho. Agora eu verifiquei e, aparentemente, este é um problema bem conhecido com a API do Tumblr, que tem sido excessivamente discutidaAqui e alguns resolveram em outras linguagens de programação (por exemploAqui), mas não consegui repetir esses sucessos.

O método (na sua totalidade abaixo) tem exatamente a mesma estrutura para o método acima (que funciona), os nameValuePairs são apenas diferentes

O método recebe uma variável de bitmap chamada foto:

    private void uploadToTumblr(Bitmap photo)

Este bitmap é convertido em uma matriz:

ByteArrayOutputStream stream = new ByteArrayOutputStream();
photo.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] bytes = stream.toByteArray();

Os nameValuePairs são preenchidos da seguinte forma:

nameValuePairs.add(new BasicNameValuePair(URLEncoder.encode("type", enc), URLEncoder.encode("photo", enc)));
nameValuePairs.add(new BasicNameValuePair(URLEncoder.encode("caption", enc), URLEncoder.encode(text, enc))); 
nameValuePairs.add(new BasicNameValuePair("data", Base64.encodeToString(bytes, Base64.URL_SAFE))); 

O resultado é um{"meta":{"status":400,"msg":"Bad Request"},"response":{"errors":["Error uploading photo."]}} do tumblr api.

Eu tenho tentativas codificando a imagem de forma diferente, como descrito emEste artigo mas sem alterações.

//http://www.coderanch.com/t/526487/java/java/Java-Byte-Hex-String
final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
char[] hexChars = new char[bytes.length * 3];
int v;
for ( int j = 0; j < bytes.length; j++ ) 
{
    v = bytes[j] & 0xFF;
    hexChars[j * 3] = '%';
    hexChars[j * 3 + 1] = hexArray[v >>> 4];
    hexChars[j * 3 + 2] = hexArray[v & 0x0F];
}
String s = new String(hexChars);                
s = URLEncoder.encode(s, enc);
nameValuePairs.add(new BasicNameValuePair(URLEncoder.encode("data", enc), s)); 

Aqui todo o método (sem a codificação hexadecimal):

private void uploadToTumblr(Bitmap photo)
{
    if(!OAUTH_TOKEN.equals(""))
    {

        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        photo.compress(Bitmap.CompressFormat.PNG, 100, stream);
        byte[] bytes = stream.toByteArray();

        String text ="SNAP";


        HttpContext context = new BasicHttpContext();
        HttpPost request = new HttpPost("http://api.tumblr.com/v2/blog/" + blogname + ".tumblr.com/post");

        List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2); 
        String enc = "UTF-8"; 

        try 
        {
            nameValuePairs.add(new BasicNameValuePair(URLEncoder.encode("type", enc), URLEncoder.encode("photo", enc)));
            nameValuePairs.add(new BasicNameValuePair(URLEncoder.encode("caption", enc), URLEncoder.encode(text, enc))); 
            nameValuePairs.add(new BasicNameValuePair("data", Base64.encodeToString(bytes, Base64.URL_SAFE))); 
        } 
        catch (UnsupportedEncodingException e2) 
        {
            Log.e(TAG, e2.toString());
            e2.printStackTrace();
        } 
        try 
        {
            request.setEntity(new UrlEncodedFormEntity(nameValuePairs));
        } 
        catch (UnsupportedEncodingException e1) 
        {
            Log.e(TAG, e1.toString());
            e1.printStackTrace();
        }

        if (consumer == null)
        {
            consumer = new CommonsHttpOAuthConsumer(OAuthConstants.TUMBR_CONSUMERKEY, OAuthConstants.TUMBR_SECRETKEY);
        }
        if (OAUTH_TOKEN == null || OAUTH_SECRET == null)
        {
            //throw new LoginErrorException(LoginErrorException.NOT_LOGGED_IN);
            Log.e(TAG, "Not logged in error");
        }
        consumer.setTokenWithSecret(OAUTH_TOKEN, OAUTH_SECRET);

            try 
            {
                consumer.sign(request);
            } 
            catch (OAuthMessageSignerException e) 
            {

            } 
            catch (OAuthExpectationFailedException e) 
            {

            } 
            catch (OAuthCommunicationException e) 
            {
            }

            HttpClient client = new DefaultHttpClient();

            //finally execute this request
            try 
            {
                HttpResponse response = client.execute(request, context);
                HttpEntity responseEntity = response.getEntity(); 
                if (responseEntity != null) 
                { 
                    Log.d(TAG, "responseEntety!=null");
                    try 
                    {
                        Log.d(TAG, EntityUtils.toString(responseEntity));
                    } 
                    catch (ParseException e) 
                    {
                        e.printStackTrace();
                        Log.e(TAG, e.toString());
                    } 
                    catch (IOException e) 
                    {
                        e.printStackTrace();
                        Log.e(TAG, e.toString());
                    } 
                }
                else
                {
                    Log.d(TAG, "responseEntety==null");
                }
            } 
            catch (ClientProtocolException e) 
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } 
            catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }


    }

    else
    {
        Log.d(TAG, "upload imposble... Toklen not set");
    }
    PostToTumblr.this.finish();
}

Agora, embora haja várias coisas com as quais estou insatisfeito (por exemplo, isso é feito usando uma atividade em vez de um serviço), o grande problema aqui é claramente o problema de carregar imagens. Eu não sou de modo algum o primeiro a ter esse problema, então alguém conseguiu fazer isso em java?

Editar 1

Não fez nenhum progresso com o problema em questão, mas criou uma solução alternativa que pode ser boa para pessoas que têm o mesmo problema. Ofertas do Tumblrpostagem via correio e você pode programar android para enviar e-mails em segundo plano comomostrado aqui. Isso funciona muito bem, mas você precisa pedir aos usuários que forneçam os dados da sua conta de e-mail e o endereço de e-mail do Tumblr para postar.

Editar 2

Anos passaram e usar e-mail não é mais a maneira mais fácil de fazer isso. Comjumblr Finalmente, há uma boa API para Java que funcionará no android. OAuth-Authentication não é divertido (nunca é), mas uma vez que você supera isso, é fantástico.

Agora, tecnicamente a questão de como fazer a autenticação não pertence aqui, mas é a minha pergunta excessivamente longa, então eu vou apenas colar algum código aqui e se não for interessante para você, pule-o.

Isso usa um jarro chamadojumblr-0.0.10-jar-with-dependencies.jar

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.util.Log;

import com.tumblr.jumblr.JumblrClient;
import com.tumblr.jumblr.request.RequestBuilder;
import com.tumblr.jumblr.types.Blog;
import com.tumblr.jumblr.types.User;

import org.scribe.builder.ServiceBuilder;
import org.scribe.builder.api.TumblrApi;
import org.scribe.model.Token;
import org.scribe.model.Verifier;
import org.scribe.oauth.OAuthService;

import java.io.File;


public class Tumblr
{
private static final String PROTECTED_RESOURCE_URL = "http://api.tumblr.com/v2/user/info";

static OAuthService service;
static Token requestToken=null;


public static void share(final Activity ctx, File file)
{
    Thread tt = new Thread(new Runnable()
    {
        @Override
        public void run()
        {
            JumblrClient client = new JumblrClient(Tumblr_Constants.CONSUMER_KEY, Tumblr_Constants.CONSUMER_SECRET);
            RequestBuilder requestBuilder = client.getRequestBuilder();
            requestBuilder.setConsumer(Tumblr_Constants.CONSUMER_KEY, Tumblr_Constants.CONSUMER_SECRET);
            SharedPreferences settings = ctx.getSharedPreferences("TumblrData", 0);
            String oauthToken=settings.getString("OauthToken", "");
            String oauthTokenSecret=settings.getString("OauthSecret", "");
            if(oauthToken.equals("") || oauthTokenSecret.equals(""))
            {
                authenticate(ctx);
                while(WebViewFragment.verifier.equals(""))
                {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                String v = WebViewFragment.verifier;
                Token accessToken = authenticatefurther(v);
                SharedPreferences.Editor edit = settings.edit();
                edit.putString("OauthToken", accessToken.getToken());
                edit.putString("OauthSecret", accessToken.getSecret());
                edit.commit();
                oauthToken=settings.getString("OauthToken", "");
                oauthTokenSecret=settings.getString("OauthSecret", "");
            }
            if(!oauthToken.equals("") && !oauthTokenSecret.equals(""))
            {
                client.setToken(oauthToken, oauthTokenSecret);

                User user = client.user();
                System.out.println(user.getName());

                for (Blog blog : user.getBlogs()) {
                    Log.d("TUMBLR", blog.getTitle());
                }
            }
        }

    });
    tt.start();

}

private static void authenticate(Context ctx) {
    service = new ServiceBuilder()
            .provider( TumblrApi.class )
            .apiKey(Tumblr_Constants.CONSUMER_KEY)
            .apiSecret(Tumblr_Constants.CONSUMER_SECRET)
            .callback("snapnao://snapnao.de/ok") // OOB forbidden. We need an url and the better is on the tumblr website !
            .build();


    Log.d("TUMBLR", "=== Tumblr's OAuth Workflow ===" );
    System.out.println();

    // Obtain the Request Token
    Log.d("TUMBLR", "Fetching the Request Token...");
    requestToken = service.getRequestToken();
    Log.d("TUMBLR", "Got the Request Token!");
    Log.d("TUMBLR", "");

    Log.d("TUMBLR", "Now go and authorize Scribe here:" );
    Log.d("TUMBLR", service.getAuthorizationUrl( requestToken ) );

    String url = service.getAuthorizationUrl(requestToken);


    Intent i = new Intent(ctx, WebViewFragment.class);
    i.putExtra("url", url);
    ctx.startActivity(i);


}

private static Token authenticatefurther(String v)
{
    Token accessToken = null;
    Log.d("TUMBLR", "And paste the verifier here");
    Log.d("TUMBLR", ">>");

    Verifier verifier = new Verifier( v);
    Log.d("TUMBLR", "");

    // Trade the Request Token and Verfier for the Access Token
    Log.d("TUMBLR", "Trading the Request Token for an Access Token...");
    accessToken = service.getAccessToken( requestToken ,
            verifier );
    Log.d("TUMBLR", "Got the Access Token!");
    Log.d("TUMBLR", "(if your curious it looks like this: " + accessToken + " )");

    Log.d("TUMBLR", "");

    return accessToken;
}


}

O WebViewFragement é assim:

import android.app.Activity;
import android.graphics.Bitmap;
import android.net.http.SslError;
import android.os.Bundle;
import android.util.Log;
import android.webkit.SslErrorHandler;
import android.webkit.WebView;
import android.webkit.WebViewClient;


public class WebViewFragment extends Activity 
{
public static String verifier="";
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.webviewfragment);

    String url = getIntent().getStringExtra("url");
    Log.d("TUMBLR", "webview-> "+url);
    WebView view = (WebView) findViewById(R.id.webView);
    view.setWebViewClient(
            new SSLTolerentWebViewClient()
    );
    view.getSettings().setJavaScriptEnabled(true);
    view.loadUrl(url);
}

// SSL Error Tolerant Web View Client
private class SSLTolerentWebViewClient extends WebViewClient {

    @Override
    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
        handler.proceed(); // Ignore SSL certificate errors
    }

    @Override
    public void onPageStarted(WebView view, String url, Bitmap favicon) {
        super.onPageStarted(view, url, favicon);
        Log.d("TUMBLR", "+++++"+url);
        if(url.contains("oauth_verifier="))
        {
            String[] x = url.split("oauth_verifier=");
            verifier=x[1].replace("#_=_", "");
            WebViewFragment.this.finish();
        }
    }
}
}

questionAnswers(5)

yourAnswerToTheQuestion