SQLの窓

2015年04月19日


Monaca と 本当の 『Onsen UI最小限のテンプレート』



エクスポートしただけなので、インポートして使えます。



手元に Monaca 公式ガイドブックがあるのですが、インターネット上のドキュメントだけは情報が整理されていない上に一番キモとなるものは、検証しないと解らない事が多かったです。

とりあえず、ons-gesture-detector 要素の使い方のサンプルとして見て下さい。ちなみに、イベント内の gesture 情報を見るには通常 jQuery が必要です。jQuery mobile では、使えません。また、イベントの書き方としても、$(document).on('イベント', 'セレクタ', function(e){} でないと動作しませんでした。他の書き方で書いても、Monaca デバッガで動いても apk にビルドしたものは動作しませんでした。目安としては、プレビューで動かないものは、apk でも動かない可能性があります。

※ jQuery mobile は、コンソールのクリアがうまくいかないような気もします

index.html
<!DOCTYPE HTML>
<html>
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
	<script src="components/loader.js"></script>
	<link rel="stylesheet" href="components/loader.css">
	<link rel="stylesheet" href="css/style.css">
	<script>
		ons.bootstrap();
		ons.ready(function(){
			console.clear();
			console.log("開始");

			$(document).on('click', '#gestureBtn', function(e){
				console.log("ページ移動\n------------");
				myNavigator.pushPage('page2.html');
			});

			// *************************************
			// gestureBtn 用
			// *************************************
			// タップ
			$(document).on('tap', '.gestureTest', function(e){
				console.log("tap");
				console.log("------------");
				for( prop in e.originalEvent.gesture) {
					console.log(prop);
				}
				
			});
			$(document).on('doubletap', '.gestureTest', function(e){
				console.log("doubletap");
			});

			// ホールド
			$(document).on('hold', '.gestureTest', function(e){
				console.log("hold");
			});
			$(document).on('release', '.gestureTest', function(e){
				console.log("release");
			});
			
			// ドラッグ
			$(document).on('drag', '.gestureTest', function(e){
				console.log("drag");
			});

			// その他
			$(document).on('touch', '.gestureTest', function(e){
				console.log("touch");
			});
			

			// *************************************
			// 共通
			// *************************************
			$(document).on('touchstart', '.gestureTest', function(e){
				console.log("touchstart");
			});

			$(document).on('touchend', '.gestureTest', function(e){
				console.log("touchend");
			});

		});
	</script>
</head>
<body>
	<ons-navigator var="myNavigator" page="page1.html">
	</ons-navigator> 
</body>
</html>


初期画面( page1.html )

ons-gesture-detector 内でのみ、スマホで必要ないろいろなイベントに対処できるようになっています。これを使わない場合は、touchstart と touchend とあといくつかの基本処理になります。ons-gesture-detector 内では、ここには無いもっと複雑なジェスチャー(スワイプ、ピンチ他)も対応されています。( 実機が無いので検証できませんでしたが )
<ons-page>
	<ons-toolbar>
		<div class="center">Navigator</div>
	</ons-toolbar>

	<div style="text-align: center">
		<br>
		<ons-gesture-detector>
		<ons-button
			class="gestureTest"
			id="gestureBtn">
			次のページを表示
		</ons-button>
		<br>
		(ジェスチャー対応)
		</ons-gesture-detector>
		
		<br>
		<ons-button
			class="gestureTest"
			onclick="myNavigator.pushPage('page2.html')"
			id="normalBtn">
			次のページを表示
		</ons-button>
		<br>
		(普通のボタン)
		
	</div>

</ons-page>


page2.html



<ons-page>
	<ons-toolbar>
		<div class="left"><ons-back-button>Back</ons-back-button></div>
		<div class="center">画像表示</div>
	</ons-toolbar>

	<div style="text-align: center">

		<h1>ページ&#10103;</h1>
		<ons-button
			onclick="myNavigator.popPage()">
		  Pop Page
		</ons-button>
		<br>
		<br>
		<img src="image/page2.jpg">

	</div>
</ons-page>


▼ 下の赤い枠内が gesture 情報



タグ:android MONACA
posted by lightbox at 2015-04-19 20:57 | Android | このブログの読者になる | 更新情報をチェックする

2015年03月29日


Android 5.1 リリース済み。SDK( android-sdk_r24.0.2-windows.zip ) でまずエミュレータ作って、あとから Eclipse と Android Studio で動作確認( 新しい事は、少しづつ問題を消して行くのが近道です )

重要

以下の理由で、エミュレータの RAM は 512 を推奨します
Android Studio : Intel の HAXM と System Image を最新にしたら、installation failed since the device possibly ... というエラーが出ました

Eclipse(Pleiades(Luna)+ADT Plugin)Android Studio の動作確認は終わってますが、ここではそれとは全く関係無い『エミュレータが動く』という事を目標に置いています。正直、最初のテストを古い PC でやってしまって結構いろんな壁にブチあたったりしてます。ですが、結果が重要なので実際に動作した AVD のバリエーションを掲載します。

いずれにしても Java は必要

後の Eclipse や Android Studio を考えたとしても、最近では Java そのものをあまり意識する必要は無いようになって来ていますが、とりあえず Java のアプリケーションが動く環境が必要です。

SDK Manager.exe を起動して、めぼしいバージョンを更新

Android 4.1.2 〜 Android 5.1 の 更新です。結構かかります。ま、でも Android 5.1 だけ入れておいて必要に応じて増やせばいいと思います。









Extras は、とにかく全部入れました(全部必要では無いはずですが、確認取る時間がもったいないので全部入れています)。ただ、最後のアクセラレータはとても重要です。

CPU のオプションとして Windows では通常オフになっていると思いますが、BIOS の設定でオンにした上でインストールするドライバが ectras フォルダに展開されます。インストールは、その中の intelhaxm-android.exe を実行する事によって、アンインストール可能な公式のインストール一覧(プログラムと機能)に表示される形でインストールされます。インストールされると、表からは見えない形のサービスとして起動されて、AVD の x86 関係の CPU が利用できるようになります。

▼ インストール後確認できるレジストリ位置
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\IntelHaxm
( サービス名を使用して、sc query IntelHaxm で確認する事もできます )



type が 1 とあるのは、Kernel device driver である事を示しています

AVD Manager.exe を起動して、デバイスを作成

Nexus S の情報を元にして作成しています



6つのバリエーションを作成して、起動を確認した後に設定アイコンで日本語に変更しています。

これらの設定情報は、C:\Users\ユーザ名\.android\avd に作成されており、この名前とこの中にある .ini ファイル名前を変更して、AVD Manager.exe で読み出した後に更新すると名称変更が可能である事を確認しました。




AVD_for_Nexus_S_412x86



AVD_for_Nexus_S_442x86



AVD_for_Nexus_S_by_Google


Google APIs 関連はなぜか警告が出ますが、間違った内容の警告が出されておりきちんと動作します。

AVD_for_Nexus_S_TEST1



AVD_for_Nexus_S_TEST2



AVD_for_Nexus_S_TEST3



優先順位としては、Android 5.1 で動作確認するといいと思いますが、Android 4.1.2 もそれなりに重要な気もします。Google APIs は、Google 特有のサービスをテストする際に必要だという事が調べた結果なのですが、ここは確認していません。というか、普通の Android アプリケーションを実行するだけなら、Google APIs は必要無いでしょう。armeabi-v7a があるのは、古い PC では アクセラレータを実行する為の機能そのものが無いのでその場合に必要になります( エミュレータの起動は遅くなりますがしかたありません )


以上全ての動作を確認しています

Android 5.0 以降のオープニングは、一番下にあるロックをゆっくり上へドラッグして中に入ります。




関連する記事

Android 5.1 エミュレータ + Android Studio 1.1.0 実行イメージ


Android 5.1 + Pleiades( Luna ) + ADT Plugin


Android Studio と Pleiades Eclipse + Android SDK が参照する Java SE ドキュメントを日本語化する


Android Studio のインストールとエミュレータ実行までの手順




タグ:android
posted by lightbox at 2015-03-29 16:48 | Android | このブログの読者になる | 更新情報をチェックする

2015年03月28日


クラウドでアプリを作成してスマホで動作させる『Monaca』を Android エミュレータの 5.1 で動作確認しました

Monaca は、オンラインで HTML5 と JavaScript を使ってプログラムを書いて、Android または iOS のアプリ経由で実行させるサービスです。実際のアプリとしてビルドも可能ですが、フリーだと24時間に3回だけビルドできるという制限があります。が、開発だけならフリーでも十分です。実際 Android エミュレータ内で普通に動いています。

Monaca の運営会社はアシアル株式会社という日本のベンチャー企業です。ですから、全て日本語で情報が手に入ります。ライセンスの問い合わせも簡単に確認できるはずです。



エミュレータを使うにはいろいろありますが、一番の問題は 公式の apk では 実際問題動作しない事です。

そもそも、更新が 2012年12月19日 で止まっており。公式ページの方法でインストールできるのが Android 4.2.2 までです。それ以降でインストールができないのは、Android のセキュリティ上の問題でしょうが、adb install でインストール可能です( 削除はエミュレータ内のアプリの管理から削除できます )

しかし、仮にインストールしたとしても『ある事』をしないと落ちます。結局最新の apk を apk ダウンローダで入手して adb install するのが推奨されますが、公式にはそんな事できるはずも無いので放置されているみたいです。

『ある事とは』



この行を削除すると、古い apk でも動作しました。何故これが解ったかというと、エミュレータを Android Studio で動作させながらログを監視すると、この行が原因と思われるエラーが出たからです。

最新を使ったほうが・・・

いいのは決まっています。今回動いたからといっていつ動かなくなるか解りませんし、最新のほうが機能が豊富です。但し、Android エミュレータで動かすには公式のサポートは望めないでしょう。しかし、エミュレータで動作する事は、実機を用意しなくていいのでとても魅力的です。





▼ 画像を掲載しやすいので、NAVER でまとめました。
Android エミュレータで使う Monacaデバッガー の選択肢


エミュレータ環境もそれなりに面倒です

非力または古い PC ですといろいろ制限がありました。

armeabi-v7a しか動かない、ちょっと古い PC で、どーしてもエミュレータが起動しなくてやった事

関連する記事

SDK( android-sdk_r24.0.2-windows.zip ) でまずエミュレータ作って、あとから Eclipse と Android Studio で動作確認
Android 5.1 エミュレータ + Android Studio 1.1.0 実行イメージ
Android 5.1 + Pleiades( Luna ) + ADT Plugin




タグ:android MONACA
posted by lightbox at 2015-03-28 01:04 | Android | このブログの読者になる | 更新情報をチェックする

2014年01月09日


JavaScript から Android へアクセス

Android で、Google MAP を扱いたい場合、WebView 内で自分がホストしているページを扱うのがとても容易で便利です。しかし、そうした場合の一番の問題点は、WEBページと、Android とのデータのやり取りができないというところです。

しかし、Android 側の WebView の addJavascriptInterface メソッドでクラスを登録すると、そのクラスのメソッドが JavaScript から呼べるようになります。また、Android 側から JavaScript を呼び出す方法としても、WebView の loadUrl メソッドを使って loadUrl("javascript:コード") というように、ブックマークレットと同じ方法でアクセスできます。

Android の ドキュメントでは以下のようなコードが掲載されています( @JavascriptInterface に注意 / only public methods that are annotated with JavascriptInterface can be accessed from JavaScript )
class JsObject {
    @JavascriptInterface
    public String toString() { return "injectedObject"; }
 }
 webView.addJavascriptInterface(new JsObject(), "injectedObject");
 webView.loadData("", "text/html", null);
 webView.loadUrl("javascript:alert(injectedObject.toString())");
この例では、Android から Web ページの alert を呼び出して、その引数として Android 側のメソッドを与えています。結果的に、Android 側の "injectedObject" という文字列が、Webページ上の alert の結果として表示されます。

以下はもう少し具体的なクラスを使った例、Webページ上のボタンで Android の intent を使用して画面遷移を行います。
package com.example.androidapp;

import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class myWeb {
    Context mContext;

    myWeb(Context c) {
        mContext = c;
    }

    @JavascriptInterface
    public void showPostActivity(String message) {
        
		Intent intent = new Intent(mContext, PostActivity.class);
		intent.putExtra("message_text",message);
		
		// アクティビティの呼び出し
		mContext.startActivity(intent);
        
    }
}

このクラスを以下のようにして登録します
// ************************************************************
// WebView のイベントを処理する
// ************************************************************
webView = (WebView)findViewById(R.id.webView1);
webView.addJavascriptInterface(new myWeb(this), "MyWeb");
webView.setWebViewClient( new WebViewClient() {
		// ページがユーザに対して表示された時のイベント
		public void onPageFinished (WebView view, String url) {
			Log.i("onPageFinished:", url);
		}
		
		public boolean shouldOverrideUrlLoading(WebView view, String url) {
			Log.i("shouldOverrideUrlLoading:", url);
			return false;
		}
	}
);		


最後にボタンで呼び出します( 緯度・経度を引き渡しています )
<input type="button" value="Android へアクセス" onclick="MyWeb.showPostActivity('-34.397, 150.644')">




posted by lightbox at 2014-01-09 12:10 | Android | このブログの読者になる | 更新情報をチェックする

2013年10月29日


Android で Post と Get

HashMap を使って以下のように使用します

Map map = new HashMap();
map.put("url", "");
map.put("charset", "SHIFT_JIS");
map.put("action", "regist");
map.put("name", "lightbox");
map.put("title", "日本語");
map.put("comment", "本文");

new HttpPost() {
	@Override
	protected void onPostExecute(String result) {
		System.out.println(result);
	}
}.execute(map);

HttpPost.java
package jp.winofsql;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.Map;

import android.os.AsyncTask;
import android.util.Log;

// *********************************************************
// POST して テキストを取得する
// *********************************************************
public class HttpPost extends AsyncTask<Map, Void, String> {

	@Override
	protected String doInBackground(Map... params) {
		
		StringBuffer get_data = null;
		
		Map<String,String> myArg = params[0];

		try {
			// ターゲット
			URL url = new URL(myArg.get("url"));
			// 接続オブジェクト
			HttpURLConnection http = (HttpURLConnection)url.openConnection();
			http.setConnectTimeout(30000);
			http.setReadTimeout(30000);
			http.setDoOutput(true);
			http.setRequestMethod("POST");
			// 接続
			http.connect();
			
			OutputStreamWriter osw = new OutputStreamWriter(http.getOutputStream());
			BufferedWriter bw = new BufferedWriter(osw);
			
			Iterator<String> it =  myArg.keySet().iterator();
			String key = null;
			String value = null;
			String data = "";
			while(it.hasNext()) {
				key = it.next().toString();
				if ( key.equals("url") ) {
					continue;
				}
				value = myArg.get(key);
				if ( !data.equals("") ) {
					data += "&";
				}
				data += key + "=" + URLEncoder.encode(value, myArg.get("charset")) ;
			}
			
			Log.i("",data);
				
			bw.write( data );

			bw.close();
			osw.close();
			
			// 以下読み込み3セット
			InputStream i_stream = http.getInputStream();
			// UTF-8 でリーダーを作成
			InputStreamReader i_stream_reader = new InputStreamReader(i_stream, myArg.get("charset"));
			// 行単位で読み込む為の準備   
			BufferedReader b_reader = new BufferedReader(i_stream_reader);
			
			// 行の一括読みこみ
			String line_buffer;
			get_data = new StringBuffer();
			// BufferedReader は、readLine が null を返すと読み込み終了 
			while ( null != (line_buffer = b_reader.readLine() ) ) {   
				// コマンドプロンプトに表示   
				get_data.append( line_buffer );
				get_data.append( "\n" );
			}
			
			// 接続解除
			http.disconnect();
		}
		catch(Exception e) {
			Log.i("HttpPost", e.getMessage());
		}
		return get_data.toString();
	}
	

}

Get は 実行時の引数に URL と キャラクタセットを指定します

new HttpGet() {
	@Override
	protected void onPostExecute(String result) {
		System.out.println(result);
	}
}.execute("http://matome.naver.jp/feed/hot","UTF-8");

HttpGet.java
package jp.winofsql;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

import android.os.AsyncTask;
import android.util.Log;

// *********************************************************
// GET して テキストを取得する
// *********************************************************
public class HttpGet extends AsyncTask<String, Void, String> {

	@Override
	protected String doInBackground(String... params) {
		StringBuffer get_data = null;

		try {
			// ターゲット
			URL url = new URL(params[0]);
			// 接続オブジェクト
			HttpURLConnection http = (HttpURLConnection)url.openConnection();
			// GET メソッド
			http.setRequestMethod("GET");
			// 接続
			http.connect();

			
			// 以下読み込み3セット
			InputStream i_stream = http.getInputStream();
			// UTF-8 でリーダーを作成
			InputStreamReader i_stream_reader = new InputStreamReader(i_stream, params[1]);
			// 行単位で読み込む為の準備   
			BufferedReader b_reader = new BufferedReader(i_stream_reader);
			
			// 行の一括読みこみ
			String line_buffer;
			get_data = new StringBuffer();
			// BufferedReader は、readLine が null を返すと読み込み終了 
			while ( null != (line_buffer = b_reader.readLine() ) ) {   
				// コマンドプロンプトに表示   
				get_data.append( line_buffer );
				get_data.append( "\n" );
			}
			
			// 接続解除
			http.disconnect();					
		}
		catch(Exception e) {
			Log.i("HttpGet", e.getMessage());
		}						
		return get_data.toString();	
	}

}
関連する記事

Framework4.5(C#)ストア : HttpClient で Post と Get する汎用 static クラス
Framework4(C#) : WebClient で Post と Get する汎用 static クラス
Framework4(C#) : Windows Phone OS 7.1 : WebClient で Post と Get する汎用 static クラス



posted by lightbox at 2013-10-29 21:37 | Android | このブログの読者になる | 更新情報をチェックする

別の ADT(Android) で作成されたワークスペースを最新の ADT で開く場合の操作方法

現在の状態



現在最新の ADT では、以前必要だった空のプロジェクトを作成せずにインポートできますが、Metadata が含まれたワークスペースを開く場合は、SDK の場所の変更と、プロジェクトが使用する Android SDK のバージーョンを指定する必要がある場合があります。

まず、プロジェクトが『自動にビルド』になっている事を確認



SDK の場所の再設定







プロジェクトの SDK のバージョンの選択






これ以外にも、直接動作に支障が無い場合が多いですが、Manifest に SDK のターゲットを指定する場所があります。 



posted by lightbox at 2013-10-29 13:13 | Android | このブログの読者になる | 更新情報をチェックする

2013年10月13日


Android : ListView Twitter 検索テンプレート



OneDrive へ移動




ListView で表示されている画像は、『private Drawable[] image_icon = new Drawable[100];』を定義しておいて、検索結果分を保存しておくようにしています。アダプタ内の getView で行うと、Android の仕様により、画像の表示がスクロールに追いつかない状態になります。

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)
ソース全体


posted by lightbox at 2013-10-13 20:19 | Android | このブログの読者になる | 更新情報をチェックする

2013年10月12日


トラブル : AndroidManifest.xml に XML 宣言があるとエラーになる???

ちょっと前に作ったプロジェクトが動かなくなったので、現在動作中のプロジェクトと比べてみると、動いているほうには XML 宣言がありません・・・・。

Android ドキュメントの The AndroidManifest.xml File には、XML 宣言あるんですけれど・・・
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.posttest"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />
    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.posttest.MainActivity"
            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="NextPage">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
            </intent-filter>
        </activity>
	</application>

</manifest>
1行目を削除すると動いてくれます。

ですが、わけが解らないのでいろいろ更新をかけていくうちに・・・・Eclipse の Android 部分が動かなくなりました。

なもんで、Google より最新版をダウンロードして日本語化して Window Builder をインストールしました。

Android は、4.3 になったので、AndroidManifest.xml の android:targetSdkVersion="17" を android:targetSdkVersion="18" に変えて動作確認しました。

※ 以下はプロジェクトのプロパティの変更です。


※ ようこそがちょっと違う???


関連する記事ADT( Android Development Tools ) Eclipse の導入(日本語化手順はこの中)
■ ADT( Android Development Tools ) Eclipse に Window Builder をインストールして Swing アプリケーションを作成する(1)ADT( Android Development Tools ) Eclipse に Window Builder をインストールして Swing アプリケーションを作成する(2)

結局今は、どちらの AndroidManifest.xml でも動作しています(なにがなんだか)。



最新の ADT の Eclipse は、特に変わらず Juno でした。
『.eclipseproduct』
name=Eclipse Platform
id=org.eclipse.platform
version=4.2.0


posted by lightbox at 2013-10-12 20:19 | Android | このブログの読者になる | 更新情報をチェックする

Twitter API の自分のアプリのトークンを使って投稿するだけの class Android_Twitter

更新履歴
2013-07-11 : 初回投稿
2013-08-24 : Twitter がエラーを返す場合は、getErrorStream を使う必要があったので修正
2013-10-12 : rfc3986 内部メソッドを追加( やはりきっちりしないとエラーになりました )

Twitter がエラーを返す場合( 例えば投稿データか空の場合 )は、getInputStream では無く getErrorStream を使わないと、エラー内容が取得できなかったので修正しました
Android なので、非同期処理は AsyncTask です。クラスのメソッド内で匿名のインナー型を使用して実装しています。終了時のイベントは、専用のインターフェイスを作成して、onPostExecute から呼び出しています。 Android_Twitter.java
package com.example.posttest;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
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;
import android.widget.EditText;

public class Android_Twitter {

	// 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/statuses/update.json";
	
	// **************************************************************
	// コンストラクタ
	// **************************************************************
	public Android_Twitter(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 Tweeted {
		public void onTweetResult(String result);
   	}
	
	// **************************************************************
	// Twitter 投稿
	// **************************************************************
	public void Tweet(String text, final Tweeted OnTweetResult) {
		
		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("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("status=" + 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 = "POST" + "&";
					// 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);

					// 接続準備
					HttpURLConnection http = (HttpURLConnection)url.openConnection();
					http.setConnectTimeout(30000);
					http.setReadTimeout(30000);
					http.setDoInput(true);
					// ヘッダ
					http.setRequestProperty("Authorization", "OAuth " + data);
					http.setRequestMethod("POST");					

					// 送信用ストリーム
					OutputStreamWriter osw = new OutputStreamWriter(http.getOutputStream());
					BufferedWriter bw = new BufferedWriter(osw);
					
					// 送信データの書き込み
					bw.write("status=" + rfc3986(URLEncoder.encode(params[0], "utf-8")));
					bw.close();
					osw.close();
					
					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 メソッドの引数のインターフェイス内のメソッドを呼び出す
				OnTweetResult.onTweetResult(result);
			}
			
		}.execute(text);
	
	}
	
	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;
	}

}

処理が完了すると、onTweetResult が実行されます。

MainActivity.java
package com.example.posttest;

import com.example.posttest.Android_Twitter.Tweeted;

import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.widget.EditText;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

	public void postAction(View view) {
		
		EditText et = (EditText)this.findViewById(R.id.editText1);
		
		new Android_Twitter(
                    "Consumer key",
                    "Consumer secret",
                    "Access token",
                    "Access token secret").Tweet(et.getText().toString(), new Tweeted() {
			
			@Override
			public void onTweetResult(String result) {
				System.out.println(result);
			}
		});

	}
   
}

関連する記事

Twitter API の自分のアプリのトークンを使って投稿するだけの class VS2010_Twitter

Twitter API の自分のアプリのトークンを使って投稿するだけの class VS2012_Twitter

WSH : VBScript と JavaScript で Twitter に投稿する

PHP : Twitter 投稿関数( twitter_update ) / cURL 関数

Twitter アプリの登録方法と、API キーの利用



タグ:twitter API
posted by lightbox at 2013-10-12 17:39 | Android | このブログの読者になる | 更新情報をチェックする

2013年09月28日


Android 単純リストビューを google-gson で最速構築

SkyDrive へ移動


このままでは使い物にはなりませんが、基本コードとしてのテンプレートになります。ですから、この後、ListView と Gson 用クラスををカスタマイズして項目を増やしていくだけでリストビューの部分の作成は容易になります。
package com.example.listviewobjectjson;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.List;

import com.google.gson.Gson;

import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

public class MyListview extends Activity {
	
	private Gson gson = null;
	private WebGet task;
	private JSON_STRING js;
	private ArrayAdapter<MyObject> basicAdapter;
	
	// ************************************************************
	// Android 用初期処理
	// ************************************************************
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_my_listview);

		Button button = (Button) findViewById(R.id.button1);
		button.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				// AsyncTask に引数を与えて呼び出す
				// 1) doInBackground, 2) onProgressUpdate, 3) onPostExecute
				task = new WebGet();
				task.execute( "http://10.0.2.2/lightbox/php_json/log.php" );
			}
		});
 	}
	
	// ************************************************************
	// Android 用非同期処理
	// ************************************************************
	private class WebGet extends AsyncTask<String, String, String> {
		
		// ここは使っていません
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
		}
		
		// *******************************************************
		// UI スレッド側の処理 ( 引数は JSON 文字列 )
		// *******************************************************
		@Override
		protected void onPostExecute(String result) {
			// UI スレッドとして処理される
			System.out.println(result);

			// gson インスタンス
			gson = new Gson();
			
			// JSON を クラスインスタンスに変換
			js = gson.fromJson(result,JSON_STRING.class);
			
			// js.item は List<MyObjecyt>
			basicAdapter = new ArrayAdapter<MyObject>(
					MyListview.this,
					R.layout.textview,
					js.item
			);
			
			// リストビューにアダプタをセット
			ListView listView = (ListView)MyListview.this.findViewById(R.id.listView1);
			listView.setAdapter(basicAdapter);			

		}

		// *******************************************************
		// 非同期スレッドの呼び出し
		// *******************************************************
		@Override
		protected String doInBackground(String... aurl) {

			Log.i("MyListview2:","開始");
			String json_string = "";

			try {

				// 引数の URL を使用して実行
				URL url = new URL(aurl[0]);
				// 接続オブジェクト( getInputStream をする為のもの )
				HttpURLConnection http = (HttpURLConnection)url.openConnection();
				http.setConnectTimeout(30000);
				http.setReadTimeout(30000);
				http.setRequestMethod("GET");
				
				// 以下読み込み3セット
				InputStream i_stream = http.getInputStream();
				// UTF-8 でリーダーを作成
				InputStreamReader i_stream_reader = new InputStreamReader(i_stream, "UTF-8");
				// 行単位で読み込む為の準備   
				BufferedReader b_reader = new BufferedReader(i_stream_reader);
				
				// 行の一括読みこみ
				String line_buffer;   
				// BufferedReader は、readLine が null を返すと読み込み終了 
				while ( null != (line_buffer = b_reader.readLine() ) ) {   
					// コマンドプロンプトに表示   
					json_string += line_buffer;
				}
	 
				// 後処理
				b_reader.close();
				i_stream_reader.close();
				i_stream.close();

				http.disconnect();
			}
			catch (Exception e) {
				e.printStackTrace();
			}
			
			// JSON 文字列を onPostExecute に対して送る
			return json_string;
		}

	}	
	
	// ************************************************************
	// GSON で使う為の JSON 文字列の構造を定義したクラス
	// ************************************************************
	private class JSON_STRING {
		List<MyObject> item;
	}

	private class MyObject {
		String text;
		
		// 単純リストビューなので、toString で 内容を返すようにしておく
		public String toString() {
			return this.text;
		}
	}
	
	// ************************************************************
	// メニュー( 未使用 )
	// ************************************************************
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.activity_my_listview, menu);
		return true;
	}
}

