ラベル Google API の投稿を表示しています。 すべての投稿を表示
ラベル Google API の投稿を表示しています。 すべての投稿を表示

2010年11月28日日曜日

Google Image Search API の android サンプル・アプリ作ってみた

Google Image Search API を使用した android アプリを作成してみました。

Google Image Search API は以下のサイトにあるように、q=xxx の部分で検索キーワードを指定します。
応答は JSON フォーマットです。


Google Image Search API



検索例: 高知城

リクエスト

レスポンス
{"responseData": {"results":[{"GsearchResultClass":"GimageSearch","width":"600","height":"400","imageId":"Ehuk98rEYswf5M:","tbWidth":"135","tbHeight":"90","unescapedUrl":"http://img01.kitaguni.tv/usr/akkesi1946/1%E9%AB%98%E7%9F%A5%E5%9F%8E.jpg","url":"http://img01.kitaguni.tv/usr/akkesi1946/1%25E9%25AB%2598%25E7%259F%25A5%25E5%259F%258E.jpg","visibleUrl":"akkesi1946.kitaguni.tv","title":"風のふくまま・・・:\u003cb\u003e高知城\u003c/b\u003e","titleNoFormatting":"風のふくまま・・・:高知城","originalContextUrl":"http://akkesi1946.kitaguni.tv/e543472.html","content":"\u003cb\u003e高知城\u003c/b\u003e","contentNoFormatting":"高知城","tbUrl":"http://images.google.com/images?q\u003dtbn:Ehuk98rEYswf5M::img01.kitaguni.tv/usr/akkesi1946/1%25E9%25AB%2598%25E7%259F%25A5%25E5%259F%258E.jpg"},{"GsearchResultClass":"GimageSearch","width":"420","height":"313","imageId":"MHxe0rjkGw3MRM:","tbWidth":"125","tbHeight":"93","unescapedUrl":"http://ozu.cc.kochi-u.ac.jp/~fuchu/syakai/top_img2%5B1%5D.jpg","url":"http://ozu.cc.kochi-u.ac.jp/~fuchu/syakai/top_img2%255B1%255D.jpg","visibleUrl":"ozu.cc.kochi-u.ac.jp","title":"top_img2[1].jpg","titleNoFormatting":"top_img2[1].jpg","originalContextUrl":"http://ozu.cc.kochi-u.ac.jp/~fuchu/syakai/kochicastle.html","content":"\u003cb\u003e高知城\u003c/b\u003e","contentNoFormatting":"高知城","tbUrl":"http://images.google.com/images?q\u003dtbn:MHxe0rjkGw3MRM::ozu.cc.kochi-u.ac.jp/~fuchu/syakai/top_img2%255B1%255D.jpg"},{"GsearchResultClass":"GimageSearch","width":"640","height":"480","imageId":"7H2hzs-xvZA-kM:","tbWidth":"137","tbHeight":"103","unescapedUrl":"http://map.papanavi.jp/ucan/img/sho/39000373_1.jpg","url":"http://map.papanavi.jp/ucan/img/sho/39000373_1.jpg","visibleUrl":"map.papanavi.jp","title":"\u003cb\u003e高知城\u003c/b\u003e | パパナビ","titleNoFormatting":"高知城 | パパナビ","originalContextUrl":"http://map.papanavi.jp/p/spot/details/S39000373-001","content":"\u003cb\u003e高知城\u003c/b\u003e","contentNoFormatting":"高知城","tbUrl":"http://images.google.com/images?q\u003dtbn:7H2hzs-xvZA-kM::map.papanavi.jp/ucan/img/sho/39000373_1.jpg"},{"GsearchResultClass":"GimageSearch","width":"2048","height":"1536","imageId":"6K73uOHjLlHRpM:","tbWidth":"150","tbHeight":"113","unescapedUrl":"http://userdisk.webry.biglobe.ne.jp/008/871/08/N000/000/000/121957888604916210164.JPG","url":"http://userdisk.webry.biglobe.ne.jp/008/871/08/N000/000/000/121957888604916210164.JPG","visibleUrl":"takachin0413.at.webry.info","title":"たかちゃんの独り言/ウェブリブログ","titleNoFormatting":"たかちゃんの独り言/ウェブリブログ","originalContextUrl":"http://takachin0413.at.webry.info/","content":"山内一豊の\u003cb\u003e高知城\u003c/b\u003eです。","contentNoFormatting":"山内一豊の高知城です。","tbUrl":"http://images.google.com/images?q\u003dtbn:6K73uOHjLlHRpM::userdisk.webry.biglobe.ne.jp/008/871/08/N000/000/000/121957888604916210164.JPG"}],"cursor":{"pages":[{"start":"0","label":1},{"start":"4","label":2},{"start":"8","label":3},{"start":"12","label":4},{"start":"16","label":5},{"start":"20","label":6},{"start":"24","label":7},{"start":"28","label":8}],"estimatedResultCount":"392000","currentPageIndex":0,"moreResultsUrl":"http://www.google.com/images?oe\u003dutf8\u0026ie\u003dutf8\u0026source\u003duds\u0026start\u003d0\u0026hl\u003dja\u0026q\u003d%E9%AB%98%E7%9F%A5%E5%9F%8E"}}, "responseDetails": null, "responseStatus": 200}


