SQLの窓

2012年11月28日


Android(4.0.3) : WebView を使って Google にログインしてアクセストークンを取得する

ログイン画面は Google が提供する画面が表示されます。



そこでログインすると、登録済のアプリケーションの名前が表示されて、処理を許可するかどうかの確認画面が表示されて、許可すると登録済のリダイレクト URL が表示されます。ここで使用している Google API に登録したアプリケーションは、『Client ID for web applications』として、自分の API Console で作成や表示が可能です。

※ Calendar API と Google+ API を ON にしています。

クリップボードアクセスについて

エミュレータでログインする場合、ユーザーの入力を省略する為に、アプリケーションでユーザ文字列をクリップボードにセットしています。その際、ClipboardManager を使うわけですが、android.content.ClipboardManager を使っています。もうひとつ、android.text.ClipboardManager がありますが、これは将来的に廃止するので android.content.ClipboardManager を使うようにと指示されます。これは、Eclipse でのチェック機能でかなり明示されますし、android.content.ClipboardManager を使っても @SuppressLint("NewApi") を記述するようにガイドされました。

WebView の JavaScript 設定

Google 側の処理で、JavaScript が必要になります。もし、JavaScript が使用できない場合はメッセージが表示されて先へ進まなくなりますので、webView.getSettings().setJavaScriptEnabled(true); を実行してからログイン画面を表示しています。

アクセストークンを POST で取得

WevView のアドレスに『仮のアクセストークン』が設定されるので、そこから取り出して正式なアクセストークンを取得する為に『Client secret』をさらにセットして呼び出します。その結果が JSON なので、その中の access_token を GSON で取得するので、プロジェクトの lib フォルダには、GSON の jar を保存しておく必要があります。

Web に対するアクセスの為の AsyncTask

Android 4.0.3 でインターネットにアクセスしようとすると、たとえ SDK のメソッドであっても同期処理の場合は非同期処理として記述しなおす必要があります。その際のむ最も簡単な方法として、無名の AsyncTask のインスタンスを使用しています。

UI 処理は onPostExecute で

doInBackground は、別スレッドなので画面にアクセスはできません。UI スレッドである onPostExecute に必要なデータを return して処理する必要があります。( ジェネリック型の型パラメータの最後のパラメータの型で受け渡しの型を決定します )

※ 『ジェネリック型の型パラメータ』は Microsoft の C# の表現です / <String, Void, String>

※ 最初の型パラメータは doInBackground に対するもので、配列になります
※ 真ん中のパラメータは、doInBackground から publishProgress で投げた内容を配列で受け取ります
※ ここでは使用していないので Void にしています。

AsyncTask | Android Developers
// ****************************************************************
// アクティビティの初期処理
// ****************************************************************
@SuppressLint("NewApi")
@Override
public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);

	// ************************************************************
	// WebView のイベントを処理する
	// ************************************************************
	WebView webView = (WebView)findViewById(R.id.webView1);
	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);
				if (url.indexOf("https://plus.google.com/u/0/101280392108947207617/posts") == 0) {
					int cur = url.indexOf("=");
					String code = url.substring(cur + 1);
					Log.i("onPageFinished:", code);

					(new AsyncTask<String, Void, String>() {
						@Override
						protected String doInBackground(String... args) {
							String json_string = "";

							try {
								URL url = new URL("https://accounts.google.com/o/oauth2/token");
								// 接続オブジェクト
								HttpURLConnection http = (HttpURLConnection)url.openConnection();
								http.setConnectTimeout(30000);
								http.setReadTimeout(30000);
								http.setDoInput(true);
								http.setRequestMethod("POST");

								// ****************************************************
								// 送信部分
								// ****************************************************
								OutputStreamWriter osw =
									new OutputStreamWriter(http.getOutputStream());
								BufferedWriter bw = new BufferedWriter(osw);
								
								// 送信データ部分
								String client_id = "";
								String client_secret = "";
								String redirect_uri = "https://plus.google.com/u/0/101280392108947207617/posts";
								
								String body = "";
								body = "code=" + args[0];
								body += "&grant_type=authorization_code";
								body += "&redirect_uri=" + redirect_uri;
								body += "&client_id=" + client_id;
								body += "&client_secret=" + client_secret;
								bw.write( body );
		
								bw.close();
								osw.close();
										
								// ****************************************************
								// 受信部分
								// ****************************************************
								// UTF-8 でリーダーを作成
								InputStreamReader isr = new InputStreamReader(http.getInputStream(), "UTF-8");
								
								// 行単位で読み込む為の準備   
								BufferedReader br = new BufferedReader(isr);   
								String line_buffer;   
								// BufferedReader は、readLine が null を返すと読み込み終了   
								while ( null != (line_buffer = br.readLine() ) ) {   
									// コマンドプロンプトに表示   
									json_string += line_buffer;
								}
					 
								br.close();
								isr.close();
		
								http.disconnect();
							}
							catch (Exception e) {
								e.printStackTrace();
							}
							
							// アクセストークン
							return json_string;
						}
						@Override
						protected void onPostExecute(String result) {

							// パーサーを取得
							JsonParser jp = new JsonParser();
							// 文字列をパース
							JsonElement je = jp.parse(result);

							// key と value を取得する為に、JsonObject から、entrySet メソッドを実行
							Set<Map.Entry<String, JsonElement>> entrySet = je.getAsJsonObject().entrySet();

							// イテレータを取得
							Iterator<Map.Entry<String, JsonElement>> it = entrySet.iterator();

							// 一覧表示
							while(it.hasNext())
							{
								Map.Entry<String, JsonElement> entry = it.next();
								
								String key = entry.getKey();
								JsonElement value = entry.getValue();
								
								if ( key.equals("access_token") ) {
									EditText editText = (EditText)findViewById(R.id.editText1);
									editText.setText(value.getAsString());
									break;
								}
							}			
						}
					}).execute(code);
					
				}
				// true を指定すると、リダイレクト処理をしなくなる
				// false でリダイレクトの場合、ここが二度よばれてから onPageFinished が呼ばれる
			    return false;
			}
		}
	);		

	String client_id = "";
	String loginUrl = "https://accounts.google.com/o/oauth2/auth";
	String type = "code";
	String redirect_uri = "https://plus.google.com/u/0/101280392108947207617/posts";
	String scope = "https://www.googleapis.com/auth/calendar+https://www.googleapis.com/auth/calendar.readonly+https://www.googleapis.com/auth/plus.me";

	String access_url = "";

	access_url = loginUrl;
	access_url += "?response_type=" + type;
	access_url += "&client_id=" + client_id;
	access_url += "&redirect_uri=" + redirect_uri;
	access_url += "&scope=" + scope;

	// ログイン画面
	webView.getSettings().setJavaScriptEnabled(true);
	webView.loadUrl(access_url);
	
	ClipboardManager clipboardManager = (ClipboardManager)this.getSystemService(CLIPBOARD_SERVICE);
	
	//クリップボードに格納するItemを作成
	ClipData.Item item = new ClipData.Item("ユーザID");

	// データの種類
	String[] mimeType = new String[1];
	mimeType[0] = ClipDescription.MIMETYPE_TEXT_PLAIN;
	 
	//クリップボードに格納するClipDataオブジェクトの作成
	ClipData cd = new ClipData(new ClipDescription("text_data", mimeType), item);
	
	clipboardManager.setPrimaryClip(cd);

}


