2012年2月15日水曜日

[Android]Postが無視されるときの対処方法

とあるページのcgiにpostしようとしたときのお話しです。

いつもどおりpostしていたのですが、なぜか結果が反映されなかったのです。
コードはこちら。

HttpPost method = new HttpPost(url);
DefaultHttpClient client = new DefaultHttpClient();
List<NameValuePair> params = new ArrayList<NameValuePair>(1);
params.add(new BasicNameValuePair("name", name));
params.add(new BasicNameValuePair("comment", content));
method.setEntity(new UrlEncodedFormEntity(params, "Shift_JIS"));
     
HttpResponse response = client.execute( method );
int status = response.getStatusLine().getStatusCode();
if ( status == HttpStatus.SC_OK ) {
res = EntityUtils.toString( response.getEntity(), "Shift_JIS" );
}


なぜ??ということでヘッダーを確認してみました。

urlencoded
Host: ホスト名
Connection: Keep-Alive
User-Agent: Apache-HttpClient/UNAVAILABLE (java 1.4)

User-Agentが怪しい。

という訳でUser-Agentを偽装して実行。
method.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.77 Safari/535.7");

おっ、できた。


コード全文はこちら。

HttpPost method = new HttpPost(url);
method.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.77 Safari/535.7");
DefaultHttpClient client = new DefaultHttpClient();
List<NameValuePair> params = new ArrayList<NameValuePair>(1);
params.add(new BasicNameValuePair("name", name));
params.add(new BasicNameValuePair("comment", content));
method.setEntity(new UrlEncodedFormEntity(params, "Shift_JIS"));


HttpResponse response = client.execute( method );
int status = response.getStatusLine().getStatusCode();
if ( status == HttpStatus.SC_OK ) {
res = EntityUtils.toString( response.getEntity(), "Shift_JIS" );
}

※文字コードは適宜変えて使用すること。
 今回はたまたまShift-JISだっただけ。


ヘッダーはphpで出力確認しました。
HeaderとGetのパラメータ、Postのパラメータを確認する。 コードはこちら。

<?php
echo "<br>Headers:<br>";
$headers = getallheaders();
while (list ($header, $value) = each ($headers)) {
echo "$header: $value\n";
}

echo "<br>Get Parameters:<br>";
foreach( $_GET as $key => $value ) {
  echo htmlspecialchars($key) . "=" . htmlspecialchars($value) . "<BR>";
}

echo "<br>Post Parameters:<br>";
foreach( $_POST as $key => $value ) {
  echo htmlspecialchars($key) . "=" . htmlspecialchars($value) . "<BR>";
}
?>
http://gettingsignals.sakura.ne.jp/test/check_header.php

2012年2月1日水曜日

楽天トラベル:スマートフォンコンテスト優秀賞受賞

楽天トラベルさんで開催されていたスマートフォンアプリコンテストの結果発表があり、
私の作成した「旅宿」アプリがな・ん・と優秀賞を受賞しました。


Android初めて、4ヶ月目に作った4番目の作品です。

選ばれて賞いただけるというのは嬉しいですね。

今後もアプリ制作頑張っていきます。

2012年1月31日火曜日

[Android]tagSoup で Webスクレイピング

tagSoup はHTMLパーサー。
AndroidのWebViewでも使われているらしい。
ただ、APIは公開されていないので、外部jarとして登録して使いました。
jar は公式サイトから Download できます。
http://ccil.org/~cowan/XML/tagsoup/
サイズも100kb未満で経済的。

使い方が、ちんぷんかんぷん。
一度、挫折しかけました。

こちらのサイトを参照しながら何とか実装。
http://d.hatena.ne.jp/Retrorocket/20110905/1315214059
http://www.beach5.net/2011/11/androidhtml-2.html
http://nullmo.blog89.fc2.com/blog-entry-492.html

調べた限りだと方法は2つあります。
1.Transformして、タグネームでノードを取得する方法。


Transformer transformer = TransformerFactory.newInstance()
.newTransformer();
DOMResult result = new DOMResult();
transformer.transform(new SAXSource(reader, source), result);

Document doc = (Document) result.getNode();
NodeList childs = doc.getElementsByTagName("td");


2.Paerser にHandlerをセットして、パースしていく方法。

InputSource source = new InputSource(new InputStreamReader(in, "UTF-8"));
parser.setContentHandler(new TestHandler());
parser.parse(source);



private class TestHandler implements org.xml.sax.ContentHandler
    {
        @Override
        public void characters(char[] ch, int start, int length)
                throws SAXException
        {
        }

        @Override
        public void endDocument() throws SAXException
        {
        }

        @Override
        public void endElement(String uri, String localName, String qName)
                throws SAXException
        {
        }

        @Override
        public void startDocument() throws SAXException
        {
        }

        @Override
        public void startElement(String uri, String localName, String qName,
                Attributes atts) throws SAXException
        {
       }


個人的には、2が分かりやすかったです。
のコードをコピペしてログ出すとこんな感じです。
01-30 13:12:51.300: I/System.out(4577): startDocument
01-30 13:12:51.460: I/System.out(4577): startElement:<html> 
01-30 13:12:51.490: I/System.out(4577): startElement: <head> 
01-30 13:12:51.630: I/System.out(4577): startElement:  <meta>  [http-equiv=content-type] [content=text/html;charset=Shift_JIS]
01-30 13:12:51.640: I/System.out(4577): endElement  :  <meta>
01-30 13:12:51.721: I/System.out(4577): startElement:  <meta>  [name=generator]
01-30 13:12:51.740: I/System.out(4577): endElement  :  <meta>
01-30 13:12:51.780: I/System.out(4577): startElement:  <title> 
01-30 13:12:51.810: I/System.out(4577): characters  :   [あ~か行のお店]
01-30 13:12:51.840: I/System.out(4577): endElement  :  <title>
01-30 13:12:52.000: I/System.out(4577): startElement:  <meta>  [name=Description] [content=]
01-30 13:12:52.010: I/System.out(4577): endElement  :  <meta>
01-30 13:12:52.080: I/System.out(4577): startElement:  <meta>  [name=Keywords] [content=]
01-30 13:12:52.092: I/System.out(4577): endElement  :  <meta>
01-30 13:12:52.240: I/System.out(4577): startElement:  <meta>  [name=viewport] [content=width=device-width] [user-scalable=yes]
01-30 13:12:52.250: I/System.out(4577): endElement  :  <meta>
01-30 13:12:52.379: I/System.out(4577): startElement:  <meta>  [name=format-detection] [content=telephone=no]
01-30 13:12:52.389: I/System.out(4577): endElement  :  <meta>
01-30 13:12:52.519: I/System.out(4577): startElement:  <meta>  [name=apple-mobile-web-app-capable] [content=yes]
01-30 13:12:52.539: I/System.out(4577): endElement  :  <meta>
01-30 13:12:52.690: I/System.out(4577): startElement:  <meta>  [name=apple-mobile-web-app-status-bar-style] [content=default]
01-30 13:12:52.690: I/System.out(4577): endElement  :  <meta>
01-30 13:12:52.811: I/System.out(4577): startElement:  <link>  [rel=start] [href=/] [title=ホーム]

これを足がかりに実装すればok

2012年1月26日木曜日


AndroidでWebスクレイプしたHtmlを表示したくて、いろいろ試してみました。

WebViewに渡してやれば表示されるだろうと思ってたけど、
<img>タグで指定した画像が表示されなかったのです。
画像が?で表示されてしまう。

上がWebView
下がTextView

WebViewで<img>タグ使えないのかと勝手な思い込みで、
TextViewに表示してみることにしました。


表示させたいHtmlはこんな感じ。
// Header
String data = "<html><head><meta http-equiv=\"content-type\" content=\"text/html;charset=UTF-8\"></head>"; 
//<img>タグ
data += "<div><img src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVAP50lVKq4kiypEuO7d0NqvOR0CCowbtRBUG0Zctzdu5nca4GXs0rdcrkUU9Jk9oa8SGWkaLLk1cVQVPowCao7xuV3SAuwrA80rbAQNcjtsPRRTqO6F51bS6LCxz0e1pyH6-yrxnwkF0/s220/CIMG0998.JPG\"></div> ";
// <a>タグ
data += "<a href=\"http://gettingsignals.blogspot.com/\">gettingsignals</a>";


TextViewに画像表示するにはHtml.ImageGetter()を使います。


        TextView textView = (TextView)findViewById(R.id.textView);
        // <a>タグに対応
        MovementMethod movementmethod = LinkMovementMethod.getInstance();
        textView.setMovementMethod(movementmethod);
        // <img>タグのsrcに指定された画像urlから画像を取得
        textView.setText(Html.fromHtml(data, new Html.ImageGetter(){
            public Drawable getDrawable(String source){
                // img srcで指定されたURLからDrawable を取得する
            Drawable d = doGetDrawable(source);

                // 取得した元画像のサイズを取得し、表示画像のサイズとする
            if(d != null) {
               int w = d.getIntrinsicWidth();
               int h = d.getIntrinsicHeight();
               d.setBounds(0, 0, w, h);
            }
                
                return d;
            }
        }, null));


// Drawableを取得するメソッド
public static Drawable doGetDrawable( String url )
{
Drawable drawable = null;
   try
   {
       HttpGet method = new HttpGet( url );

       DefaultHttpClient client = new DefaultHttpClient();

       method.setHeader( "Connection", "Keep-Alive" );
       
       HttpResponse response = client.execute( method );
       int status = response.getStatusLine().getStatusCode();
       if ( status == HttpStatus.SC_OK ) {
        InputStream is = response.getEntity().getContent();
        drawable = Drawable.createFromStream(is, "");
        is.close();
       }
   }
   catch ( Exception e )
   {
    e.printStackTrace();
       return null;
   }
   return drawable;
}


実装完了。
で、試してみる。

UnknownHostException



AndroidManifest.xml にパーミッションの追加漏れ。。

<uses-permission android:name="android.permission.INTERNET"/>

もう一度、実行。
上がWebView
下がTextView

WebViewも表示されてる・・・。
単純にパーミッションの追加漏れが原因だったようです。


最後にWebViewのコード
        WebView webView = (WebView)findViewById(R.id.webView);
        webView.setWebViewClient(new WebViewClient() {});
        webView.getSettings().setJavaScriptEnabled(true);
        webView.getSettings().setPluginsEnabled( true );
// Headerはつけておくと文字化けとかしない。
        String data = "<html><head><meta http-equiv=\"content-type\" content=\"text/html;charset=UTF-8\"></head>"; 
    data += "<div><img src=\"https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVAP50lVKq4kiypEuO7d0NqvOR0CCowbtRBUG0Zctzdu5nca4GXs0rdcrkUU9Jk9oa8SGWkaLLk1cVQVPowCao7xuV3SAuwrA80rbAQNcjtsPRRTqO6F51bS6LCxz0e1pyH6-yrxnwkF0/s220/CIMG0998.JPG\"></div> ";
        data += "<a href=\"http://gettingsignals.blogspot.com/\">gettingsignals</a>";
// webView.loadData()でもいいけど、特定の文字(%とか)が含まれてると表示されないらしい。
        webView.loadDataWithBaseURL(null, data, "text/html", "UTF-8", null);

2012年1月20日金曜日

Androidでffmpegライブラリを組み込んでみた

という記事を投稿する予定です。

--
GettingSignals


(2012/3/21 追記)
なかなか記事にまとめられず、だんだん忘れてきてしまいました。
完全に忘れてしまう前にメモ残しときます。

libffmpeg.soをビルドパスに追加。
jniのソースにffmpeg.cを追加。
main関数の名前をパッケージ名に変換する
ndk-buildする
アプリ側から呼び出す

こんな流れです。だいぶ忘れてます。

2011年12月23日金曜日

イヤホンジャックアクセサリ

スマートフォンをカスタマイズしていますか?

カバーや液晶保護シールが一般的ですが、
おしゃれなものがなく、似たようなものばかりですよね。

オリジナリティ出したい!という方にはコチラがお勧めです。

あなたのスマートフォンにイヤホンジャックは付いていますか?
イヤホンジャックに付けるアクセサリです。
イヤホンジャックカバー、イヤホンピアスとか呼ばれています。

いろいろな種類があったので、まとめてみました。

シンプル系

葉っぱ
ネジ
動物系

食品系
食品系

キラキラ系
キラキラ系
動物キラキラ系


シンプルなものからおもしろ系、女子が好きそうなキラキラ系などいろいろあります。

注意しないといけないのは、イヤホンジャックの位置です。
機種によっては横にあったり、そもそもなかったりします。

自分だけのオリジナルスマートフォンにしてみてはどうでしょうか?

<ニュースリソース>
スマートフォン 専用 アクセサリー スマホ キャップル
スマホ女子はキラキラがお好き!? イヤホンジャックをキラめかせるアクセサリー『i-Diamond』
Androidケータイのイヤホンジャックに、おしゃれと機能をプラス! キティやミッフィーデザインの「プラギィ」
ドロイド君をiPhoneとかブラックベリーのイヤホンジャックに挿してみた。可愛すぎ
海外でも話題! 個性派スマホグッズ plugy android
プレゼントにも◎ 女心をくすぐるスマホ向けピアスがカワイイ! 男性向けもあるでよ~
【グッズ情報】イヤホンジャックアクセサリー、今度は葉っぱ!「happa PLUG APLI」
【アクセサリー】ドロイド君といつも一緒!イヤホンジャックアクセサリー!
【グッズ情報】新鮮な寿司ネタを豊富に入荷しました(笑)「ネタ系食品スマホピアス」

2011年11月30日水曜日

[Android][Help]動画のコマ送り・コマ戻しの方法(注:できてないです)

Androidで動画のコマ送り・コマ戻しがしたいのですが、
 数日調べて分からなかったので調査結果だけをメモします。

 ・Androidではフレームごとのデータを取得する方法がAPIでは用意されていない。
  (VideoView や MediaPlayerではそのようなAPIがないということ。)

 ・MediaMetadataRetriever を使えば、指定の時間の画像を取得できそうだが、
試してみると先頭 / 先頭+1のフレームしか取れない。 http://developer.android.com/reference/android/media/MediaMetadataRetriever.html

 MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(getApplicationContext(), uri); int time = 1500; // 取得したい時間(ms)
 Bitmap bitmap = mmr.getFrameAtTime(time);
 //mmr.getFrameAtTime(time, MediaMetadataRetriever.OPTION_NEXT_SYNC); // 先頭の次のフレーム取得する
 //mmr.getFrameAtTime(time, MediaMetadataRetriever.OPTION_PREVIOUS_SYNC); // 先頭フレーム取得する
//mmr.getFrameAtTime(time, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
if(bitmap != null) {
 imageView.setImageBitmap(bitmap);
 } 

・VideoView / MediaPlayer のseekTo()で指定秒数に飛ばしてすぐにpause()すると、
映像が更新されない。
 // ボタン押したら、コマ送り
 button.setOnClickListener(new OnClickListener() {
 @Override
 public void onClick(View v) {
 int pos = mMediaPlayer.getCurrentPosition();
 // mMediaPlayer.start();       // 絵が更新されないので、再生してみる(→更新されない)
 mMediaPlayer.seekTo(pos + 150); // 150ms加算してコマ送り
 mMediaPlayer.start();       // 絵が更新されないので、再生してみる(→更新されない)
 videoView.invalidate();     // 強制的に描画(→更新されない)
 mMediaPlayer.pause();
 }
 } 

・MediaPlayer でシーク完了(setOnSeekCompleteListener)したときに処理しても、映像が更新されない
// ボタン押したら、コマ送り
 button.setOnClickListener(
new OnClickListener() {
 @Override
 public void onClick(View v) { 
int pos = mMediaPlayer.getCurrentPosition();
 mMediaPlayer.seekTo(pos + 150); // 150ms加算してコマ送り
 }
 }

 // button押下後、イベントはきている
mMediaPlayer.setOnSeekCompleteListener(new OnSeekCompleteListener() {
 @Override
 public void onSeekComplete(MediaPlayer mp) {
 if(isSeek) {
 int time = mp.getCurrentPosition();
 mp.start();
 mp.pause();
 }
 }
 });

 なぜなぜ分析してみる
 ・getFrameAtTime()で先頭フレームしか取得できないのはなぜ?
  パラメータの設定の仕方が悪いの? 

・pause中のseekTo()で画像が更新されないのは理解できるとして、
  pause -> start()したときに画像が更新されないのはなぜ?
 だれかヘルプm(__)m

 できたこと、わかったこと
・seekTo()とかgetFrameAtTime()使わずに、コマ送り(風)な処理はできました
 mMediaPlayer.start();  // 再生して、
try {
 Thread.sleep(33);      // 指定秒数スリープしたあとに、
 } catch (InterruptedException e) { }
mMediaPlayer.pause();        // 一時停止する

 ただこの方法だとコマ戻しができないのです。
コマ戻しするには秒数指定するしかないですよね。
困った。どうしましょう