今回のサンプル・アプリでは、"title" と "unescapedUrl" の値を利用しています。

JSON は階層構造になっているので、こららの値を取得するには、
"responseData" --> "results" と値を辿っていく必要があります。

JSON の解析は、org.json.JSONObject か org.json.JSONArray を使うと簡単に行えます。

"responseData" は値が1つのなので、JSONObject を、
"results" は値が複数(4つ)なので、JSONArray を使用します。

ポイントは、こんなところです。


サンプル・アプリは、3つの要素で構成しています。

  • 検索キーワードを入力する EditText
  • 検索処理を開始する Button
  • 検索結果を表示する ListView


ListView の項目をクリックすると、
別途 Activity を起動し、画像を表示させるようにしています。



メイン Activity のレイアウトです。

main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<EditText
    android:id="@+id/keyword"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="高知城"
    />
<Button
    android:id="@+id/search"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="イメージ検索"
    />
<ListView
    android:id="@+id/list"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_weight="2"
    />
</LinearLayout>


メイン Activity です。

JsonTestActivity.java
package jp.kochi.rtaki;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;

public class JsonTestActivity extends Activity {

  private static final String QUERY_URL = "https://ajax.googleapis.com/ajax/services/search/images?v=1.0&q=";

  private ListView mListView = null;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Button search = (Button) findViewById(R.id.search);

    search.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick(View v) {
        EditText keyword = (EditText) findViewById(R.id.keyword);
        String key = keyword.getText().toString();

        updateList(key);
      }
    });

    mListView = (ListView) findViewById(R.id.list);

    mListView.setOnItemClickListener(new OnItemClickListener() {

      @Override
      public void onItemClick(AdapterView<?> parent, View view,
          int position, long id) {

        Map map = (Map) parent.getItemAtPosition(position);
        String url = (String) map.get("unescapedUrl");

        Intent intent = new Intent();
        intent.setClass(JsonTestActivity.this, ImageActivity.class);
        intent.putExtra("url", url);
        
        startActivity(intent);
      }
    });
  }

  private void updateList(String key) {

    ArrayList listData = requestImageSearch(key);

    SimpleAdapter adapter = new SimpleAdapter(this, listData,
        android.R.layout.simple_list_item_1, new String[] { "title" },
        new int[] { android.R.id.text1 });

    mListView.setAdapter(adapter);

  }

  private ArrayList requestImageSearch(String key) {
    Map<String, Object> temp;
    ArrayList<Map> listData = new ArrayList<Map>();

    String encodeKey = Uri.encode(key);

    HttpClient client = new DefaultHttpClient();

    String req = QUERY_URL + encodeKey;

    Log.v("JsonTest", "query :" + req);

    HttpUriRequest httpUriReq = new HttpGet(req);

    try {

      HttpResponse res = client.execute(httpUriReq);

      if (res.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
        Toast.makeText(JsonTestActivity.this,
            "StatusCode = " + res.getStatusLine().getStatusCode(),
            Toast.LENGTH_LONG).show();
      } else {

        
        String entity = EntityUtils.toString(res.getEntity());

        JSONObject jsonObj = new JSONObject(entity);

        String responseStatus = jsonObj.getString("responseStatus");
        Log.v("JsonTest", "responseStatus :" + responseStatus);

        if (responseStatus.equals("200") != true) {
          Toast.makeText(JsonTestActivity.this,
              "responseStatus = " + responseStatus,
              Toast.LENGTH_LONG).show();
        } else {

          JSONObject jsonObjData = jsonObj
              .getJSONObject("responseData");

          JSONArray jsonObjResultArray = jsonObjData
              .getJSONArray("results");
          for (int i = 0; i < jsonObjResultArray.length(); i++) {
            JSONObject jsonObjResult = jsonObjResultArray
                .getJSONObject(i);
            Log.v("JsonTest", "title :"
                + jsonObjResult.getString("title"));

            Map map = new HashMap<String, String>();
            map.put("title", jsonObjResult.getString("title"));
            map.put("unescapedUrl", jsonObjResult
                .getString("unescapedUrl"));
            listData.add(map);
          }

        }

      }

    } catch (ClientProtocolException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    } catch (JSONException e) {
      e.printStackTrace();
    }

    return listData;
  }
}


