Android : Twitter 検索 + ListView
|
|
日時: 2013/10/13 20:15
名前: lightbox
|
TwitterSearch.java
拡張子:
package com.example.listviewobjectjson;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import android.os.AsyncTask;
import android.util.Base64;
public class TwitterSearch {
// AsyncTask のインラインで参照する為の final
private final String _consumer_key;
private final String _consumer_secret;
private final String _token;
private final String _secret;
// API
private final String _tweet_api = "https://api.twitter.com/1.1/search/tweets.json";
// **************************************************************
// コンストラクタ
// **************************************************************
public TwitterSearch(String _consumer_key, String _consumer_secret,
String _token, String _secret) {
this._consumer_key = _consumer_key;
this._consumer_secret = _consumer_secret;
this._token = _token;
this._secret = _secret;
}
// **************************************************************
// AsyncTask の onPostExecute から外部イベントとして呼び出す為のインターフェイス
// **************************************************************
public interface Searched {
public void onSearchResult(String result);
}
// **************************************************************
// Twitter 投稿
// **************************************************************
public void Search(String query, int count, final Searched OnSearchResult) {
new AsyncTask<String, Void, String>() {
// **************************************************************
// 非同期処理
// **************************************************************
@Override
protected String doInBackground(String... params) {
ArrayList<String> lst = new ArrayList<String>();
String nonce = getNonce();
String timeStamp = getTimeStamp();
String result_string = "";
try {
// *********************************
// 投稿に必要なデータ (1)
// *********************************
lst.add("count=" + params[1]);
lst.add("oauth_consumer_key=" + _consumer_key);
lst.add("oauth_nonce=" + nonce);
lst.add("oauth_signature_method=" + "HMAC-SHA1");
lst.add("oauth_timestamp=" + timeStamp);
lst.add("oauth_token=" + _token);
lst.add("oauth_version=1.0");
lst.add("q=" + rfc3986(URLEncoder.encode(params[0], "utf-8")));
Collections.sort(lst);
String work = "";
for(int i = 0; i < lst.size() ; i++ ){
if ( i != 0 ) {
work += "&";
}
work += lst.get(i);
}
// *********************************
// 投稿に必要なデータ (2)
// *********************************
String work2 = "GET" + "&";
// API のエントリポイント
work2 += rfc3986(URLEncoder.encode(_tweet_api,"utf-8")) + "&";
// 投稿に必要なデータ (1)
work2 += rfc3986(URLEncoder.encode(work,"utf-8"));
// *********************************
// 投稿に必要なデータ (3)
// *********************************
String oauth_signature = getSignature(work2);
// *********************************
// 投稿に必要なデータ (4) / ヘッダ
// *********************************
String data = "oauth_consumer_key=" + dD(_consumer_key) +
",oauth_nonce=" + dD(nonce) +
",oauth_signature=" + dD(rfc3986(URLEncoder.encode(oauth_signature, "utf-8"))) +
",oauth_signature_method=" + dD("HMAC-SHA1") +
",oauth_timestamp=" + dD(timeStamp) +
",oauth_token=" + dD(_token) +
",oauth_version=" + dD("1.0");
// 投稿先
URL url = new URL(_tweet_api + "?q="
+ rfc3986(URLEncoder.encode(params[0], "utf-8"))
+ "&count=" + params[1]
);
// 接続準備
HttpURLConnection http = (HttpURLConnection)url.openConnection();
http.setConnectTimeout(30000);
http.setReadTimeout(30000);
http.setRequestMethod("GET");
// ヘッダ
http.setRequestProperty("Authorization", "OAuth " + data);
InputStreamReader isr = null;
try {
// 受信用ストリーム
isr = new InputStreamReader(http.getInputStream(), "UTF-8");
}
catch( Exception e ) {
isr = new InputStreamReader(http.getErrorStream(), "UTF-8");
}
// 受信
BufferedReader br = new BufferedReader(isr);
String line_buffer;
while ( null != (line_buffer = br.readLine() ) ) {
// コマンドプロンプトに表示
result_string += line_buffer;
}
// 終了処理
br.close();
isr.close();
http.disconnect();
}
catch( Exception e ) {
result_string = "{\"errors\":\"unknown\"}";
}
return result_string;
}
// **************************************************************
// 非同期処理終了後の処理( 画面へのアクセスが可能 )
// **************************************************************
@Override
protected void onPostExecute(String result) {
// Tweet メソッドの引数のインターフェイス内のメソッドを呼び出す
OnSearchResult.onSearchResult(result);
}
}.execute(query,String.valueOf(count));
}
private String rfc3986( String param ) {
param = param.replace("+", "%20");
param = param.replace("*", "%2A");
param = param.replace("%7E","~");
return param;
}
private String dD(String param){
return "\""+param+"\"";
}
private String getNonce(){
Random random = new Random();
return String.valueOf(random.nextInt(1000000000));
}
private String getTimeStamp(){
return String.valueOf(System.currentTimeMillis() / 1000L);
}
private String getSignature(String baseString){
String work = "";
work += _consumer_secret;
work += "&";
work += _secret;
String signature = "";
SecretKeySpec key = new SecretKeySpec(work.getBytes(), "HmacSHA1");
try {
Mac mac = Mac.getInstance(key.getAlgorithm());
try{
mac.init(key);
} catch(InvalidKeyException ike){
}
byte[] rawHmac = mac.doFinal(baseString.getBytes());
signature = new String (Base64.encodeToString(rawHmac, Base64.NO_WRAP));
} catch(NoSuchAlgorithmException e){
}
return signature;
}
}
|
|
MyListview.java ( No.1 ) |
|
日時: 2013/10/13 20:38
名前: lightbox
|
日時: 2013/10/13 20:38 名前: lightbox
拡張子:
package com.example.listviewobjectjson;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import com.google.gson.Gson;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.Base64;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.EditText;
public class MyListview extends Activity {
private Gson gson = null;
private JSON_STRING js = null;
private CustomAdapter basicAdapter = null;
// ImageView 用
private Drawable[] image_icon = new Drawable[100];
private int counter = 0;
// ************************************************************
// Android 用初期処理
// ************************************************************
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_listview);
// ボタンのイベントの登録
((Button)MyListview.this.findViewById(R.id.button1))
.setOnClickListener(new View.OnClickListener() {
// ボタンがクリックされた時の処理
@Override
public void onClick(View v) {
// 未入力時の検索文字列
String text = ((EditText)MyListview.this.findViewById(R.id.editText1)).getText().toString();
if ( text.equals("") ) {
text = "消費税";
}
// new オブジェクト(アクセストークンリスト).Search("検索文字列",MAX件数,検索終了イベント)
new TwitterSearch(
"Consumer key",
"Consumer secret",
"Access token",
"Access token secret"
).Search(text, 20, new TwitterSearch.Searched() {
// 検索が終了した時の処理( String result に結果の JSON )
@Override
public void onSearchResult(String result) {
System.out.println(result);
// gson インスタンス
gson = new Gson();
// JSON を クラスインスタンスに変換
// ※ 結局 JSON の処理はこの一行
js = gson.fromJson(result,JSON_STRING.class);
// 画像を件数ぶん保存( インターネットアクセスになるので非同期
// 処理の AsyncTask をインラインで記述 )
// 引数は、JSON_STRING 型のオブジェクト
// 第3引数は未使用( 引渡しが必要無かったので )
new AsyncTask<JSON_STRING, Void, String>() {
// 非同期処理( 画像を件数ぶん保存 )
@Override
protected String doInBackground(JSON_STRING... params) {
counter = 0;
for (TwitterSearchObject item : params[0].statuses) {
URL url;
try {
System.out.println(item.user.profile_image_url);
url = new URL( item.user.profile_image_url );
InputStream is = (InputStream)url.getContent();
image_icon[counter] = Drawable.createFromStream(is, "");
} catch (Exception e) {
e.printStackTrace();
}
counter++;
}
return null;
}
// 画像が保存完了した後に ListView に設定する
// (終了後でないと、リストビューの最初の画像が表示されない)
@Override
protected void onPostExecute(String param) {
// js.statuses は List<TwitterSearchObject>
basicAdapter = new CustomAdapter(
MyListview.this,
R.layout.listview_item,
js.statuses
);
// リストビューにアダプタをセット
ListView listView = (ListView)MyListview.this.findViewById(R.id.listView1);
listView.setAdapter(basicAdapter);
}
}.execute(js); // クラス作成後メソッドを実行
}
});
}
});
((ListView)MyListview.this.findViewById(R.id.listView1))
.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ListView listView = (ListView) parent;
TwitterSearchObject item = (TwitterSearchObject) listView.getItemAtPosition(position);
Toast.makeText(getApplicationContext(), item.text,
Toast.LENGTH_LONG).show();
}
});
}
// ************************************************************
// GSON で使う為の JSON 文字列の構造を定義したクラス
// ************************************************************
private class JSON_STRING {
List<TwitterSearchObject> statuses;
}
private class TwitterSearchObject {
String text;
UserObject user;
}
private class UserObject {
String name;
String screen_name;
String profile_image_url;
}
// ************************************************************
// 専用アダプタの作成
// ************************************************************
public class CustomAdapter extends ArrayAdapter<TwitterSearchObject> {
private LayoutInflater builder;
private int curResourceId;
public CustomAdapter(Context context, int textViewResourceId, List<TwitterSearchObject> objects) {
super(context, textViewResourceId, objects);
curResourceId = textViewResourceId;
builder = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 特定の行(position)のデータを得る
TwitterSearchObject item = (TwitterSearchObject)getItem(position);
// convertViewは使い回しされている可能性があるのでnullの時だけ新しく作る
if (null == convertView) {
convertView = builder.inflate(curResourceId, null);
}
// ************************************************************
// 各項目のセット
// ************************************************************
// 名前
((TextView)convertView.findViewById(R.id.textView1))
.setText(item.user.name);
// 投稿文
((TextView)convertView.findViewById(R.id.textView2))
.setText(item.text);
// 表示名
((TextView)convertView.findViewById(R.id.textView3))
.setText(item.user.screen_name);
// 画像をセット
if ( image_icon[position] != null ) {
((ImageView)convertView.findViewById(R.id.imageView1))
.setImageDrawable(image_icon[position]);
}
return convertView;
}
}
// ************************************************************
// メニュー( 未使用 )
// ************************************************************
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_my_listview, menu);
return true;
}
}
|
主画面 ( activity_my_listview.xml ) ( No.2 ) |
日時: 2013/10/13 20:22 名前: lightbox
|
ListView 部分( listview_item.xml ) ( No.3 ) |
日時: 2013/10/13 20:22 名前: lightbox
|
|
|
|