ソース掲示板




すべてから検索

キーワード   条件 表示 現行ログ 過去ログ トピックス 名前 本文
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;
	}
}
このアーティクルの参照用URLをクリップボードにコピー メンテナンス
主画面 ( activity_my_listview.xml ) ( No.2 )
日時: 2013/10/13 20:22
名前: lightbox
拡張子:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:tools="http://schemas.android.com/tools"
	android:layout_width="match_parent"
	android:layout_height="match_parent" >

	<LinearLayout
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:layout_alignParentBottom="true"
		android:layout_alignParentLeft="true"
		android:layout_alignParentRight="true"
		android:layout_alignParentTop="true"
		android:orientation="vertical" >

		<Button
			android:id="@+id/button1"
			android:layout_width="match_parent"
			android:layout_height="wrap_content"
			android:text="検索" />

		<EditText
			android:id="@+id/editText1"
			android:layout_width="match_parent"
			android:layout_height="wrap_content"
			android:ems="10" >

			<requestFocus />
		</EditText>

		<ListView
			android:id="@+id/listView1"
			android:layout_width="match_parent"
			android:layout_height="224dp"
			android:layout_weight="0.02" />

	</LinearLayout>

</RelativeLayout>
このアーティクルの参照用URLをクリップボードにコピー メンテナンス
ListView 部分( listview_item.xml ) ( No.3 )
日時: 2013/10/13 20:22
名前: lightbox
拡張子:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="match_parent"
	android:layout_height="match_parent" >

	<TextView
		android:id="@+id/textView1"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:layout_alignParentLeft="true"
		android:layout_alignParentRight="true"
		android:layout_alignParentTop="true"
		android:focusable="false"
		android:paddingLeft="5dp"
		android:paddingTop="5dp" />

	<TextView
		android:id="@+id/textView2"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:layout_alignParentLeft="true"
		android:layout_alignParentRight="true"
		android:layout_below="@+id/textView1"
		android:layout_marginLeft="22dp"
		android:layout_marginTop="5dp"
		android:focusable="false" />

	<ImageView
		android:id="@+id/imageView1"
		android:layout_width="50dp"
		android:layout_height="60dp"
		android:layout_alignLeft="@+id/textView2"
		android:layout_below="@+id/textView2"
		android:layout_marginLeft="5dp"
		android:src="@drawable/ic_action_search" />

	<TextView
		android:id="@+id/textView3"
		android:layout_width="200dp"
		android:layout_height="wrap_content"
		android:layout_alignTop="@+id/imageView1"
		android:layout_marginLeft="15dp"
		android:layout_marginTop="16dp"
		android:layout_toRightOf="@+id/imageView1"
		android:textColor="#0000FF" />

</RelativeLayout>
このアーティクルの参照用URLをクリップボードにコピー メンテナンス