image.xml と ImageActivity.java は、ListView の項目をクリックしたときに、
画像を表示させる Activity です。

image.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<ImageView
    android:id="@+id/image"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    />
</LinearLayout>


ImageActivity.java
package jp.kochi.rtaki;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.widget.ImageView;

public class ImageActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.image);
    
    Intent intent = getIntent();
    String url = intent.getStringExtra("url");
    
    Bitmap bitmap = getImage(url);
    
    ImageView image = (ImageView) findViewById(R.id.image);
    image.setImageBitmap(bitmap);
    
  }

  private Bitmap getImage(String url) {
    Bitmap bitmap = null;
    InputStream in = null;
    BufferedOutputStream out = null;
    byte[] buf = new byte[512];
    int bytes;

    try {
      in = new BufferedInputStream(new URL(url).openStream(), 512);

      final ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
      out = new BufferedOutputStream(dataStream, 512);
      while ((bytes = in.read(buf)) > 0) {
        out.write(buf, 0, bytes);
      }

      out.flush();

      final byte[] data = dataStream.toByteArray();
      BitmapFactory.Options options = new BitmapFactory.Options();
      // options.inSampleSize = 1;

      bitmap = BitmapFactory.decodeByteArray(data, 0, data.length,
          options);

    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        if (in != null)
          in.close();
        if (out != null)
          out.close();

      } catch (IOException e) {
        e.printStackTrace();
      }
    }

    return bitmap;
  }
}


マニフェスト・ファイルは、青字の2行を追加しています。

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="jp.kochi.rtaki"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".JsonTestActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        
        <activity android:name=".ImageActivity" />

    </application>
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-sdk android:minSdkVersion="4" />

</manifest> 


以上、はじめてマッッシュアップでした~(・o・)

2010年11月27日土曜日

Google API について調べてみた

Google API って、いろいろとあるな~と気づいた今日この頃です。

どんなものか気になります。

ということで、、調べてみました。