関連する記事

Android をテストするのにほんの少し楽になるかもしれないクラス
( このクラスでもう少しコードがすっきりします )



posted by lightbox at 2013-09-28 23:19 | Android | このブログの読者になる | 更新情報をチェックする

2013年09月14日


Android をテストするのにほんの少し楽になるかもしれないクラス



例えばここでテストしたいのは、Twitter の投稿なのですが、そのたびに一からイベントやらキャストやらファイル読み書きとか面倒なので作りました。
2013-09-12 : 初回投稿
2013-09-14 : ダイアログ処理とラベル(TextView)処理を追加しました
メソッドの引数でクラスのオブジェクトが必要なところで new と 入力して CTRL+SPACE で Eclipse が匿名のインナー型の候補を出してくれる事を期待しています。
util.buttonClick(R.id.button1, new OnClickListener() {
			
	@Override
	public void onClick(View v) {
		// TODO 自動生成されたメソッド・スタブ
				
	}
});
util. と 最後のセミコロンを除いて、殆ど Eclipse が候補を出してくれます。 Util.java
package com.example.textfile;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Calendar;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.app.DatePickerDialog.OnDateSetListener;
import android.content.DialogInterface;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class Util {
	
	public Activity cur = null;
	public final Calendar c = Calendar.getInstance();
	public final int year = c.get(Calendar.YEAR);
	public final int month = c.get(Calendar.MONTH);
	public final int day = c.get(Calendar.DAY_OF_MONTH);

	public Util(Activity ma) {
		cur = ma;
	}
	
	// ******************************************
	// その他
	// ******************************************
	public void toast(String message) {
        Toast.makeText(cur, message, Toast.LENGTH_LONG).show();    	
	}
	
	// ******************************************
	// 入力
	// ******************************************
	public String getFieldStr(int id) {
		
		return ((EditText)cur.findViewById(id)).getText().toString();
		
	}
	public void setFieldStr(int id,String text) {
		
		((EditText)cur.findViewById(id)).setText(text);
		
	}

	// ******************************************
	// ラベル
	// ******************************************
	public String getViewStr(int id) {
		
		return ((TextView)cur.findViewById(id)).getText().toString();
		
	}
	public void setViewStr(int id,String text) {
		
		((TextView)cur.findViewById(id)).setText(text);
		
	}
	
	// ******************************************
	// ボタン
	// ******************************************
	public Button getButton(int id) {

		return (Button) cur.findViewById(id);

	}
	public void buttonClick(int id,OnClickListener I) {

		((Button) cur.findViewById(id)).setOnClickListener(I);

	}
	
	// ******************************************
	// テキストファイル
	// ******************************************
	public void saveText( String name, String text) throws Exception  {
        
		FileOutputStream outStream = cur.openFileOutput(name, android.content.Context.MODE_PRIVATE);
        OutputStreamWriter writer = new OutputStreamWriter(outStream);
		writer.write(text);
        writer.flush();
        writer.close();
		
	}
	public String getText( String name ) throws Exception {
		
        FileInputStream fis = cur.openFileInput(name);
        int size = fis.available();
        InputStreamReader isr = new InputStreamReader( fis );
        BufferedReader br = new BufferedReader(isr);
        StringBuffer all_string = new StringBuffer( size ); 
        String str;
        while((str = br.readLine()) != null){
        	all_string.append(str);
        }
        br.close();
        
        return all_string.toString();
		
	}
	
	// ******************************************
	// ダイアログ
	// ******************************************
	public DatePickerDialog dateDialog(OnDateSetListener listener) {
	
			return new DatePickerDialog(cur,listener,year,month,day);
			
	}
	
	public AlertDialog messageBox(String title,String message,DialogInterface.OnClickListener onClickListener1,DialogInterface.OnClickListener onClickListener2) {

		AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(cur);
		alertDialogBuilder.setTitle(title);
		alertDialogBuilder.setMessage(message);
		
		alertDialogBuilder.setPositiveButton("YES",onClickListener1);
		alertDialogBuilder.setNegativeButton("NO", onClickListener2);
		alertDialogBuilder.setCancelable(true);

		return alertDialogBuilder.create();
		
	}

}


