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

2012年4月28日土曜日

Windows8 でのデータ保存

さてGoogle+に対してのOAuthアクセスは作成しましたが、
アクセストークン等のアプリケーションデータを保存しよーと思ったら、
WindowsPhone的なIsolatedStorageFileStreamがない事に気付きました。

そう言われるとデータベースとか、スマートフォン系の特殊なアクセスばっかりで
「設定ファイル」何かを作ってデータを永続化してないなぁと思ったので
設定ファイルを作ってみる事にした。

シリアライズ可能なオブジェクト
シリアライズ可能なクラスを作成します。
        private static Settings _settings;
        public static Settings Settings {
            get { return _settings; }
        }
私はAppにstaticなメンバを作成しておきました。 この中にアクセストークンのデータを残すようにしておきます。 ※アクセストークン(string)、リフレッシュトークン(string)、有効期限(現在時刻から足し込んだDateTime)
データの保存
データの保存は以下の通りで行いました。
        async static private Task<StorageFile> GetSettingFile()
        {
            var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
            StorageFile file = await folder.CreateFileAsync("settings.xml",CreationCollisionOption.OpenIfExists);
            return file;
        }

        async static public void SaveSetting()
        {
            var file = await GetSettingFile();
            var serial = new XmlSerializer(typeof(Settings));
            using ( var tWriter = new StringWriter() ) {
                serial.Serialize(tWriter, _settings);
                using (var stream = await file.OpenAsync(FileAccessMode.ReadWriteUnsafe))
                {
                    var outputStream = stream.GetOutputStreamAt(0);
                    using (var writer = new DataWriter(outputStream))
                    {
                        writer.WriteString(tWriter.ToString());
                        await writer.StoreAsync();
                    }
                }
            }
        }
「Windows.ApplicationModel.Package.Current.InstalledLocation」で インストールしたディレクトリを取得する事ができます。 他にドキュメント内とかでも保存できるのですが、 他の場所に保存する場合は、パーミッション(Package.appxmanifest)が必要になるようです。 ※また機会あったら書きます。 CreateFileAsync()を使用していますが、 同期的に取得するのでawaitで呼び出しています。 第2引数の「CreationCollisionOption.OpenIfExists」は存在しない場合作成してくれます。 ※これを付けないとsettings(2).xmlとかをモリモリ作ります。 あとはSettingsオブジェクトをシリアライズして保存しています。
データの取得
        async private void LoadSetting() 
        {
            var file = await GetSettingFile();
            using (var stream = await file.OpenAsync(FileAccessMode.Read))
            {
                var inputStream = stream.GetInputStreamAt(0);
                using (var reader = new DataReader(inputStream))
                {
                    uint size = (uint)stream.Size;
                    await reader.LoadAsync(size);っd
                    string data = reader.ReadString(size);
                    if (!data.Equals(""))
                    {
                        using (var textReader = new StringReader(data))
                        {
                            XmlSerializer serial = new XmlSerializer(typeof(Settings));
                            _settings = (Settings)serial.Deserialize(textReader);
                        }
                    }
                    else
                    {
                        _settings = new Settings();
                    }
                }
            }
        }
後は同様に読み込むだけですね。
保存したデータ
<?xml version="1.0" encoding="utf-16"?>
<Settings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <AccessToken>
    <accessToken>ya29.AHES6ZQ0-H7cotFnLdVAcZ5CVz280mBarXy4tUn7-_Bs-AE</accessToken>
    <refreshToken>1/x8SHm9FaHD299fdZSNNyme2m3X2Up_Ki90pcUc2Y2Iw</refreshToken>
    <expiresIn>2012-04-28T21:48:28.55985+09:00</expiresIn>
  </AccessToken>
</Settings>
こんな感じで残ってました。 ※AccessTokenクラスで保存しています。 開発中は、 「C:\Users\(ユーザ名)\Documents\Visual Studio 11\Projects\GooglePlus\GooglePlus\bin\Debug\AppX」 って位置にありました。 リリースすると 「C:\Users\(ユーザ名)\AppxLayouts\(アプリのID)」 のようです。
感想
保存方法がかなりWindowsPhoneと違いました。 現状開発中で気になっているのは Suspendingのイベントに対して、保存処理を行ったんですけど、 開発中だからか、動作せずにStaticで他の画面から呼び出している状態になってしまいました。
何か間違ってるのかな?

await async知らなかったので呼び出した時のエラー原因がわからなかったです。

2012年4月26日木曜日

Windows8でのWebViewのスクレイピング

Windows8用アプリの開発がVisualStudio11(ベータ版)により可能になりました。
iPhoneが世間を席巻している状況下でも
現在のWindowsユーザがMacへの移行をはじめるのかは怪しいところで、
※開発者の移動はかなりありましたが。
Windows8への移行により、かなりのユーザさんが、
困惑するんじゃないかなぁ~と心配しているところではあります。
※特にまだXPを使っている人

私はWindowsPhone(というよりMetro)のかわいさに負けて、
少し開発しましたが、Microsoftの底力というか、
言語設計からなる雰囲気に少しMicrosoftに惚れ直したところでして、
メインマシンをWindows8端末にして、Metro開発でもやってみよーかなーと思っていまして。

以前、WindowsPhoneで少し開発したGoogle+アプリを
Windows8に移植しようと思い立ったわけです。

で最初のOAuth部分でWebViewに関して仕様が違ったみたいなので
以前書いた「認可コードのスクレイピング(WindowsPhone)」との違いを書きたいと思います。

2点の違い
試したのは「LoadCompletedイベント」の時なので 他のイベントの時にどうかはわかりません。 以前のコードの仕様としては 「認可後のページを開いたら、HTMLを取得して認可コードを取得」 っていう感じなのですが、 ・WebViewのUriが更新されない ・WebViewのSaveToString()がサポートされてない の2点が違います。
アクセスしているUriの取得
これはすごく単純でイベント自体のUriを取得すれば良いので
private void loginView_LoadCompleted(object sender, NavigationEventArgs e)
{
            string uri = e.Uri.AbsoluteUri;
}
とする事で回避できます。 回避というか、これが当たり前かな?
HTMLの取得方法
これはトリッキーというか正式にやっていいものなのか。。。 別の方法がある気がするのですが、私的にはHTMLがどうしても欲しかったので JavaScriptを呼び出すInvokeScript()を使ってHTMLを抜き出してみました。 eval()関数でouterHTMLを取得してみました。
string html = loginView.InvokeScript("eval", new string[] { "document.documentElement.outerHTML;" });
これでHTMLが抜き出せます。
最終的には
単純に2つのプロパティ(メソッド)での取得データを切り替えただけでOKでした。
        private void loginView_LoadCompleted(object sender, NavigationEventArgs e)
        {
            string uri = e.Uri.AbsoluteUri;
            if (uri.IndexOf("/o/oauth2/approval") != -1)
            {
                string html = loginView.InvokeScript("eval", new string[] { "document.documentElement.outerHTML;" });

                int tagStart = html.IndexOf("<textarea");
                int tagStartEnd = html.IndexOf(">", tagStart);

                int tagEnd = html.IndexOf("</textarea>");

                string pin = html.Substring(tagStartEnd + 1, tagEnd - tagStartEnd - 1);
                pinText.Text = pin;
            }  
        }
という感じのコードになり
って感じで認可コードも自動設定可能になりました。 あくまで見た感じだと WindowsPhoneの時とUIの部分で色々大変な事がありそうですけどね。