タグ:android java
posted by lightbox at 2012-11-28 17:14 | Android | このブログの読者になる | 更新情報をチェックする

2012年11月21日


Eclipse+Android(4.0.3) のメニューの処理

※ Android の最新(2012年6月末)の開発環境に関して

SkyDrive へ移動


普通にプロジェクトを作成すると以下のようになります。メニューの XML は最初から作成されていて、メニューが作成される最初のイベント(onCreateOptionsMenu)も作成されていますので、onOptionsItemSelected を追加して、メニューの処理を実装します。

▼ ifRoom フラグの意味
それのための余地がある場合にのみ、アクションバーでこの項目を配置します。



※ この設定により、以降のメニュー位置になります


Google、AndroidのMenuボタンに“告別
Googleは既存アプリのMenuボタンが担っていた機能を、「ActionBar」クラスを使って組み込み直すよう勧めている。
package com.example.androidapp;

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {

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

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }
    
    @Override
	public boolean onOptionsItemSelected(MenuItem item) {
		
		// 選択されたメニュー項目の ID
		int menuId = item.getItemId();
		// 選択されたメニュー項目に表示されているテキスト
		String menuText = item.getTitle().toString();
		// 画面上のテキストフィールド
		EditText editText = (EditText)findViewById(R.id.editText1);
		// 画面上画像表示用コントロール
		ImageView imageView = (ImageView)findViewById(R.id.imageView1);
		
		switch(menuId){
		case R.id.menu_action1:
			Log.i("DEBUG:", "処理1が選択されました");
			// 実際の番号でアイコンリソースを指定
			imageView.setImageDrawable(getResources().getDrawable(17301555));
			
			break;
		case R.id.menu_action2:
			Log.i("DEBUG:", "処理2が選択されました");
			// 定義IDでアイコンリソースを指定
			imageView.setImageDrawable(getResources().getDrawable(android.R.drawable.ic_menu_add));
			break;
			
		case R.id.menu_post:
			// 画面遷移用
			Log.i("DEBUG:", "投稿画面へ移動します");
			break;
		}

		// 選択されたメニュー項目に表示されているテキストを
		// テキストフィールドに表示
		editText.setText(menuText);

		return true;
	}	
}

ここでは、『投稿』で画面遷移を想定しています。17301555 は、android.R.drawable.ic_menu_add実際の値です。



画面定義
<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" >

    <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:padding="20px"
        android:text="CTRL+F12でエミュレータを横にすると、メニューにテキストが付加されます" />

    <EditText
        android:id="@+id/editText1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/textView1"
        android:ems="10" />

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="48px"
        android:layout_height="48px"
        android:layout_below="@+id/editText1"
        android:layout_marginTop="24dp"
        android:src="@drawable/ic_action_search" />

</RelativeLayout>