MainActivity.java
package com.example.textfile;

import com.example.textfile.Android_Twitter.Tweeted;

import android.os.Bundle;
import android.app.Activity;
import android.app.DatePickerDialog;
import android.app.DatePickerDialog.OnDateSetListener;
import android.content.DialogInterface;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.DatePicker;

public class MainActivity extends Activity {

	private Util util = new Util( this );
	
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // ボタンイベント
        util.buttonClick(R.id.button1,new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				
				// Twitter 投稿
				new Android_Twitter(
					"",
					"",
					"",
					"").Tweet(
							util.getFieldStr(R.id.editText1), new Tweeted() {
			
					// 投稿結果
					@Override
					public void onTweetResult(String result) {
						try {
							// 内部ストレージへのファイル保存
							util.saveText("json.txt", result);
						}
						catch( Exception ex ) {
							ex.printStackTrace();
						}
					}
		        });    	

				// メッセージ
				util.toast("投稿しました!!");
				
			}
		});

        // 初期処理として、内部ストレージから文字列を取得
        try{
            util.setFieldStr(R.id.editText1, util.getText("file1.txt"));
        }
        catch(Exception ex){
			ex.printStackTrace();
        }
        
        // ボタンイベント
        util.buttonClick(R.id.button2, new OnClickListener() {
			
			@Override
			public void onClick(View v) {

				// 日付ダイアログ
				util.dateDialog(new OnDateSetListener() {
					
					@Override
					public void onDateSet(DatePicker view, int year, int monthOfYear,
							int dayOfMonth) {

						// 結果を表示
						util.setViewStr(R.id.textView1, year + "/" + (monthOfYear+1)+"/"+dayOfMonth);
						
					}
				}).show();	// 表示
				
			}
		});

        // ホダンイベント
        util.buttonClick(R.id.button3, new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				
				// 選択肢応答ダイアログ
				util.messageBox("メッセージボックス", "Yes または No による選択処理",
						// YES の処理
						new DialogInterface.OnClickListener() {
					
							@Override
							public void onClick(DialogInterface dialog,
									int which) {
								System.out.println("YES");
								
							}
							
						},
						// NO の処理
						new DialogInterface.OnClickListener() {

							@Override
							public void onClick(DialogInterface dialog,
									int which) {
								System.out.println("NO");
								
							}
						}
				).show();	// 表示
			}
		});
    }

    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
    
}



posted by lightbox at 2013-09-14 16:33 | Android | このブログの読者になる | 更新情報をチェックする

2013年08月31日


Android : Google Spreadsheets API version 3.0でGoogleスプレッドシートを参照

SkyDrive へ移動



Google のライブラリのダウンロード

gdata-java-client

かなり多くのライブラリが中に入っていますが、実際使うのは 5つです。

1) gdata-client-1.0.jar
2) gdata-core-1.0.jar
3) gdata-spreadsheet-3.0.jar
4) guava-11.0.2.jar
5) jsr305.jar

1〜3 は、lib ディレクトリ内で、4〜5 は、deps 内です。Google 的には、gdata を使うのは spreadsheet のみのようで、他の機能( 例えばカレンダー ) は他の API を使うようになっています。また、認証も本来は OAuth を推奨しているようですが、機能の説明に重点を置くために、簡単にユーザーとパスワードのログインで処理しています。


package com.example.posttest;

import java.net.URL;
import java.util.List;

import com.google.gdata.client.spreadsheet.*;
import com.google.gdata.data.spreadsheet.*;
import com.google.gdata.util.*;

import android.os.AsyncTask;
import android.widget.EditText;

public class Android_Spreadsheet {

	// AsyncTask のインラインで参照する為の final
	private final String _userid;
	private final String _password;

	// **************************************************************
	// コンストラクタ
	// **************************************************************
	public Android_Spreadsheet(String _userid, String _password) {
		this._userid = _userid;
		this._password = _password;
	}

	// **************************************************************
	// AsyncTask の onPostExecute から外部イベントとして呼び出す為のインターフェイス
	// **************************************************************
	public interface SendMailed {
		public void onTweetResult(List<CellEntry> result);
   	}
	
	// **************************************************************
	// 通信処理
	// **************************************************************
	public void GetCells(String FileName, String SheetName,final SendMailed OnTweetResult) {
		
		new AsyncTask<String, Void, List<CellEntry>>() {

			// **************************************************************
			// 非同期処理
			// **************************************************************
			@Override
			protected List<CellEntry> doInBackground(String... params) {
				
				List<CellEntry> cells = null;

				String result_string = "";
				try {
					
					// *************************************************
					// ClientLogin
					// *************************************************
				    String USERNAME = _userid;
				    String PASSWORD = _password;

				    SpreadsheetService service = new SpreadsheetService("MySpreadsheetIntegration-v1");
				    service.setUserCredentials(USERNAME, PASSWORD);
				    
					// *************************************************
				    // バージョン3
					// *************************************************
				    SpreadsheetService sss = new SpreadsheetService("MySpreadsheetIntegration");
				    sss.setProtocolVersion(SpreadsheetService.Versions.V3);
				    
					// *************************************************
				    // 一覧を取得
					// *************************************************
				    URL SPREADSHEET_FEED_URL = new URL(
				        "https://spreadsheets.google.com/feeds/spreadsheets/private/full");
				    SpreadsheetFeed feed = service.getFeed(SPREADSHEET_FEED_URL, SpreadsheetFeed.class);
				    // 一覧用のリスト
				    List<SpreadsheetEntry> spreadsheets = feed.getEntries();
				    
				    // スプレッドシート一覧より目的のインデックスを取得
				    int target = 0;
				    for (SpreadsheetEntry Spreadsheet : spreadsheets) {

				    	System.out.println( Spreadsheet.getTitle().getPlainText() );
				    	
				    	if ( Spreadsheet.getTitle().getPlainText().equals(params[0]) ) {
				    		break; 
				    	}
				    	
				    	target++;
				    	
				    }				    
				    

				    SpreadsheetEntry spreadsheet = spreadsheets.get(target);
				    WorksheetFeed worksheetFeed = service.getFeed(
					        spreadsheet.getWorksheetFeedUrl(), WorksheetFeed.class);
				    List<WorksheetEntry> worksheets = worksheetFeed.getEntries();

				    target = 0;
				    for (WorksheetEntry worksheet : worksheets) {

				    	System.out.println( worksheet.getTitle().getPlainText() );
				    	
				    	if ( worksheet.getTitle().getPlainText().equals(params[1]) ) {
				    		break; 
				    	}
				    	
				    	target++;
				    	
				    }				    
				    
				    WorksheetEntry worksheet = worksheets.get(target);

				    URL cellFeedUrl = worksheet.getCellFeedUrl();
				    CellFeed cellFeed = service.getFeed(cellFeedUrl, CellFeed.class);
				    cells = cellFeed.getEntries();
					
				}
				catch( Exception e ) {
					System.out.println(e.getMessage());
				}
				
				return cells;
			}

			// **************************************************************
			// 非同期処理終了後の処理( 画面へのアクセスが可能 )
			// **************************************************************
			@Override
			protected void onPostExecute(List<CellEntry> result) {
				// 引数のインターフェイス内のメソッドを呼び出す
				OnTweetResult.onTweetResult(result);
			}
			
		}.execute(FileName,SheetName);
	
	}

}

スプレッドシートの一覧や、シートの一覧等、それぞれで通信処理をするので、Android では AsyncTask を用いて全て一括で処理を行って、最後の一覧のみを終了イベントとして処理するようにしています。

package com.example.posttest;

import java.net.URL;
import java.util.List;

import com.example.posttest.Android_Spreadsheet;
import com.google.gdata.data.spreadsheet.CellEntry;
import com.google.gdata.data.spreadsheet.CellFeed;
import com.google.gdata.data.spreadsheet.SpreadsheetEntry;
import com.google.gdata.data.spreadsheet.WorksheetEntry;
import com.google.gdata.data.spreadsheet.WorksheetFeed;

import android.os.Bundle;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;

public class MainActivity extends Activity {

	private Android_Spreadsheet as = null;
	private ArrayAdapter<String> adapter = null;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		as = new Android_Spreadsheet(
			"", // アカウント
			"" // パスワード
			);
		
		adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1);
		
	}

	public void postAction(View view) {
		
	    
	    new MessageBox(this,"確認","送信しますか?") {

			@Override
			void onYesClick(DialogInterface dialog, int which) {

				System.out.println("onYesClick");
				
				as.GetCells( "In Googleドライブ", "シート2" ,new Android_Spreadsheet.SendMailed() {
					
						@Override
						public void onTweetResult(List<CellEntry> result) {
							
							ListView listView = (ListView)MainActivity.this.findViewById(R.id.listView1);
							listView.setAdapter(adapter);
							
						    for (CellEntry cell : result) {
						    	
						    	adapter.add( cell.getCell().getValue().toString() );
						    	// System.out.println( cell.getCell().getValue().toString() );
						    	
						    }				    
						    
						}
				});
				
			}
			
		}.show();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		
		// 選択されたメニュー項目の ID
		int menuId = item.getItemId();
		
		switch(menuId){
		case R.id.action_settings:
			System.out.println("action_settings が選択されました");
			break;
		case R.id.menu_action1:
			System.out.println("menu_action1  が選択されました");
			
			Intent intent = new Intent();
			intent.putExtra("data", "メッセージを別のページに引き渡します");
			intent.setClassName("com.example.posttest", "com.example.posttest.NextPage");
			startActivity(intent);		
			
			break;
		}

		return true;
	}		
	
}




posted by lightbox at 2013-08-31 01:10 | Android | このブログの読者になる | 更新情報をチェックする

2013年06月27日


Android 4.2.2(ADT) : class MyDatePicker extends DatePickerDialog

Android : 日付ダイアログをインナーで使う では、その場で処理を記述するのでとりあえずの処理としてならば簡単ですが、きちんと制御したい場合には不向きなので、継承してクラスを作ってみました

ただ、この場合専用のインターフェイスが必要になります。それと、もともとキャンセルした時とそうでない決定ボタンを使った時の処理に汎用性が無かったので、いろいろ面倒な事をする結果となっています。



※ 画面の大きな Nexus 7 でエミュレートしてみました
package com.example.shapetextview;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

import org.w3c.dom.Document;

import jp.winofsql.*;

import android.app.Activity;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.app.TimePickerDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.text.format.DateFormat;
import android.view.Menu;
import android.view.View;
import android.widget.DatePicker;
import android.widget.TextView;
import android.widget.TimePicker;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		this.setContentView(R.layout.activity_main);
	}
	
	private interface MyClick {
		public void onClick(DialogInterface dialog, int which,int yy,int mm, int dd);
		public void onCancel();
	}

	private class MyDatePicker extends DatePickerDialog {

		private int yy = 0;
		private int mm = 0;
		private int dd = 0;

		private int year = 0;
		private int monthOfYear = 0;
		private int dayOfMonth = 0;
		
		private MyClick clickCallBack;

		public MyDatePicker(Activity context, MyClick callBack,
				int year, int monthOfYear, int dayOfMonth) {
			super(context, null, year, monthOfYear, dayOfMonth);
			this.clickCallBack = callBack;
			this.year = year;
			this.monthOfYear = monthOfYear;
			this.dayOfMonth = dayOfMonth;
		}

		@Override
		public void onClick(DialogInterface dialog, int which) {
			if ( yy == 0 ) {
				yy = this.year;
				mm = this.monthOfYear;
				dd = this.dayOfMonth;
			}
			clickCallBack.onClick(dialog, which,yy,mm+1,dd);
		}

		@Override
		public void onDateChanged(DatePicker view, int year, int month, int day) {
			this.yy = year;
			this.mm = month;
			this.dd = day;
		}
		
		@Override
		protected void onStop() {
			super.onStop();
			if ( yy == 0 ) {
				clickCallBack.onCancel();
			}
		}
		
	}	
	
	public void openDialog(View view) {
		
		final Calendar c = Calendar.getInstance();
		int year = c.get(Calendar.YEAR);
		int month = c.get(Calendar.MONTH);
		int day = c.get(Calendar.DAY_OF_MONTH);

		MyDatePicker mdp = 	new MyDatePicker(this,new MyClick(){

			@Override
			public void onClick(DialogInterface dialog, int which, int yy,
					int mm, int dd) {
				TextView textView = (TextView)MainActivity.this.findViewById(R.id.textView1);
				textView.setText(yy + "/" + mm + "/" + dd);
				
			}
			
			@Override
			public void onCancel() {
				System.out.println("キャンセル");
			}

			
		}, year, month, day);
		mdp.show();

	}
}

onStop は、無条件にダイアログが閉じられた時に発生します。
onDateChanged は、日付を UI で変更しないと発生しません



posted by lightbox at 2013-06-27 19:57 | Android | このブログの読者になる | 更新情報をチェックする

2013年06月26日


Android : 日付ダイアログをインナーで使う( 完了ボタンをクリックした場合としない場合の対応 )







DatePickerDialog の 引数に与える callback では、完了ボタンを押さなくても日付が返って来るので、単純に閉じた時の判断をしています。
package com.example.shapetextview;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

import org.w3c.dom.Document;

import jp.winofsql.*;

import android.app.Activity;
import android.app.DatePickerDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.DatePicker;
import android.widget.TextView;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		this.setContentView(R.layout.activity_main);
	}

	public void openDialog(View view) {

		final Calendar c = Calendar.getInstance();
		final int year = c.get(Calendar.YEAR);
		final int month = c.get(Calendar.MONTH);
		final int day = c.get(Calendar.DAY_OF_MONTH);

		new DatePickerDialog(this, null, year, month, day) {

			int yy = 0;
			int mm = 0;
			int dd = 0;

			@Override
			public void onDateChanged(DatePicker view, int year, int month,
					int day) {
				this.yy = year;
				this.mm = month;
				this.dd = day;
			}
			
			@Override
			public void onClick(DialogInterface dialog, int which) {

				TextView textView = (TextView)MainActivity.this.findViewById(R.id.textView1);
				if ( this.yy == 0  ) {
					textView.setText(year + "/" + (month+1) + "/" + day);
				}
				else {
					textView.setText(this.yy + "/" + (this.mm+1) + "/" + this.dd);
				}
			}
			
		}.show();
		
	}
}

関連する記事

Android 4.2.2(ADT) : class MyDatePicker extends DatePickerDialog


posted by lightbox at 2013-06-26 21:42 | Android | このブログの読者になる | 更新情報をチェックする

2013年06月20日


Android(4.2.2) ADT から Post や Get をできるかぎり簡単にするサンプル

SkyDrive へ移動




このサンプルの目的は、サーバにあるデータベースを Android より更新したり読み出したりする事にあります。ここでは、まだ一覧表示である ListView 部分がありませんが、4つのクラスを含むパッケージを用意しています。

1) HttpPostAndXml : Post して結果を XML で取得する
2) HttpGetAndXml : Get して結果を XML で取得する
3) GetRssItemValue : 取得した XML 内の item 要素内の先頭データの指定要素の値を取得する
4) MessageBox : 更新確認の為の AlertDialog を使用する

1) 2) は、非同期処理である必要があるため、AsyncTask を継承しています( AsyncTask<String, Void, Document> )。GetRssItemValue は、RSS 的な単純なフォーマットを想定しており、値を容易に取得可能です。MessageBox は、実際以下のように使っています
package com.example.shapetextview;

import java.util.HashMap;
import java.util.Map;

import org.w3c.dom.Document;

import jp.winofsql.*;

import android.app.Activity;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		this.setContentView(R.layout.activity_main);
	}

	public void postData(View view) {
		
		new MessageBox(this, "確認", "更新しますか?") {
			
			@Override
			public void onYesClick(DialogInterface dialog, int which) {
				
				Map<String,String> map = new HashMap<String,String>();
				map.put("url", "http://10.0.2.2/lightbox/webapi/write_utf8.php");
				map.put("u", "insert into rss (guid,title) values('0001','タイトル')");

				new HttpPostAndXml() {

					@Override
					protected void onPostExecute(Document result) {
						
						GetRssItemValue iv = new GetRssItemValue(result);
						TextView tv = (TextView)MainActivity.this.findViewById(R.id.textView1);
						tv.setText(iv.get("title"));
						
					}
					
				}.execute(map);
				
			}
			
		}.show();
	
	}
	
	public void getData(View view) {
		
		String url = "http://matome.naver.jp/feed/hot";
		url = "http://10.0.2.2/lightbox/webapi/show_utf8.php";
		
		new HttpGetAndXml() {

			@Override
			protected void onPostExecute(Document result) {
				
				GetRssItemValue iv = new GetRssItemValue(result);
				TextView tv = (TextView)MainActivity.this.findViewById(R.id.textView1);
				tv.setText(iv.get("title"));
				
			}
			
		}.execute(url);
	
	}

}

プロジェクトにある wkprefs フォルダにあるデータは、ワークスペースの ADT( Eclipse ) の設定を他のワークスペースに以降する為のセットです(行番号・フォント・テンプレート等)。

※ TextView の Background に、XML で定義したリソースで枠線を引いています

関連する記事

Android : POST 送信。Eclipse(ADT) を使ったコード作成手順 / MessageBox.java と HttpPostAndXml.java



posted by lightbox at 2013-06-20 21:18 | Android | このブログの読者になる | 更新情報をチェックする
Seesaa の各ページの表示について
Seesaa の 記事がたまに全く表示されない場合があります。その場合は、設定> 詳細設定> ブログ設定 で 最新の情報に更新の『実行ボタン』で記事やアーカイブが最新にビルドされます。

Seesaa のページで、アーカイブとタグページは要注意です。タグページはコンテンツが全く無い状態になりますし、アーカイブページも歯抜けページはコンテンツが存在しないのにページが表示されてしまいます。

また、カテゴリページもそういう意味では完全ではありません。『カテゴリID-番号』というフォーマットで表示されるページですが、実際存在するより大きな番号でも表示されてしまいます。

※ インデックスページのみ、実際の記事数を超えたページを指定しても最後のページが表示されるようです

対処としては、このようなヘルプ的な情報を固定でページの最後に表示するようにするといいでしょう。具体的には、メインの記事コンテンツの下に『自由形式』を追加し、アーカイブとカテゴリページでのみ表示するように設定し、コンテンツを用意するといいと思います。


※ エキスパートモードで表示しています

アーカイブとカテゴリページはこのように簡単に設定できますが、タグページは HTML 設定を直接変更して、以下の『タグページでのみ表示される内容』の記述方法で設定する必要があります

<% if:page_name eq 'archive' -%>
アーカイブページでのみ表示される内容
<% /if %>

<% if:page_name eq 'category' -%>
カテゴリページでのみ表示される内容
<% /if %>

<% if:page_name eq 'tag' -%>
タグページでのみ表示される内容
<% /if %>
この記述は、以下の場所で使用します


Windows
container 終わり

フリーフォントで簡単ロゴ作成
フリーフォントでボタン素材作成
フリーフォントで吹き出し画像作成
フリーフォントではんこ画像作成
ほぼ自由に利用できるフリーフォント
フリーフォントの書体見本とサンプル
画像を大きく見る為のウインドウを開くボタンの作成

Android SDK ポケットリファレンス
改訂版 Webデザイナーのための jQuery入門
今すぐ使えるかんたん ホームページ HTML&CSS入門
CSS ドロップシャドウの参考デモ
PHP正規表現チェッカー
Google Hosted Libraries
cdnjs
BUTTONS (CSS でボタン)
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり