2010年1月9日土曜日

Android用のTwitterクライアントを作ってみる

もう猫も杓子もTwitterな昨今いかがお過ごしでしょうか。

というわけですごい単純なTwitterClientを作成したいと思います。
ちなみにこちらの「きしだのはてな」さんのサンプルをす~っごく参考にしました。
http://d.hatena.ne.jp/nowokay/20091030
また、
「ところで、HTMLの最初のほうに変更がないと、loadDataしても画面が更新されないのはなんでなんだぜ?」
と発言されていますが、本当になぜなんだぜ?

ちなみに

  • キャッシュかな?と思い、WebView.clearCache でキャシュをクリアするも意味なし
  • 描画に更新かければいいのかと考え、WebView.postInvalidate()を呼ぶも意味なし
  • WebView.loadData を2回呼ぶと更新される・・・。←とりあえず暫定で採用
  • きしだのはてなさんのサンプルのように、上のほうの文字列を変更する
という状況でした。
わけわかんね。


layout/main.xml



 
  
   
   
  
 

 
 
 



ボタンを2つ並べて表示するために、TableLayoutを使用。
また、2つボタンのサイズを統一するためにlayout_weightの値をそろえる。


AndroidManifest.xml ネットに繋ぐ前提となるので
・<uses-permission android:name="android.permission.INTERNET"></uses-permission>
を追加

Main.java
package com.omokageru.ak.twitter;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.MultiAutoCompleteTextView.Tokenizer;

public class Main extends Activity {
 private Button updateButton;
 private Button reloadButton;
 private EditText updateEdit;
 private WebView webView;

 private DefaultHttpClient httpClient;

 /** Called when the activity is first created. */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  updateButton = (Button) findViewById(R.id.UpdateButton);

  // ボタン押下時の処理
  updateButton.setOnClickListener(new OnClickListener() {

   public void onClick(View v) {
    // TODO 自動生成されたメソッド・スタブ

    // 通信を伴うので別スレッドで
    new Thread(){
     @Override
     public void run() {
      // TODO 自動生成されたメソッド・スタブ
      updateButton.setClickable(false);
      reloadButton.setClickable(false);
      String str = updateEdit.getEditableText().toString();
      UpdateTwitter(str);
      reloadButton.setClickable(true);
      updateButton.setClickable(true);
     }
    }.start();
   }
  });

  reloadButton = (Button) findViewById(R.id.ReloadButton);

  reloadButton.setOnClickListener(new OnClickListener() {

   public void onClick(View view) {
    // TODO 自動生成されたメソッド・スタブ

    // 通信を伴うので別スレッドで
    new Thread(){
     @Override
     public void run() {
      updateButton.setClickable(false);
      reloadButton.setClickable(false);
      GetAndWriteHomeTimeLine();
      reloadButton.setClickable(true);
      updateButton.setClickable(true);
     }
    }.start();
   }
  });

  updateEdit = (EditText) findViewById(R.id.UpdateEdit);
  webView = (WebView) findViewById(R.id.twitter_web);

  httpClient = new DefaultHttpClient();
  Credentials cred = new UsernamePasswordCredentials( "Twitter User ID", "Password");//userとpassを
  httpClient.getCredentialsProvider().setCredentials( new AuthScope("twitter.com", 80), cred);

  GetAndWriteHomeTimeLine();
 }

 /**
  * Twitterへつぶやく
  * @param str
  * 投稿内容
  */
 private synchronized void UpdateTwitter( String str){
  try {
   // パラメータstatus=hoge でhogeを発言
   // status は必須パラメータ
   HttpPost post = new HttpPost( "http://twitter.com/statuses/update.json?status=" + str);
   // 一応これで発言するはず
   HttpResponse response = httpClient.execute(post);

   // 失敗時の処理
   if(response.getStatusLine().getStatusCode() != HttpStatus.SC_OK){
    return;
   }

   // ここからの描画はなぜか利かない・・・
   GetAndWriteHomeTimeLine();

  } catch (ClientProtocolException e) {
   // TODO 自動生成された catch ブロック
  } catch (IOException e) {
   // TODO 自動生成された catch ブロック
  }

 }

 /**
  * friend_timelineは将来廃止予定らしいので home_timeline を取得し表示。<br />
  * http://d.hatena.ne.jp/nowokay/20091030 を参考にしました。
  */
 private synchronized void GetAndWriteHomeTimeLine(){
  try {
   //   webView.clearCache(false);

   HttpGet get = new HttpGet( "http://twitter.com/statuses/home_timeline.json");
   // これで取得するはず
   HttpResponse response = httpClient.execute(get);

   // 失敗時の処理
   if(response.getStatusLine().getStatusCode() != HttpStatus.SC_OK){
    return;
   }

   //解析と出力
   //サーバーからのデータを取得
   InputStream is = response.getEntity().getContent();
   InputStreamReader isr = new InputStreamReader(is);
   StringWriter strin = new StringWriter();
   BufferedReader buf = new BufferedReader(isr);
   for(String line; (line = buf.readLine()) != null;){
    strin.write(line);
   }
   buf.close();
   isr.close();
   is.close();


   //出力準備
   StringWriter strout = new StringWriter();
   PrintWriter out = new PrintWriter(strout);
   out.println("<html><head><title>Twitter testapites</title></head>");
   out.println("<body>");
   //JSONデータからタイムラインを取得してHTMLを生成
   JSONTokener token = new JSONTokener(strin.toString());
   JSONArray arr = new JSONArray(token);
   for(int i = 0; i < arr.length(); i++){
    JSONObject obj = arr.getJSONObject(i);
    JSONObject user = obj.getJSONObject("user");
    out.println("<div style='border-bottom: 1px solid #888888'>");
    out.println("<img style='width:48px; height:48px;' src='" + user.get("profile_image_url") + "' align='left'>");
    out.println("<bold><font color='#6666ff'>" + user.getString("screen_name") + "</font></bold><br />" );
    out.println(obj.get("text") + "<br clear='all' />");
    // ここで出力を確認しているが、発言内容が含まれているにもかかわらず、
    // loadData一回ではなぜか更新されない。
    Log.i("t_test", "" + obj.get("text"));
    out.println("</div>");
   }
   out.println("</body></html>");

   strin.close();
   out.close();

   // 二回呼び出すと反映される・・・
   // invalidate() の意味は??
   webView.loadData(strout.toString(), "text/html", "utf-8");
   webView.loadData(strout.toString(), "text/html", "utf-8");


   strout.close();

   // 描画更新
   // 本体スレッドからの呼び出しではない場合に備えpostInvalidateを使用。
   //   webView.postInvalidate();
   //   Log.i("t_test", "Invalidate");


  } catch (ClientProtocolException e) {
   // TODO 自動生成された catch ブロック
  } catch (JSONException e) {
   // TODO 自動生成された catch ブロック
  } catch (IOException e) {
   // TODO 自動生成された catch ブロック
  }

 }
}

一応発言と描画には synchronized をつけているので、重複して呼び出して落ちるってことはないと思いますが、念のため作業中はボタンを無効化しています。
失敗時の処理はめんどいので放置。
こんなソースを参考にする場合は、その辺はきっちりやることをお勧めします。

ちなみにhttp://twitter.com/statuses/home_timeline.json をたたくとこんな感じで返ってきます。
twitter_json_sample.txt


動かすとこんなかんじ。
起動時に取得に行きます。


発言すると、発言がきちんと反映

とりあえずメデタシメデタシ

0 件のコメント:

コメントを投稿