メニュー項目定義
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/menu_post"
          android:icon="@android:drawable/ic_menu_save"
          android:title="投稿"
          android:showAsAction="ifRoom|withText" />
    <item android:id="@+id/menu_action1"
        android:title="処理1"
        android:orderInCategory="100"
        android:showAsAction="never" />
    <item android:id="@+id/menu_action2"
        android:title="処理2"
        android:orderInCategory="100"
        android:showAsAction="never" />
</menu>

android:drawable の android は、package を意味しています。ic_menu_save 以外もたくさん定義されています。

関連する Android ドキュメント

Action Bar

関連する記事

android-binding を使って Windows C#(XAML) のようなバインド処理の実装



posted by lightbox at 2012-11-21 23:48 | Android | このブログの読者になる | 更新情報をチェックする

Google 拡張の『Screen Capture』のツールバーからのポップアップメニューが表示されないバグの対処方法とついでに日本語化する方法。



上のような状態になって、表示されているキャプチャされた画像の下にポップアップメニューが隠れてしまいます。(v5.0.5)

原因は、z-index が指定されていないだけなので、まず、以下のフォルダにアクセスします。
(ユーザ名はログインしているユーザ名です)
C:\Users\ユーザ名\AppData\Local\Google\Chrome\User Data\Default\Extensions\cpngackimfmofbokmjmljamhdncknpmg\5.0.5_0
(※ これは Windows 7 です)

そして、showimage.css を開いて .toolbar に z-index: 10000; のように大きな値を設定して、ツールバーが手前になるようにします。
.toolbar {
  position:fixed;
  height: 30px;
  margin-left: 10px;
  padding-top: 6px;
  z-index: 10000;
}

保存後はいったん Google Chrome を終了させれば正しく動くようになります。そこでついでに『日本語化』します。これは、messages.json というファイルなので以下からダウンロードして下さい。

SkyDrive へ移動


このファイルを _locales フォルダの中に ja フォルダを作成して その中にコピーして下さい。その後はやはり Google Chrome をいったん終了する必要があります。







posted by lightbox at 2012-11-21 16:15 | Google Chrome | このブログの読者になる | 更新情報をチェックする

2012年11月19日


Windows8(C#)からFacebook へ画像をアップロードする。

▼ 実行画面

ログイン > 画像表示 > 画像投稿
( WEB カメラの画像も投稿できます )

SkyDrive へ移動


実行するには、Facebook での APIキーが必要になります。MainPage.xaml.cs の先頭に 『public String apiKey = "";』とありますので変更して下さい。
先にログインしてアクセストークンを取得する必要がありますが、サンプルコードに実装してあります。この処理で取得するのは2時間使用可能なアクセストークンになります。60日間に変更するには、手動でも可能なのでこちらを参考にして下さい(一旦60日に変更すると、この処理で取得しても60日になるようです)。

ファイルのアップロードは、通常の仕様通りですが、具体的なフォーマットをソースコードで知りたい場合は、『Windows Phone から Facebook に画像アップロード』を参照するか、実行時にローカルのWEBサーバーに対して実行してダンプしてみて下さい。AN HTTPD ならば、トレースログを有効にすると見る事ができます。

ファイルのアップロードには、画像データのバイト配列が必要になりますが、その読み込み部分もサンプルに実装されています。具体的には、StorageFile から IBuffer を作成して、 CryptographicBuffer.CopyToByteArray メソッドを実行するのが最も簡単です( 他にも方法はあります )

HTTP の仕様(マルチパートを使用する方法)に合わせてデータを作る方法としては、System.Net.Http(HttpClient の名前空間)に全て用意されていました。仕様をご存じな方であれば非常に簡単に実装できる事がお解りになると思います。

これらの処理は全て本来非同期ですが、Windows8 の非同期処理の実装方法によってイベント等を記述する必要がなくなっています。この実装の概要に関しては、『Windows8 の非同期処理(C#)』を参照して下さい。
try
{
	HttpClient httpClient = new HttpClient();
	httpClient.MaxResponseContentBufferSize = int.MaxValue;
	httpClient.DefaultRequestHeaders.ExpectContinue = false;

	MultipartFormDataContent form = new MultipartFormDataContent();
	StringContent sc = new StringContent(PostBody.Text);
	sc.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("post-data");
	sc.Headers.ContentDisposition.Name = "message";
	form.Add(sc, "message");

	var fileContent = new System.Net.Http.ByteArrayContent(data);
	fileContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("form-data");
	fileContent.Headers.ContentDisposition.FileName = "imagefile.jpg";
	fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/jpeg");
	fileContent.Headers.ContentDisposition.Name = "source";
	form.Add(fileContent, "source");

	HttpResponseMessage response = await httpClient.PostAsync(api_base_url+"me/photos?access_token=" + accessToken, form);
	String text = await response.Content.ReadAsStringAsync();

	Debug.WriteLine(text);

	MessageDialog msg = new MessageDialog("投稿が終了しました");

	msg.Commands.Add(new UICommand(
		"確認",
		new UICommandInvokedHandler(this.CommandInvokedHandler)));

	// Set the command that will be invoked by default
	msg.DefaultCommandIndex = 0;

	// Set the command to be invoked when escape is pressed
	msg.CancelCommandIndex = 1;

	// ダイアログ表示
	await msg.ShowAsync();

}
catch (Exception Err)
{
	throw;
}

参考

Uploading a file with Windows 8 WinRT -
How-To: Use the Graph API to Upload Photos to a user’s profile - Facebook開発者

関連する記事

Windows Phone から Facebook に画像アップロード
HTML(ローカル) を使用して Facebook へ画像をアップロードする
手動で Facebook API の 60日間の アクセストークンを取得する



タグ:Windows8 C# Facebook
posted by lightbox at 2012-11-19 19:16 | Win8 ストアアプリ | このブログの読者になる | 更新情報をチェックする

2012年11月11日


Windows Phone から Facebook に画像アップロード

SkyDrive に移動


アクセストークンは取得したという前提での処理です。アクセストークンは URL に含ませる仕様になっており、メッセージと画像データは一般的なものです。プライバシーの設定についてはまだ良く解りませんが、テスト後は友人のみ公開となっていました。

※ SkyDrive にはプロジェクト全体を書庫としてダウンロードできるようにしています。
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Media.Imaging;
using System.IO;
using System.Text;

namespace WebPost
{
	public class ControlImageUpload
	{
		private WriteableBitmap _wb;

		// イベント引き渡し用クラス
		private class MyParam
		{
			public WebRequest wr;
			public MemoryStream ms;
			public string strBoundary;
			public string file_name;

			public MyParam()
			{
				wr = null;
				ms = null;
				strBoundary = null;
				file_name = null;
			}
		}

		// コンストラクター
		public ControlImageUpload( WriteableBitmap wb)
		{
			_wb = wb;
		}

		public void UploadJpg( string url, string file_name, int quality=85 )
		{

			string strBoundary = DateTime.Now.Ticks.ToString("x");

			MemoryStream stream = new MemoryStream();
			_wb.SaveJpeg(stream, _wb.PixelWidth, _wb.PixelHeight, 0, quality);

			WebRequest wr = HttpWebRequest.Create(url);

			wr.Method = "POST";
			wr.ContentType = "multipart/form-data; boundary=---" + strBoundary;

			AsyncCallback writeCallBack = new AsyncCallback(WriteCallBack);
			MyParam myParam = new MyParam() {
				wr = wr,
				ms = stream,
				strBoundary = strBoundary,
				file_name = file_name
			};

			// 要求開始
			IAsyncResult iar1 = wr.BeginGetRequestStream(writeCallBack, myParam);
		}

		// 書き込み
		private void WriteCallBack(IAsyncResult ar)
		{
			HttpWebRequest req = (HttpWebRequest)((MyParam)ar.AsyncState).wr;
			MemoryStream stream = ((MyParam)ar.AsyncState).ms;
			string strBoundary = ((MyParam)ar.AsyncState).strBoundary;
			string file_name = ((MyParam)ar.AsyncState).file_name;

			Stream rs = req.EndGetRequestStream(ar);

			BinaryWriter sw = new BinaryWriter(rs);

			Encoding encoding = Encoding.GetEncoding("iso-8859-1");
			Byte[] content = encoding.GetBytes("-----" + strBoundary + "\r\n");
			sw.Write(content, 0, content.Length);

			content = encoding.GetBytes("Content-Disposition: post-data; name=\"message\"\r\n\r\n");
			sw.Write(content, 0, content.Length);

			Encoding encoding_utf8 = Encoding.GetEncoding("utf-8");
			content = encoding_utf8.GetBytes("Windows Phone からアップロード\r\n");
			sw.Write(content, 0, content.Length);

			content = encoding.GetBytes("-----" + strBoundary + "\r\n");
			sw.Write(content, 0, content.Length);

			content = encoding.GetBytes("Content-Disposition: form-data; name=\"source\"; filename=\"" + file_name + "\"\r\n");
			sw.Write(content, 0, content.Length);

			content = encoding.GetBytes("Content-Type: image/jpeg\n");
			sw.Write(content, 0, content.Length);

			content = encoding.GetBytes("\r\n");
			sw.Write(content, 0, content.Length);

			sw.Write(stream.GetBuffer(), 0, stream.GetBuffer().Length);

			content = encoding.GetBytes("\r\n-----" + strBoundary + "--\r\n");
			sw.Write(content, 0, content.Length);

			sw.Close();

			AsyncCallback readCallBack = new AsyncCallback(this.ReadCallBack);
			IAsyncResult iar2 = req.BeginGetResponse(readCallBack, req);

		}

		// 読み込み
		private void ReadCallBack(IAsyncResult ar)
		{

			HttpWebRequest req = (HttpWebRequest)ar.AsyncState;
			HttpWebResponse response = (HttpWebResponse)req.EndGetResponse(ar);

			Encoding enc = System.Text.Encoding.GetEncoding("UTF-8");
			StreamReader sr = new StreamReader(response.GetResponseStream(), enc);

			string str = sr.ReadToEnd();
			sr.Close();

			// 完了後の UI スレッドへのアクセス
			Deployment.Current.Dispatcher.BeginInvoke(() =>
			{
				// 内部のイベントの呼び出し
				OnImageUploadResult(new ImageUploadArgs(str));
			});
		}

		// カスタムイベント用引数
		public class ImageUploadArgs : EventArgs
		{
			public ImageUploadArgs(string s)
			{
				message = s;
			}
			private string message;

			public string Message
			{
				get { return message; }
				set { message = value; }
			}
		}

		// カスタムイベントハンドラの定義 ( EventHandler<T> )
		public event EventHandler<ImageUploadArgs> ImageUploadResult;

		// 外部へイベントを発行する為の内部メソッドの定義
		protected virtual void OnImageUploadResult(ImageUploadArgs e)
		{
			EventHandler<ImageUploadArgs> handler = ImageUploadResult;

			// イベントが外部で実装されている場合、そのイベントを呼び出す
			if (handler != null)
			{
				handler(this, e);
			}
		}
	}
}

関連する記事

手動で Facebook API の 60日間の アクセストークンを取得する
Windows Phone からカメラで撮影した画像を http でアップロードする
Window Phone 内の画像ファイルを選択して別の画像ファイルとしてメディアライブラリーに保存


タグ:C# Windows Phone
posted by lightbox at 2012-11-11 04:06 | Windows Phone | このブログの読者になる | 更新情報をチェックする

WebClient で2時間用アクセストークンを取得して60日間用に交換してFacebook に投稿する Windows Phone 用のコード

SkyDrive に移動
手動で Facebook API の 60日間の アクセストークンを取得する』を WebClient で記述したものです。実際は最後にユーザ情報を取得して、画面にユーザのプロファイル画像を表示しています。

最後のユーザ情報は JSON で帰って来るので、Json.NET のライブラリを使用しています。

※ アクセストークンが取得できてしまえば、投稿はただの単純な POST です。

アプリケーションとして実装していませんが、アクセストークンの期限が切れると投稿エラーになるので、その場合はログイン画面に移動して ApplicationSettings をクリアする必要があります。
using System;
using System.Collections.Generic;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;

using System.Diagnostics;

using System.Windows;
using System.Net;
using Newtonsoft.Json.Linq;

namespace WP_C_Sharp_Facebook1
{
	public partial class Login : PhoneApplicationPage
	{
	private WebClient webClient = new WebClient();
		private WebClient webClient_me = new WebClient();

		public Login()
		{
			InitializeComponent();

			string url_target = String.Format(
				"https://www.facebook.com/dialog/oauth/?redirect_uri=http://www.facebook.com/connect/login_success.html&response_type=token&client_id={0}&scope=user_about_me,user_photos,read_stream,publish_stream",
				App.mainPage.ApplicationID);

			// 認証ページへ遷移
			webBrowser.Navigate(new Uri(url_target));
		}

		private void webBrowser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
		{
			Debug.WriteLine("webBrowser_Navigated: {0}", e.Uri);

			string url = e.Uri.ToString();
			if (url.IndexOf("access_token") != -1)
			{
				Debug.WriteLine(url);
				string target_line = url.Substring(url.IndexOf("access_token"));
				string [] separators = new string[1];
				separators[0] = "&";
				string[] part_string = target_line.Split(separators, System.StringSplitOptions.RemoveEmptyEntries);

				Dictionary<string,string> dic = new Dictionary<string,string>();
				separators[0] = "=";
				string[] part_values = null;
				part_values = part_string[0].Split(separators, System.StringSplitOptions.RemoveEmptyEntries);
				dic.Add(part_values[0], part_values[1]);
				part_values = part_string[1].Split(separators, System.StringSplitOptions.RemoveEmptyEntries);
				dic.Add(part_values[0], part_values[1]);


		    webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient_DownloadStringCompleted);
				url = String.Format("https://graph.facebook.com/oauth/access_token?client_id={0}&client_secret={1}&grant_type=fb_exchange_token&fb_exchange_token={2}",
					App.mainPage.ApplicationID,
					App.mainPage.ApplicationSecret,
					dic["access_token"]);

				webClient.DownloadStringAsync(new Uri(url));
       		}

		}

	private void webClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
	{
	    if (e.Error != null)
	    {
		Deployment.Current.Dispatcher.BeginInvoke(() =>
		{
		    Debug.WriteLine(e.Error.Message);
		});
	    }
	    else
	    {
		// プロパティにセット
		string new_token_line = e.Result;
				Debug.WriteLine(new_token_line);

				string[] separators = new string[1];
				separators[0] = "&";
				string[] part_string = new_token_line.Split(separators, System.StringSplitOptions.RemoveEmptyEntries);

				Dictionary<string, string> dic = new Dictionary<string, string>();
				separators[0] = "=";
				string[] part_values = null;
				part_values = part_string[0].Split(separators, System.StringSplitOptions.RemoveEmptyEntries);
				dic.Add(part_values[0], part_values[1]);
				part_values = part_string[1].Split(separators, System.StringSplitOptions.RemoveEmptyEntries);
				dic.Add(part_values[0], part_values[1]);

				Debug.WriteLine(dic["access_token"]);

				App.isolatedStore.Add("access_token", dic["access_token"]);

				webClient_me.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient_me_DownloadStringCompleted);
				string url = String.Format("https://graph.facebook.com/me?access_token={0}",
					dic["access_token"]);

				webClient_me.DownloadStringAsync(new Uri(url));
			}
	}

		private void webClient_me_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
		{
			if (e.Error != null)
			{
				Deployment.Current.Dispatcher.BeginInvoke(() =>
				{
					Debug.WriteLine(e.Error.Message);
				});
			}
			else
			{
				Debug.WriteLine(e.Result);

				JObject me = JObject.Parse(e.Result);

				App.isolatedStore.Add("fb_name", me["name"].ToString());
				App.isolatedStore.Add("fb_id", me["id"].ToString());
				App.isolatedStore.Add(
						"fb_image_url",
						string.Format("https://graph.facebook.com/{0}/picture", me["id"].ToString())
						);

				Dispatcher.BeginInvoke(() =>
				{
					// 全ての処理が終わったのでメインページへ戻る
					this.NavigationService.GoBack();

				});
			}
		}

	}
}
最初、Facebook SDK を使ってたんですが、ただログインして投稿するだけならコードは少し長くなりますが、する事は全く同じなので、全て自前のコードで記述しました。