大したことは調べてませんが、気になったところを整理します。

  1. android でどうあつかうか?

    処理の要求は、HTTPのGETリクエスト。
    応答のフォーマットは JSON(ジェイソン、JavaScript Object Notation)。
    JSON フォーマットは、org.json.JSONObject.JSONObject で解析可能。


  2. JSONって何?

    <キー>:<値>のペアをカンマ区切りで列記する形式。
    JavaScript のハッシュ記法と同じ。
    これは、JSON の名前の通り、元々は JavaScript で、そのまま利用することを想定しているため。
    値をタグで挟む(ことが多い) XML よりは記述量が少なくなる傾向がある。


  3. 要求/応答のサンプルを見せてくれ。

    Blog Search API を使った例。
    高知城に関するブログを検索する。

    ブラウザで以下の URL を入力する。

    Blog Search - 検索
    https://ajax.googleapis.com/ajax/services/search/blogs?v=1.0&q=%E9%AB%98%E7%9F%A5%E5%9F%8E

    "v=1.0" はバージョン。

    "q=%E9%AB%98%E7%9F%A5%E5%9F%8E" は検索条件。

    %E9%AB%98%E7%9F%A5%E5%9F%8E は 高知城 を JavaScript の encodeURIComponent() と同等の方式でエンコードした値。
    android の場合、android.net.Uri.encode() でエンコードすればよい。


    上記 HTTP リクエストに対する応答は以下の通り。

    Blog Search - 応答
    {"responseData": {"results":[{"GsearchResultClass":"GblogSearch","title":"\u003cb\u003e高知城\u003c/b\u003e: 青春18切符で行く,日本の「城」巡り","titleNoFormatting":"高知城: 青春18切符で行く,日本の「城」巡り","postUrl":"http://oojijisun.seesaa.net/article/170228621.html","content":"\u003cb\u003e高知城\u003c/b\u003e,現存天守閣のある12城から始め、国内の100名城を鑑賞します。日本の歴史、建築土木技術を語る旅です。春休み、夏休み、冬休み、にカメラを持って行きます。","author":"oojijisun","blogUrl":"http://oojijisun.seesaa.net/","publishedDate":"Sun, 21 Nov 2010 14:45:09 -0800"},{"GsearchResultClass":"GblogSearch","title":"12月の日程予定 - \u003cb\u003e高知城\u003c/b\u003eサッカークラブ情報ブログ","titleNoFormatting":"12月の日程予定 - 高知城サッカークラブ情報ブログ","postUrl":"http://blog.goo.ne.jp/kochijyo-sc/e/8267635293edf7372e58db52ba4917f4","content":"1991年結成 \u003cb\u003e高知\u003c/b\u003e県\u003cb\u003e高知\u003c/b\u003e市で活動する壮年サッカーチームの情報交換広場です.","author":"kochijyo-sc","blogUrl":"http://blog.goo.ne.jp/kochijyo-sc/","publishedDate":"Wed, 24 Nov 2010 01:07:38 -0800"},{"GsearchResultClass":"GblogSearch","title":"山内一豊と千代さんの夫婦愛の結晶 \u003cb\u003e高知城\u003c/b\u003eを歩こう!","titleNoFormatting":"山内一豊と千代さんの夫婦愛の結晶 高知城を歩こう!","postUrl":"http://www.pippikochi.or.jp/events/2010/12/post-252.shtml","content":"さすが敵から身を守るお城!\u003cb\u003e高知城\u003c/b\u003eの至る所に仕掛けがあるのをご存知ですか?知られていない\u003cb\u003e高知城\u003c/b\u003eの姿がわかるかも!?私たちと一緒に歩いてみませんか。 【日時】12月4日(土)13:30~15:30 【集合場所】高知樹追手門前 【定員】20名(先着) ...","author":"kvnc2","blogUrl":"http://www.pippikochi.or.jp/events/","publishedDate":"Wed, 24 Nov 2010 16:58:46 -0800"},{"GsearchResultClass":"GblogSearch","title":"中央公園と\u003cb\u003e高知城\u003c/b\u003e - こんぶろ-酒屋のブログ-","titleNoFormatting":"中央公園と高知城 - こんぶろ-酒屋のブログ-","postUrl":"http://blog.goo.ne.jp/konjiru/e/584250c87cdbc6c0b25926c2406b68af","content":"とある小さな酒屋、近藤印 \u003cb\u003e高知\u003c/b\u003e酒店のお酒にまつわったり・まつわんなかったりするブログです。","author":"konjiru","blogUrl":"http://blog.goo.ne.jp/konjiru/","publishedDate":"Tue, 16 Nov 2010 06:28:49 -0800"}],"cursor":{"pages":[{"start":"0","label":1},{"start":"4","label":2},{"start":"8","label":3},{"start":"12","label":4},{"start":"16","label":5},{"start":"20","label":6},{"start":"24","label":7},{"start":"28","label":8}],"estimatedResultCount":"80969","currentPageIndex":0,"moreResultsUrl":"http://blogsearch.google.com/blogsearch?oe\u003dutf8\u0026ie\u003dutf8\u0026safe\u003dactive\u0026source\u003duds\u0026start\u003d0\u0026hl\u003dja\u0026q\u003d%E9%AB%98%E7%9F%A5%E5%9F%8E"}}, "responseDetails": null, "responseStatus": 200}


    ブラウザ画面



  4. Google API には、どんなものがある?

    以下のサイトの APIs Using the Loader にあるものが Google API。
    但し、JSON に対応していないものがある。

    http://code.google.com/intl/ja/apis/loader/index.html


実際に、Google API を使った android のサンプル・アプリを作ってみようと思います~
たぶん。