タグ:C# Windows Phone
posted by lightbox at 2012-11-11 01:31 | Windows Phone | このブログの読者になる | 更新情報をチェックする

2012年11月10日


Windows Phone からカメラで撮影した画像を http でアップロードする

SkyDrive へ移動


カメラで撮影した画像を WEB 上へアップロードするアプリケーションですが、アップロード部分はクラス化しているのですぐ利用可能だと思います。

エミュレータでは、カメラで撮影はダミー画像になりますが、実機の場合 Zune で接続していると、写真や音楽系のデバッグができないので以下のようにします。

1) Zune でデバイスとPCを接続して同期接続にする
2) Zune を終了させる

C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v7.1\Tools\WPConnect\x64\WPConnect.exe または C:\Program Files\Microsoft SDKs\Windows Phone\v7.1\Tools\WPConnect\x86\WPConnect.exe を実行する

ControlImageUpload クラス

画像をアップロードする為のクラスです。コンストラクタを二つ用意していますが、一つは JPEG フォーマットのストリームを渡して、Windows Phone 内での通常の画像を扱うのに使用します。もう一つは、WriteableBitmap を渡しますが、これは画面上のコントロールを WriteableBitmap(コントール名, null) でそのまま取得したものを内部で JPEG 化してアップロードしています。

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Media.Imaging;
using System.IO;
using System.Text;

namespace WebPost
{
	public class ControlImageUpload
	{
		private WriteableBitmap _wb;
		MemoryStream stream = new MemoryStream();

		// イベント引き渡し用クラス
		private class MyParam
		{
			public WebRequest wr;
			public MemoryStream ms;
			public string strBoundary;
			public string file_name;

			public MyParam()
			{
				wr = null;
				ms = null;
				strBoundary = null;
				file_name = null;
			}
		}

		// コンストラクター
		public ControlImageUpload(WriteableBitmap wb, int quality = 85 )
		{
			_wb = wb;
			_wb.SaveJpeg(stream, _wb.PixelWidth, _wb.PixelHeight, 0, quality);
		}
		public ControlImageUpload(Stream img_stream)
		{
			img_stream.CopyTo(stream);
		}

		public void UploadJpg( string url, string file_name )
		{

			string strBoundary = DateTime.Now.Ticks.ToString("x");

			WebRequest wr = HttpWebRequest.Create(url);

			wr.Method = "POST";
			wr.ContentType = "multipart/form-data; boundary=---" + strBoundary;

			AsyncCallback writeCallBack = new AsyncCallback(WriteCallBack);
			MyParam myParam = new MyParam() {
				wr = wr,
				ms = stream,
				strBoundary = strBoundary,
				file_name = file_name
			};

			// 要求開始
			IAsyncResult iar1 = wr.BeginGetRequestStream(writeCallBack, myParam);
		}

		// 書き込み
		private void WriteCallBack(IAsyncResult ar)
		{
			HttpWebRequest req = (HttpWebRequest)((MyParam)ar.AsyncState).wr;
			MemoryStream stream = ((MyParam)ar.AsyncState).ms;
			string strBoundary = ((MyParam)ar.AsyncState).strBoundary;
			string file_name = ((MyParam)ar.AsyncState).file_name;

			Stream rs = req.EndGetRequestStream(ar);

			BinaryWriter sw = new BinaryWriter(rs);

			Encoding encoding = Encoding.GetEncoding("iso-8859-1");
			Byte[] content = encoding.GetBytes("-----" + strBoundary + "\n");
			sw.Write(content, 0, content.Length);

			content = encoding.GetBytes("Content-Disposition: form-data; name=\"target\"; filename=\""+file_name+"\"\n");
			sw.Write(content, 0, content.Length);

			content = encoding.GetBytes("Content-Type: image/jpeg\n");
			sw.Write(content, 0, content.Length);

			content = encoding.GetBytes("\n");
			sw.Write(content, 0, content.Length);

			sw.Write(stream.GetBuffer(), 0, stream.GetBuffer().Length);

			sw.Write("\n-----" + strBoundary + "--\n");

			sw.Close();

			AsyncCallback readCallBack = new AsyncCallback(this.ReadCallBack);
			IAsyncResult iar2 = req.BeginGetResponse(readCallBack, req);

		}

		// 読み込み
		private void ReadCallBack(IAsyncResult ar)
		{

			HttpWebRequest req = (HttpWebRequest)ar.AsyncState;
			HttpWebResponse response = (HttpWebResponse)req.EndGetResponse(ar);

			Encoding enc = System.Text.Encoding.GetEncoding("UTF-8");
			StreamReader sr = new StreamReader(response.GetResponseStream(), enc);

			string str = sr.ReadToEnd();
			sr.Close();

			// 完了後の UI スレッドへのアクセス
			Deployment.Current.Dispatcher.BeginInvoke(() =>
			{
				// 内部のイベントの呼び出し
				OnImageUploadResult(new ImageUploadArgs(str));
			});
		}

		// カスタムイベント用引数
		public class ImageUploadArgs : EventArgs
		{
			public ImageUploadArgs(string s)
			{
				message = s;
			}
			private string message;

			public string Message
			{
				get { return message; }
				set { message = value; }
			}
		}

		// カスタムイベントハンドラの定義 ( EventHandler<T> )
		public event EventHandler<ImageUploadArgs> ImageUploadResult;

		// 外部へイベントを発行する為の内部メソッドの定義
		protected virtual void OnImageUploadResult(ImageUploadArgs e)
		{
			EventHandler<ImageUploadArgs> handler = ImageUploadResult;

			// イベントが外部で実装されている場合、そのイベントを呼び出す
			if (handler != null)
			{
				handler(this, e);
			}
		}
	}
}

関連する記事

Windows Phone の実行画面(自分のアプリのみ)をJPG化してWEBにPOSTするサンプル


タグ:Windows Phone C#
posted by lightbox at 2012-11-10 22:05 | Windows Phone | このブログの読者になる | 更新情報をチェックする

Window Phone 内の画像ファイルを選択して別の画像ファイルとしてメディアライブラリーに保存



カメラで撮影した画像も含めて、PhotoChooserTask で読みだす事ができますが、他の操作に使う場合は Memory Stream にコピーする必要も出て来るので、その方法を介した保存方法と二段構えのサンプルです。
( 一応、画像は JPEG 形式であるという前提です )

単に選択した画像を XAML の画面に表示したいだけならば、e.ChosenPhoto を BitmapImage にして Source プロパティにセットします。

SavePicture メソッドは、Picture オブジェクトなので、GetImage メソッドで再びストリームを取得できます。( GetThumbnail メソッドもあります )

SavePicture に渡されるストリームは、JPEG 形式である必要があるので、BitmapImage の SetSource メソッドで渡すストリームも JPEG 形式で問題無いようです。

※ CameraCaptureTask で撮影した画像のストリームも JPEG 形式です
public partial class MainPage : PhoneApplicationPage
{
	PhotoChooserTask pct = new PhotoChooserTask();

	public MainPage()
	{
		InitializeComponent();

		pct.Completed += new EventHandler<PhotoResult>(task_Completed);
		pct.ShowCamera = true;
	}

	private void btnLoad_Click(object sender, EventArgs e)
	{
		// 画像選択
		pct.Show();
	}

	void task_Completed(object sender, PhotoResult e)
	{
		if (e.TaskResult != TaskResult.OK)
		{
			return;
		}

		// Microsoft.Xna.Framework.Media
		// ※ メディアライブラリーにストリームから直接保存
		using (MediaLibrary ml = new MediaLibrary())
		{
			Picture pic = ml.SavePicture("DirectPictureSave1_" + DateTime.Now.ToString("yyyyMMddhhmmss"), (Stream)e.ChosenPhoto);
		}

		// メモリストリームにコピーしてから保存
		MemoryStream ms = new MemoryStream();
		((Stream)e.ChosenPhoto).Seek(0, SeekOrigin.Begin);	// Stream を最初に
		((Stream)e.ChosenPhoto).CopyTo(ms);	// Memory Stream にコピー

		// Microsoft.Xna.Framework.Media
		// ※ メディアライブラリーに保存
		using (MediaLibrary ml = new MediaLibrary())
		{
			Picture pic = ml.SavePicture("DirectPictureSave2_" + DateTime.Now.ToString("yyyyMMddhhmmss"), ms.GetBuffer());
			// 選択した画像を表示する
			BitmapImage bmp = new BitmapImage();
//				bmp.SetSource(pic.GetThumbnail());
			// Picture オブジェクトより、ストリームを取得
			bmp.SetSource(pic.GetImage());
			imgPhoto.Source = bmp;

			MessageBox.Show("保存しました");
		}

	}

}

JPEG のストリームを デコードする必要がある場合は
WriteableBitmap wb = PictureDecoder.DecodeJpeg(ストリーム);
として WriteableBitmap を取得する事ができます


タグ:C# Windows Phone
posted by lightbox at 2012-11-10 19:37 | Windows Phone | このブログの読者になる | 更新情報をチェックする

2012年11月01日


Windows Phone の実行画面(自分のアプリのみ)をJPG化してWEBにPOSTするサンプル

この画面をアップロードします。



POST された画像はこちらです。



ごく普通の Boundary を使ったバイナリアップロードです。
最後に、UI スレッドへ結果を通知する為に Deployment.Current.Dispatcher.BeginInvoke を使用しています。

Windows Phone まとめリンク

Windows Phone

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;

using System.Diagnostics;
using System.IO;
using System.Windows.Media.Imaging;
using System.Text;
using System.Threading;
using System.Windows.Threading;

namespace WebPost
{
	public partial class MainPage : PhoneApplicationPage
	{

		// コンストラクター
		public MainPage()
		{
			InitializeComponent();
		}

		// イベント引き渡し用クラス
		private class MyParam
		{
			public WebRequest wr;
			public MemoryStream ms;
			public string strBoundary;

			public MyParam()
			{
				wr = null;
				ms = null;
				strBoundary = null;
			}
		}

		IAsyncResult iar2 = null;

		// 書き込み
		public void WriteCallBack(IAsyncResult ar)
		{
			Debug.WriteLine("WriteCallBack");

			HttpWebRequest req = (HttpWebRequest)((MyParam)ar.AsyncState).wr;
			MemoryStream stream = ((MyParam)ar.AsyncState).ms;
			string strBoundary = ((MyParam)ar.AsyncState).strBoundary;

			Stream rs = req.EndGetRequestStream(ar);

			BinaryWriter sw = new BinaryWriter(rs);

			Encoding encoding = Encoding.GetEncoding("iso-8859-1");
			Byte[] content = encoding.GetBytes("-----"+strBoundary+"\n");
			sw.Write( content,0 , content.Length );

			content = encoding.GetBytes("Content-Disposition: form-data; name=\"target\"; filename=\"LayoutRoot.jpg\"\n");
			sw.Write( content,0 , content.Length );

			content = encoding.GetBytes("Content-Type: image/jpeg\n");
			sw.Write( content,0 , content.Length );

			content = encoding.GetBytes("\n");
			sw.Write( content,0 , content.Length );

			sw.Write(stream.GetBuffer(), 0, stream.GetBuffer().Length);

			sw.Write("\n-----" + strBoundary + "--\n");

			sw.Close();

			AsyncCallback readCallBack = new AsyncCallback(this.ReadCallBack);
			IAsyncResult iar2 = req.BeginGetResponse(readCallBack, req);

		}


		// 読み込み
		public void ReadCallBack(IAsyncResult ar)
		{

			HttpWebRequest req = (HttpWebRequest)ar.AsyncState;
			HttpWebResponse response = (HttpWebResponse)req.EndGetResponse(ar);

			Encoding enc = System.Text.Encoding.GetEncoding("UTF-8");
			StreamReader sr = new StreamReader(response.GetResponseStream(), enc);

			string str = sr.ReadToEnd();
			Debug.WriteLine(str);
			sr.Close();

			// 完了後の UI スレッドへのアクセス
			Deployment.Current.Dispatcher.BeginInvoke(() =>
			{
				MessageBox.Show("送信しました");
			});

		}

		// 送信
		private void button1_Click_1(object sender, RoutedEventArgs e)
		{
			string strBoundary = DateTime.Now.Ticks.ToString("x");

			WriteableBitmap wb = new WriteableBitmap(LayoutRoot, null);
			MemoryStream stream = new MemoryStream();
			wb.SaveJpeg(stream, wb.PixelWidth, wb.PixelHeight, 0, 85);

			WebRequest wr = HttpWebRequest.Create("http://localhost/php_json/up.php");

			wr.Method = "POST";
			wr.ContentType = "multipart/form-data; boundary=---" + strBoundary;

			AsyncCallback writeCallBack = new AsyncCallback(WriteCallBack);
			MyParam myParam = new MyParam() { wr = wr, ms = stream, strBoundary = strBoundary };

			IAsyncResult iar1 = wr.BeginGetRequestStream(writeCallBack, myParam);

		}


	}
}
関連する記事

VBScript を使って HTTPプロトコルで PHP へファイルをアップロードする方法

ファイルのアップロード時のデータのダンプ


▼ 受け取り側の PHP です
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; CHARSET=utf-8">
</HEAD>
<BODY>
<FORM 
	enctype="multipart/form-data"
	method="POST"
>
 
	アップロードするファイル : 
	<INPUT name="target" type="file" style='width:400'>
	<INPUT type="submit" value="アップロード">
 
</FORM>
<PRE>
<?
if ( $_SERVER['REQUEST_METHOD'] == "POST" ) {
 
	$upload = realpath("./");
	$upload .= ( DIRECTORY_SEPARATOR . $_FILES['target']['name'] );

	print $upload;

	if ( move_uploaded_file(
		$_FILES['target']['tmp_name'], $upload ) ) {
		print "アップロードに成功しました<br>\n";
	}

	print_r( $_FILES );
}

?>
</PRE>
</BODY></HTML>



posted by lightbox at 2012-11-01 23:45 | Windows Phone | このブログの読者になる | 更新情報をチェックする
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 ドロップシャドウの参考デモ
Google Hosted Libraries
cdnjs
BUTTONS (CSS でボタン)
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり