[WinRT] ストアアプリのセカンダリタイルを動的に作成する方法

Kindle Launcher の技術ネタの続きです。

クイック スタート: セカンダリ タイルのピン留め (XAML) (Windows)
http://msdn.microsoft.com/ja-jp/library/windows/apps/xaml/hh868249.aspx
WinRT/Metro TIPS:Windows 8.1の新機能、セカンダリ・タイル作成時の代替ロゴを使うには?[Windows 8.1ストア・アプリ開発] – @IT
http://www.atmarkit.co.jp/ait/articles/1312/05/news084.html

ストアアプリを登録すると、スタート画面いタイルを表示できますが、これを複数表示させることができます。複数のタイルにIDを付けておいて、いろいろな状態でアプリを起動することが可能です。
サンプルには、リソース「ms-appx:///」のタイルを表示する方法が載っていますが、実は「ms-appdata:///」も使えます(とマニュアルに書いてあります)。リソースの場合はアプリ内に含める必要があるのですが、アプリケーションデータ(ms-appdata)が使えるということは、どこからダウンロードしたり作成した画像を使えるということです。
これを Amazon の書籍の画像を使えば、スタート画面に本の画像が表示できるということです。

アプリケーションデータの扱いについては、以下を立ち読みするか、

ひと目でわかる Windowsストアアプリ開発入門VisualC#2012編
http://www.amazon.co.jp//dp/482229806X

マニュアルのほうはこっちで。

Windows ランタイムを使ったアプリ データへのアクセス (Windows ランタイム アプリ) – Windows app development
http://msdn.microsoft.com/ja-jp/library/windows/apps/hh464917.aspx

を参照してください。

動的にセカンダリタイルを作る

Kindle Launcher で使っているのがこれです。セカンダリタイルは、いちいちユーザーの確認が必要なので、一気に作ることがでいません。まあ、プログラムで一気に作るとスタート画面が「D○c○m○」で埋もれてしまいそうなので、これはこれでいいのかもしれませんが。

private async void OnButtonMakeTile(object sender, RoutedEventArgs e)
{
    var item = this.itemGridView.SelectedItem as Book;
    if (item == null) return;

    await CreateSecondaryTileAsync(item);
}

private async System.Threading.Tasks.Task<bool> CreateSecondaryTileAsync(Book item)
{
    string tileId = &quot;ASIN-&quot; + item.ASIN;
    var tile = new Windows.UI.StartScreen.SecondaryTile()
    {
        TileId = tileId,
        DisplayName = item.Title,
        Arguments = item.ASIN,
        RoamingEnabled = true,
    };
    tile.VisualElements.ForegroundText = Windows.UI.StartScreen.ForegroundText.Light;
    tile.VisualElements.ShowNameOnSquare150x150Logo = true;

    // アプリローカルに保存
    var cl = new HttpClient();
    var data = await cl.GetByteArrayAsync(new Uri(item.IconUrl));

    var folder = Windows.Storage.ApplicationData.Current.LocalFolder;
    var path = item.ASIN + &quot;.jpg&quot;;

    try
    {
        var file = await folder.CreateFileAsync(path);
        using (var sw = await file.OpenStreamForWriteAsync())
        {
            sw.Write(data, 0, data.Length);
            sw.Flush();
        }
    }
    catch
    {
        // 同名のファイルがある場合は、そのまま使う
    }

    // tile.VisualElements.Square150x150Logo = new Uri(item.IconUrl);
    tile.VisualElements.Square150x150Logo = new Uri(&quot;ms-appdata:///local/&quot; + path );

    return await tile.RequestCreateForSelectionAsync(GetElementRect( this.btnMakeTile ));
}

public static Rect GetElementRect(FrameworkElement element)
{
    GeneralTransform buttonTransform = element.TransformToVisual(null);
    Point point = buttonTransform.TransformPoint(new Point());
    return new Rect(point, new Size(element.ActualWidth, element.ActualHeight));
}

Kindle の商品画像を HttpClient#GetByteArrayAsync でダウンロードしてきて、アプリケーションデータに保存します。保存するファイル名が一意になるようにするため、ASIN コードそのものを使っています。
保存したあとに、セカンダリタイルの画像を

tile.VisualElements.Square150x150Logo = new Uri("ms-appdata:///local/" + path );

のように指定しておけば良いわけです。

起動時に分岐させる

セカンダリタイルをクリックしたときの引数は TileId に設定しておきます。ここでは「”ASIN-” + item.ASIN」のように設定しています。これが引数となってアプリの起動時にわたるので、app.xaml.cs を開いて、以下のように、e.Arguments で判別させます。

protected async override void OnLaunched(LaunchActivatedEventArgs e)
{
#if DEBUG
    if (System.Diagnostics.Debugger.IsAttached)
    {
        this.DebugSettings.EnableFrameRateCounter = true;
    }
#endif

    if (e.Arguments != "")
    {
        var asin = e.Arguments;
        // 引数がある場合は、Kindle を起動する
        var opt = new LauncherOptions();
#if !WINDOWS_PHONE_APP
        opt.DesiredRemainingView = Windows.UI.ViewManagement.ViewSizePreference.UseNone;
#endif
        await Launcher.LaunchUriAsync(
            new Uri(string.Format("http://read.amazon.co.jp/?asin={0}", asin)), opt );
    }

引数は ASIN コードなので、そのまま、Kindle Cloud Reader に渡せば ok です。LauncherOptions を指定しておけば、起動時の状態が設定できるので Kindle Cloud Reader が全面にできるように UseNone に設定しておきます。デフォルトは半分半分ですね。

タイル自体は、tile.VisualElements で色々と外観が変えられますが、ここでは表紙を全面出しているため書名がちょっと見づらくなっています。まあ、ダウンロード時に画像の加工をして保存するとか、いろいろと手段があります。画像の加工自体は、

[win8.1] RenderTargetBitmapクラスで、XAMLをビットマップ形式で保存する
http://www.moonmile.net/blog/archives/4983

こんな風に RenderTargetBitmap を使えば手軽にできるので、一度、画面に表示させたうえで保存すればよいでしょう。ピン留めする直前とかに作れば ok かと。
あと、画像の入れ替えやバッチなども作れるので、既読とか交互に宣伝とかを流すのもよいでしょう。

カテゴリー: C#, WinRT | [WinRT] ストアアプリのセカンダリタイルを動的に作成する方法 はコメントを受け付けていません

[WinRT] ストアアプリで Amazon の商品検索をする方法

Kindle Launcher を作るときに、Amazon から商品検索をしたかったので、これに嵌りました。サンプルコード自体は Amazon で提供されているものの、WinRT(Windows ストアアプリ)では動かなかったんですよね。

■アフリエイトキーを取得する

http://affiliate.amazon.co.jp/

上記からアフリエイトキーを作成します。AWS とは違うので注意が必要(暫く使っていなかったので探した探した)。まあ、商品検索 API を使いたいのにクレジットカード登録とかは変な話なので。

API キーの取得等は以下を参照してください。アプリでは「アクセスキー」と「シークレットキー」の2つを使います。

Access Key IDとSecret Access Keyの取得 – Amazon Web サービス
http://www.ajaxtower.jp/ecs/pre/index1.html

■SignedRequestHelper.cs を修正する

C# では、アフリエイト API を使うときにヘルパークラスを使うと便利です。WPF や Windows Phone の場合は、提供されている SignedRequestHelper のままでいいのですが、WinRT の場合はちょっと違います。

[C#] Amazon Product Advertising API の利用(REST サンプルコードの修正) | プログラミング生放送
http://pronama.azurewebsites.net/2014/05/23/csharp-amazon-product-advertising-api-rest-sample-code-modified/

↓こんな風に修正してください。

SignedRequestHelper.cs for WinRT
https://gist.github.com/moonmile/16c950174d0c276cc2c3

System.Security.Cryptography じゃなくて、Windows.Security.Cryptography.Core を使うってのがミソですね。下に元ネタがあります。

c# – Working with hmacsha256 in windows store app – Stack Overflow
http://stackoverflow.com/questions/13293420/working-with-hmacsha256-in-windows-store-app

SHA で MAC キーを使うところが少し違うだけで、ヘルパークラスの使い方は変わりません。
おそらく、WinRT での違いがあって、ストア版の Amazon アプリが増えないのかもしれません。セキュリティ回りの名前空間とクラスが再整理(改悪?)されているので、いままでのコードが使えないのが面倒です。互換クラスを作ってしまえばよいような気もするのですが。

■指定したキーワードで商品情報を検索する

SignedRequestHelper を使って Amazon の商品を検索しているところがこんな感じです。

private const string MY_AWS_ACCESS_KEY_ID = &quot;YOUR_AWS_ACCESS_KEY_ID&quot;;
private const string MY_AWS_SECRET_KEY = &quot;YOUR_AWS_SECRET_KEY&quot;;
private const string DESTINATION = &quot;ecs.amazonaws.jp&quot;;

/// <summary>
/// 検索ボタンをクリック
/// </summary>
/// <param name=&quot;sender&quot;></param>
/// <param name=&quot;e&quot;></param>
private async void OnButtonItemSearch(object sender, RoutedEventArgs e)
{
    var helper = new SignedRequestHelper(MY_AWS_ACCESS_KEY_ID, MY_AWS_SECRET_KEY, DESTINATION, &quot;mycompany-22&quot;);
    var param = new Dictionary<string, String>();
    param[&quot;Service&quot;] = &quot;AWSECommerceService&quot;;
    param[&quot;Version&quot;] = &quot;2011-08-01&quot;;
    param[&quot;Operation&quot;] = &quot;ItemSearch&quot;;
    param[&quot;Keywords&quot;] = this.vm.Title + &quot; Kindle&quot;;
    param[&quot;Author&quot;] = this.vm.Author;
    param[&quot;SearchIndex&quot;] = &quot;All&quot;; // &quot;Books&quot;;
    param[&quot;ResponseGroup&quot;] = &quot;Large&quot;;

    try
    {
        var requestUrl = helper.Sign(param);
        var request = new System.Net.Http.HttpClient();
        var res = await request.GetStringAsync(new Uri(requestUrl));
        // Debug.WriteLine(res);

        var doc = ExDocument.LoadXml(res);
        int TotalPages = doc * &quot;TotalPages&quot;;
        int TotalResults = doc * &quot;TotalResults&quot;;
        vm.Total = TotalResults;

        int count = 0;
        vm.Books.Clear();

        for (int i = 1; i <= TotalPages; i++)
        {
            param[&quot;ItemPage&quot;] = i.ToString();

            await Task.Delay(1000); // ちょっとだけ待つ
            var reqUrl = helper.Sign(param);
            res = await request.GetStringAsync(new Uri(reqUrl));
            doc = ExDocument.LoadXml(res);
            var ASINs = doc * &quot;Item&quot; * &quot;ASIN&quot;;
            foreach (var it in ASINs)
            {
                count++;
                var asin = it.Value;
                var item = it.Parent;
                var format = item * &quot;ItemAttributes&quot; * &quot;Format&quot;;

                if (format == &quot;Kindle本&quot;)
                {
                    string title = item * &quot;ItemAttributes&quot; * &quot;Title&quot;;
                    string iconurl = item * &quot;LargeImage&quot; * &quot;URL&quot;;
                    Debug.WriteLine(&quot;{0} ASIN:{1} Title:{2} img:{3} &quot;, count, asin, title, iconurl);

                    vm.Books.Add(new Book { ASIN = asin, Title = title, IconUrl = iconurl });
                    vm.TotalKindle = vm.Books.Count();
                    vm.TotalStr = string.Format(&quot;{0}/{1}&quot;, vm.TotalKindle, vm.Total);
                }
            }
        }
    }
    catch (Exception ex)
    {
        // MessageBox.Show(&quot;API制限に達しました。暫く経ってから実行してくださいn&quot; + ex.Message);
        Debug.WriteLine(ex.Message);
    }
}

ItemSearch で戻される検索はページ単位で返ってくるのと、検索数が制限されています。サイトでは結構な数で検索できるのですが、API の場合は、4000件(400ページ)程度のようです。
この検索では、商品画像(本の表紙)を取るために Large にして商品データを取っています。まあ、Kindle Launcher の書籍検索がちょっと遅いのはそれが原因でもあります。ResponseGroup を変更するともうちょっとデータ量を絞れるでしょう。
SearchIndex を Books だけにすると「和書」だけになるので、All にしています。洋書も検索結果に入れたかったのです。Keywords に Kindle を含めているのは「Kindle本」に絞るためです。こうすると、おそらく Kindle 対象の本だけが検索できるはずです。そして、もういちど取得したデータを改めて」「Kindle本」で検索しています。

データは XML 形式で取得できるので適当にパースします。ここでは自前の ExDoc を使ってパースをしています。

https://github.com/moonmile/ExDoc/

にあるのは、Windows デスクトップ版なので、近いうちに PCL 版をアップしておきます。

XML データからクラスを作成する VS のアドイン(だっけ?)もあるので、それを使うのもよいでしょう。

■検索した商品を表示する

表示自体は GridView でやっています。
そのままのテンプレートでは使いづらいので「GridView.ItemTemplate」で自前テンプレートを作ります。

<!-- 水平スクロール グリッド -->
<GridView
    Grid.Column=&quot;1&quot; Grid.Row=&quot;1&quot;
    x:Name=&quot;itemGridView&quot;
    ItemsSource=&quot;{Binding Books}&quot;
    TabIndex=&quot;1&quot;
    Padding=&quot;10&quot;
    SelectionMode=&quot;{Binding SelectMode}&quot;
    IsSwipeEnabled=&quot;false&quot;
    Tapped=&quot;itemGridView_Tapped&quot;>
    <GridView.ItemTemplate>
        <DataTemplate>
            <Grid HorizontalAlignment=&quot;Left&quot; Width=&quot;250&quot; Height=&quot;250&quot;>
                <Border Background=&quot;{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}&quot;>
                    <Image Source=&quot;{Binding IconUrl}&quot; Stretch=&quot;UniformToFill&quot; />
                </Border>
                <StackPanel VerticalAlignment=&quot;Bottom&quot; Orientation=&quot;Vertical&quot;
                    Background=&quot;{StaticResource ListViewItemOverlayBackgroundThemeBrush}&quot;>
                    <TextBlock Text=&quot;{Binding ASIN}&quot;
                        Height=&quot;18&quot;
                        Foreground=&quot;{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}&quot;
                        TextWrapping=&quot;NoWrap&quot; Margin=&quot;3&quot;/>
                    <TextBlock Text=&quot;{Binding Title}&quot;
                        Foreground=&quot;{StaticResource ListViewItemOverlayForegroundThemeBrush}&quot;
                        Height=&quot;30&quot; Margin=&quot;3&quot;
                        TextWrapping=&quot;NoWrap&quot;/>
                </StackPanel>
            </Grid>
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>

IconUrl とか ASIN とかをバインドしておけば、自動的に表示されますね。

こんな風に表示されます。

カテゴリー: C#, WinRT | [WinRT] ストアアプリで Amazon の商品検索をする方法 はコメントを受け付けていません

セカンドディスプレイのタッチを有効に働かせる方法

先日、液晶テレビを買ったので、家族用のタッチ液晶 LG-23ET83 が不要になりました。液晶テレビに PC がつなげられるので、(ちょっと字は滲みますが)ブラウザも快適に動いています。そうそう、Mac mini をつなげて字の滲みを比較する予定なのですが、まだやっていません。

さて、余ったタッチ液晶を仕事用のディスプレイにしようと思って、3面にしました。

image

メインではなくて、セカンドディスプレイにしてあるのは、タッチパネルためか映り込みが激しいんですよね、このディスプレイ。映像を映しているときにはいいのですが、面と向かってプログラミングをすると自分の顔を見ながら仕事する、ってことになってちょっと変です。なので、通常は、左のディスプレイで作業をして、真ん中をタッチにしようと考えたわけです。

■メインディスプレイにタッチが移動する?

この順番でつなげると、何故か、タッチ用の2番のディスプレイを触っているにも関わらず、1番目の液晶のマウスカーソルが動きます。どうやら、タッチ液晶の反応を別のディスプレイ(メインディスプレイ)に送っているらしい現象なんですよね。

仕方がないので、2番目のタッチ液晶の「これをメインディスプレイにする」にして作業をしていました。何故か、メインディスプレイに設定すると、タッチ部分と液晶表示は同期するのです。これでもまあいいのですが、タスクバーの時計が表示されているところが、メインのタスクバーだけなので、ちょっと変な感じなんですよ。あと、デスクトップにアイコンを作るときはメインディスプレイに作成されるので、いちいち2番目のディスプレイ(タッチ液晶)に探しに行かないといけません。

非常に面倒だった…のですが、解決策がありました。

■タブレットPC設定で、タッチ液晶を設定する

コントロールパネルで「タッチ」を検索して、「タブレットPC設定」をクリックします。

image

構成にある「ペンとタッチディスプレイを構成します」の「セットアップ」ボタンをクリックすると、どれがタッチ液晶なのかという設定ができます。

image

タッチ入力を選択すると、全画面にこんな風な表示がでます。image

これを、タッチ液晶のところまで Enter キーで動かし後、タッチ。設定を保存すれば、メインディスプレイでなくてもタッチ液晶が正しく認識されます。

タッチ液晶が2枚あったときにはどうなるのか?は不明ですが、ひとまず1枚のときはこれで良さそうです。

カテゴリー: windows 8.1 | セカンドディスプレイのタッチを有効に働かせる方法 はコメントを受け付けていません

本ごとに Kindle Cloud Reader へのリンクをスタート画面に作る方法

ブラウザで Kindle 本を読む方法とスタート画面へのピン留め | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/6311

の続きです。

IE からショートカットを作ってスタート画面にタイル表示できることは分かったのですが、どうせならば、本のアイコンが表示されて欲しいですよね。タイトルが変えられるとはいえ、すべて「k」になってしまうのでは、どれがどの本か分からなくなってしまいます。

image

が、少し手間を掛けるとアイコンの登録もできます。

■URLショートカットを作成する

[InternetShortcut]
IDList=
URL=http://read.amazon.co.jp/?asin=<asin>
IconFile=http://images-jp.amazon.com/images/P/<asin>?.09.MZZZZZZZ
IconIndex=1

 

メモ帳などで、<タイトル>.url というファイルを作成します。これは、IE9 とは違って、昔のURLショートカットでブラウザで表示する URL とアイコンを指定しています。<asin>なところは、Kindle 本の ASIN コードを入れます。IconFile 自体は、amazon の商品画像を指定しています。

IE9 で作るショートカットは、Web サイトをピン留めする方法 にある通り website ショートカットなんですね。なるほど。開くと何故か IconFile が書き換わるので、スタート画面のアイコンが変更されてしまうという仕様のようです。なので、*.website ファイル自体を読み取り専用にすれば同じことができます。

作成したショートを プログラムの中に移動させます。

image

このフォルダは Win+R で「shell:programs」で開けます。私の場合は Kindle というフォルダを作ってその中にいれています。

ここにコピーすると、スタート画面で「新しいプログラムが…」が出るので、ちまちまとスタート画面にピン留めをすればできあがり。ピン留自体と順番を変えるのが非常に面倒なのですが、まあ、

アイコン自体が、デスクトップ用の小さな画像になってしまうので、視認性が悪いのが難点。登録自体は、Amazon Web Service を利用すればいいので、さほど難しくないでしょう。たぶん、Excel VBA とかを使ってもできそう。

スタート画面のタイル自体のカスタマイズは、以下でできるのでもう少しなんとかなりそう。

デスクトップ アプリのスタート画面のタイルをカスタマイズする方法 (Windows ランタイム アプリ) (Windows)
http://msdn.microsoft.com/ja-jp/library/windows/apps/xaml/dn449733.aspx

他にも、仮のストアアプリを登録しておいてサブタイルでもできるでしょう。まあ、漫画本ばっかりずらずらと並ぶのもアレなのでカテゴリごとのランチャーアプリを作るほうが良いかも。大量に URL ショートカットを作るのも面倒だし、シリーズ毎に分けられると積読が少なくなるでしょう。あとは参考用の洋書まわりとか。

カテゴリー: 雑談 | 本ごとに Kindle Cloud Reader へのリンクをスタート画面に作る方法 はコメントを受け付けていません

ブラウザで Kindle 本を読む方法とスタート画面へのピン留め

Windows で Kindle を読む方法と言えば、日本語Windows 8 Pro で Kindle を動かす方法 な感じで、Android のエミュレータ Bluestacks などを使う方法しかなかったのですが、先日 amazon.co.jp から Kindle Cloud Reader が出ました。

アマゾン、PC ブラウザで読めるKindle Cloud Reader 提供開始。漫画・雑誌と洋書のみ – Engadget Japanese
http://japanese.engadget.com/2014/09/22/pc-kindle-cloud-reader/

記事では「漫画と洋書のみ」となっていますが、画像キャプチャの雑誌系や、いくつかの横書きの日本語本は読めます。順次、コンバートということなので、そのうち対応されるかもしれません(縦書きが面倒なので対応されない、というパターンもありますが)

image

多少画面は荒いですが、漫画が読めるのと洋書が PC で読めるのは便利です。手元にあるのは、Kindle whitepaper と iPad なので、普段は Kindle、ちょっと大きめなもので読むときは iPad を使っています。

image

液晶ディスプレイで並列に表示しながら使えるのは結構便利ですね。これはデスクトップ版の IE で表示してますが、Windows ストア アプリアプリ版のモダンIEのほうでも表示ができます。

■スタート画面に指定の本をピン留めする

いくつか手順…と制限がありますが、スタート画面に Kindle 本をピン留めできます。

 

多少、面倒臭い(苦笑)のは後でツール化するとして、http://read.amazon.co.jp/?asin=<ASIN> の形式で指定の本を開くことができます。この asin は amazon の商品コードで一意に決まっているものです。
Kindle 本を開いたときに、登録情報の ASIN を見ると、「B00AA9W658」のようなコードがあります。

image

これを http://read.amazon.co.jp/?asin=B00AA9W658 のようにすれば、「ヨコハマ買い出し紀行」の1巻が Cloud reader で開かれます。購入していない場合は、左下に「完全版を入手」と出るので、サンプルを開いていることがわかりますね。ちなみに、私は紙のほうを持っているので Kindle のほうは買ってません。

image

さて、購入した本の場合、これをいちいちブラウザで打つのは面倒なのでスタート画面にピン留めします。IE の左上のアイコンをデスクトップへドラッグするとショートカットができます。

image

このショートカットをメモ帳で開くと、こんな感じに URL が指定されています。

image

適当にタイトルを変えておけばよいでしょう。

image

ここに ASIN が書かれているので、これを書き換えれば、指定の Kindle 本を呼び出すことができます。また amazon の登録情報の表示以外にも URL にも ASIN が含まれているので、そこから抜き出すことも可能です。http://www.amazon.co.jp/%E3%83%A8%E3%82%B3%E3%83%8F%E3%83%9E%E8%B2%B7%E3%81%84%E5%87%BA%E3%81%97%E7%B4%80%E8%A1%8C%EF%BC%88%EF%BC%91%EF%BC%89-%E3%82%A2%E3%83%95%E3%82%BF%E3%83%8C%E3%83%BC%E3%83%B3KC-%E8%8A%A6%E5%A5%88%E9%87%8E%E3%81%B2%E3%81%A8%E3%81%97-ebook/dp/B00AA9W658/ref=sr_1_1?s=digital-text&ie=UTF8&qid=1411541708&sr=1-1&keywords=%E3%83%A8%E3%82%B3%E3%83%8F%E3%83%9E%E8%B2%B7%E3%81%84%E5%87%BA%E3%81%97%E7%B4%80%E8%A1%8C この中の dp/ の後ろにあるコードが ASIN なので、これを抜き出します。

ショートカット自体を、スタート画面にピン留めするには、いくつか方法がありますが、次のように作れます。

  1. Win+R で、shell:programs と入力して、スタートメニュー/プログラムのフォルダを表示する。
  2. Kindle のようなフォルダを作って、作成したショートカットをコピーする。
  3. スタート画面に「新しいアプリがインストールされました」と出るので、アプリ画面から、指定のアイコンをスタート画面へピン留めする。

image

こうすると、アイコンは変わりませんが、スタート画面から直接 Kindle 本を起動できます。

image

まあ、欠点は

  • (おそらく)デフォルトのブラウザが起動されるので、モダンIE に設定しておかないと駄目?
  • アイコンが変更できない。

ってところです。ただ、Kindle にたくさんの本がある場合はちょっと便利かと。特に漫画のシリーズ本なんかはたくさんの本が並ぶので、1冊ごとにアイコンがあると便利…かもしれません。

アイコン自体は IconFile で指定されているのですが、これを変更してもアイコンは変わりません。と言いますか、元の Kindle 以外のアイコンはうまく出ません。ライブラリごとに本をまとめたりすると便利そうなので、別途簡易アプリを作るほうがいいかもしれませんね。

ちなみに Kindle では送り先の端末が制限されていますが、この Kindle Cloud Reader は制限の内に入っていないようです。そのうち、制限されるような気がしないでもないのですが、ブラウザでよんでいる限り、何台の PC でも利用できそうです。英語版の Kindle on PC はどうだったのかな?

一応、Kindle Cloud Reader も送り先に出てくるので、これで1台カウントみたいですね。

image

ちなみに初代 Surface RT でも動いています。横向きだとロードがかかって重たいですが、縦置きだとサクサク進みます。

埋め込み画像への固定リンク

カテゴリー: 雑談 | ブラウザで Kindle 本を読む方法とスタート画面へのピン留め はコメントを受け付けていません

CloudFlash を無線LANルータに接続して使う(ネットワーク参加させる)

CloudFlash をデジカメではない環境で動かそう | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/6277

の続きです、CloudFlash は WiFi のホスト機能を持っていてデフォルトで SSID が「Cloud Flash」にして接続ができます。多少チープな環境ですが、写真をダウンロードできる web サーバを積んでいるので、ブラウザから写真がダウンロードできて便利です。
普通は、デジカメに CloudFlash を差して使うところですが、私の場合は SD カードをリーダを使うことで「単体」で CloudFlash を動かします。…意味があるのかどうかは不明ですが(苦笑)、ひとまず、

  • 特別な機器なし(カードリーダーは必要だけど)に WiFi 付きの Linux 環境が使える。
  • ストレージは SD メモリで拡張できるので、うまくやれば .NET が動く?
  • 初代 Surface からターミナルで接続しよう

ってのが目標です。

■CloudFlash を無線LAN 環境に参加させる

デフォルトでは、CloudFlash がホストになっていますが、これだといちいちノートPCやスマートフォンの接続先を変更しないので少々面倒です(逆に外にいるときは便利なんですけど)。なので、既存のネットワークに参加させるように設定し直します。

設定自体は、日経Linuxの2014/08号を参考にするか、以下の記事を読み進めます。

Flucard Pro – Wi-Fi 機能搭載 SD カードを Linux サーバーとして楽しむ方法
http://netbuffalo.doorblog.jp/archives/4811269.html

普通はネットワークに接続するだけでは意味がないので、自動起動時にスクリプトを動かすようにするのですが、単純に WiFi させるだけならば以下のスクリプトを autorun.sh にして SD メモリのルートに置きます。

– autorun.sh
– APP/wifi.conf
– APP/wpa.conf

#!/bin/sh

APP_HOME=/mnt/sd/APP
LOG=$APP_HOME/log.txt
SCAN=$APP_HOME/scan.txt

# echo "---" >> $LOG
# date       >> $LOG
# ifconfig   >> $LOG

. $APP_HOME/wifi.conf
ifconfig mlan0 down
sleep 3
a1 
sleep 3
ifconfig mlan0 up || exit 1
sleep 3

wpa_supplicant -i mlan0 -B -c $APP_HOME/$WPA_CONF
ifconfig mlan0 $IPADDR netmask $NETMASK  
route add default gw $GATEWAY  

sync
exit 0

起動時の動作は適宜 APP/log.txt に書き込んでいるのですが、Windows のドライブとの同期がうまく取れないときがあります。そのような時は、一度 SD カードを抜き差しして再度確認してください。一応 sync で同期させているのですが、完全ではないようです。

ESSID_NAME=<接続先のSSID>
WPA_CONF=wpa.conf
BOOTPROTO=static
IPADDR=172.16.0.99
NETMASK=255.255.255.0
GATEWAY=172.16.0.1
network={
	ssid="<接続先のSSID>"
	key_mgmt=WPA-PSK
	proto=WPA
	pairwise=TKIP
	group=TKIP
	#psk="<password>"
	psk=ff2ba81ca3ce58f606fd99998cf54271d3af00f36b371b235b7f53c0438e51a8
}

ひとまず固定IPにしてネットワークに参加させます。wifi.conf と wpa.conf の書き方は Linux 系のネットワークを調べるとよいでしょう。通常の Linux のものがそのまま使えます。

どうやら、autorun.sh スクリプト内にある a1 スクリプトが肝で、カーネルライブラリをロードしているようです。他にも w1 というスクリプトがあるので、どちらかを使えばいいんでしょう。

# cat a1
ifconfig mlan0 down

insmod /lib/ar6000.ko
sleep 1
insmod /lib/ka2000-sdio.ko
sleep 1
#iwconfig mlan0 txpower 8
#ifconfig mlan0 up
#dev=ath0
dev=mlan0

#iwpriv $dev version
#iwpriv $dev httxcfg 0x62
#iwpriv $dev htcapinfo 0x1820000

# cat w1
#insmod /mnt/sd/ka2000-sdio.ko
insmod /lib/ka2000-sdio.ko

#iwpriv mlan0 version
#iwpriv mlan0 httxcfg 0x62
#iwpriv mlan0 htcapinfo 0x1820000
#

こうすると、無線ルータにネットワーク参加して「172.16.0.99」で接続ができます。CloudFlash 自体は telnetd が自動で起動されるようになっているので(スクリプト自体は、/etc/init.d 内にあります)、tera term などで接続が可能です。ただし、ユーザー名、パスワードがない状態なので、社内 LAN などの公共のものに接続する場合は注意してください。中身が覗けてしまいますから。

■busybox が使われている

CloudFlash のメモリは 30MB弱しかありません。

組み込み Linux & 簡易 Web サーバーとして動くので、まあこれで十分といえば十分です。コマンド自体は組み込み用の busybox が使われているので、うまくマウントしてやれば機能を増やすことができます。SD カードのストレージ自体はたくさんありますから。
ストレージを活用して、Ruby を入れている例もあるので、ひょっとすると .NET Framework が乗るかもしれません。ARM で動いているそうなので、Raspberry Pi でビルドしたものを持ってくるか、QEMU 環境でビルドしたものをコピーすればよいでしょう。

■ファイル等は Windows からコピーできる

autorun.sh 等のファイルは、Windows PC からコピーできます。普通に SD メモリカードを USB リーダーに差し込んで PC に接続すればドライブとして認識がされます。

CloudFlash では /mnt/sd の環境が、SD メモリのルートになります。ここに適当にフォルダやファイルをコピーして起動時に、ln -s 等してやれば Ruby などを動かすことができます。CloudFlash の中身自体は ROM になっているので、起動時にこの作業が必ず必要になります。

ちなみに date 自体は起動時に 2012-1-1 にリセットされているようで、日付をチェックするにはちょっと特殊な操作が必要かもしれません。

また、ファイル自体は FAT32 フォーマットなので、Linux 特有のグループ設定などはできないと思われます。

■元の WiFi 環境に戻すにはどうするのか?

ブラウザを使って Auto WiFi を OFF にすることはできたのですが、元の ON に戻すには(CloudFlash を WiFi のホストとして機能させるには)どうしたらいいのかと悩みました。何故か、どこにも書いていない。
と思ったら、実は簡単です。

SD メモリカードをクイックフォーマットすれば OK です。

初期状態では、WiFi のホストとして機能しているので、確かにそうだった。結構悩んで、元に戻す設定を3時間ほど探していました。フォーマットすると JPEG などの写真データも一緒に消えてしまうので(フォルダ等が残っている状態でも駄目なようです)、きれいさっぱりフォーマットします。どうやらクイックフォーマットでよさそうです。実験としてはホスト用とネットワーク参加用の2種類のメモリカードを用意しておけばよいでしょう。
CloudFlash 自体のリセットは、SD カードリーダーに抜き差しすることで可能です。10秒ぐらいまてばいいみたいですね。

カテゴリー: 開発 | CloudFlash を無線LANルータに接続して使う(ネットワーク参加させる) はコメントを受け付けていません

CloudFlash をデジカメではない環境で動かそう

image

先日、CloudFlash というのを知って、ちょっと買ってみました。デジカメに入れて WiFi でデータを飛ばして PC で受け取ることができる、というものです。以前話題になったときに興味はあったのですが、デジカメもあまり使わなくなったので、忘れていました。日経 Linux 2014年08月号 に @netbuffalo さんの記事があって、この CloudFlash をホットスポットにつなげて使うという記事があり、中身が Linux ということが分かりました。なるほど、Linux + WiFi 環境ならば単体で Linux 環境が手にはいるのでは?と思ったわけです。

■電源供給だけで動くのか?

CloudFlash と同じ製品はいくつかあって、もともとがデジカメに差し込んで使う製品です。中身の Linux を弄ろうとする人は稀らしくて、デジカメで動くけど、PC のハブに差し込んだらどうなのか? 電源だけならどうなのか?という話はあまりないんですよね。

結論から言うと

  • 電源供給だけでは(たぶん)動きません。
    コンセントに差し込んだ、充電用ハブに SD カードアダプタを差し込んだけど、CloudFlash はうんともすんとも言いません。
  • PC のハブに差し込むときは、SD カードアダプタとの相性があります。
    私の場合、一番左のサンワサプライ ADR-SDU2L だけが動きました。
    真ん中の2つは100円ショップで買ったものですが、動きませんでした。
  • デジカメの場合は、全然大丈夫です。

私の場合、一番右の SD カードアダプタを持っていました。100円ショップで買ったもので、普通に SD メモリが読めていたから、これでいけるのでは?と思っていたものの、動かず…ひょっとしたら、デジカメオンリーなのかと思って @netbuffalo さん に尋ねたところ、SD カードアダプタでも動くことが判明。そんな訳で、上記の SD カードアダプタを買ったわけです。

相性が悪いのかどうかわかりませんが、サンワの ADR-SDU2L(500円で買った)は動いて、100円ショップの3つは動きません。正確には、左から2番目の SD カードリーダーは、最初動いたような感じなのですが、後から動かなくなりました。

電源供給の場合には動かず、SD カードリーダーやデジカメで動く。またデジカメがスリープした状態では動かない(WiFi が切れる)ところを見ると、CloudFlash 内蔵のチップとデジカメ(あるいは PC )がやり取りをしていて、それが切れるとスリープ状態に入るようです。あと、おそらくデジカメのほうが電力は安定しているでしょうから、デジカメのメモリ程度の安定性が必要なのかもしれません。

■試しに Raspberry Pi の USB につなげてみると

CloudFlash となんらかのやり取りをしていれば良いのであれば、Raspberry Pi にそれを肩代わりsてい良いわけです…ってことで、Raspberry Pi の USB につなげてみました。

結果は、CloudFlash のホストは見れるのですが(iPhone から Wifi の探索でチェックしています)、いざ繋げて写真などをダウンロードしようとすると WiFi が落ちます。RasPi に無線 LAN をつなげていることもよくある現象なので、これと同じ感じですね。たぶん、途中で電力が足りなくなって WiFi が落ちてしまっているようです。外部電源のハブをつなげるとうまくいくかもしれません。

■telnet がつながる

ip が 192.168.1.1 固定ですが、telnet がつながります。

image

ただ、これだといちいち CloudFlash をホストにして接続しないといけないので、結構面倒です。ホットスポット(自宅の無線LANネットワーク)に参加させる方法は、先の日経 Lunix の記事にあるので後で試してみます。ルートに autorun.sh を作ってやる方法なので、汎用性があってよいと思います。

CloudFlash に差し込むメモリ自体は、/mnt/sd  で覗くことができます。ftp もつながるそうなので、比較的簡単に内部にはアクセスできそうです。Linux コマンドは BusyBox が使われていました。

BusyBoxって何ぞや?:組み込みLinuxで際立つ「BusyBox」の魅力 (1/2) – MONOist(モノイスト)
http://monoist.atmarkit.co.jp/mn/articles/0802/04/news114.html

ただし、vi も動かない貧弱な環境なので、この環境のままで何かやるのは難しそうです。

image

image

デジカメ内部でRubyを動かす狂気!無線LAN内蔵SDカードアダプタPQI Air Cardの間違った使い方
http://hitoriblog.com/?p=12627

Ruby が動くのだから Mono も動かしたい、と思ったり。
# この記事をみると、電源供給だけで動いているのですが…私の手元のやつだと動かないんですよね。PQI だから動くのかも。

~~

追記:2014/09/16
グリーンハウス SDXC対応USB2.0カードリーダ/ライタ ホワイト GH-CRSDXC と CloudFlash の組み合わせで行けました。さすが「あつをリーダー」ッ!!!
これで、電源供給だけで CloudFlash を操作できます。

カテゴリー: 雑談 | CloudFlash をデジカメではない環境で動かそう はコメントを受け付けていません

F#でOpenCvSharpを使ってカメラキャプチャする

OpenCvSharp を使ってカメラキャプチャをする | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/6258

の F# 版です。WPF で作ってあります。

module MainApp

open System
open System.Windows
open System.Windows.Controls
open FsXaml
open OpenCvSharp.CPlusPlus
open OpenCvSharp.Extensions

type MainWindow = XAML<&quot;MainWindow.xaml&quot;>

let mutable _frame:Mat = null

/// <summary>
/// カメラを表示
/// </summary>
/// <param name=&quot;id&quot;></param>
let camera(id:int) =
    let cap = new VideoCapture()
    cap.Open(id)
    if cap.IsOpened() then
        let fs = FrameSource.CreateCameraSource(0)
        let win = new Window(&quot;camera&quot;)
        let frame = new Mat()
        _frame <- frame
        let mutable loop = true
        while loop do
            cap.Read( frame )
            if frame.Empty() = false then
                win.ShowImage( frame )
                let key = Cv2.WaitKey(100)
                if key = 27 then
                    loop <- false
            else
                loop <- false

let loadWindow() =
   let window = MainWindow().CreateRoot()
   let accessor = MainWindow.Accessor(window)
   // ボタンクリック時のイベント
   accessor.btnCamera.Click.Add( fun e -> camera(0) )
   accessor.btnCapture.Click.Add( fun e -> 
        accessor.img.Source <- WriteableBitmapConverter.ToWriteableBitmap(_frame)
       )
   // ウィンドウを返す
   window

[<STAThread>]
(new Application()).Run(loadWindow()) |> ignore

やっていることは、C# 版と同じで、カメラ用のウィンドウを開くのとボタンを押したときにキャプチャする処理が入っています。ファイルに保存する処理はほとんど同じなので省略。

F# で WPF を扱うときは FsXaml のタイププロバイダを使います。ボタンイベントとかはボタン自身に名前を付けて Add.Click 等で追加すれば ok.
フォームの場合は Bitmap に落とすのに BitmapConverter.ToBitmap を使いますが、WPF の Image.Source に対しては WriteableBitmapConverter.ToWriteableBitmap を使って Mat から WritableBitmap に落とします。
Mat と Bitmap の相互変換と、float/double で処理した自前の Pixcels クラスを使えば、画像解析のトライアンドエラーがしやすいかなと、奮闘中です。もう少しできたら github にでも。

ひとまず、動作サンプルはこちら。直ぐ動くように opencv の dll も含んでいるのでちょっと大きいです。

カテゴリー: F#, OpenCV | F#でOpenCvSharpを使ってカメラキャプチャする はコメントを受け付けていません

クラス名はUMLスタンダードに則って作るのがベター

 

気になったので、ラショナルプロセス風なUMLスタンダードパターンを紹介しておきます。

Amazon.co.jp: ラショナル統一プロセス入門 第3版 (ASCII Software Engineering Series): フィリップ クルーシュテン, Philippe Kruchten, 藤井 拓: 本

  1. ユースケース等で対象のオブジェクトを抽出する。
  2. オブジェクトを辞書化する。
  3. 辞書を整理して、オブジェクト図を作成する。
  4. オブジェクト図を抽象化して、クラス図を作成する。
  5. クラス図を作成した後に、GoF などのパターンがあれば、それに準じる。

統一プロセス自体には 5 番目のパターン適用はありません(GoF の前のため)。このため、GoF などのパターン(特に Java のエンタープライズパターンなど)が既にある状態での、現在では Manager などの「パターン特有の単語」が頻出していますが、もともとはパターン適用時の単語なので変更しては駄目なんですよ。

最初にユースケースからオブジェクトを抽出するプロセスでは、具象的な名前を使います。これは、オブジェクト図(具象図)とクラス図(抽象図)を明確に区別するためでもあり、いきなりモデル化してしまうことにより、間違った抽象化を避けるためです。今でいえば、ドメイン駆動などがあるので、業務ドメインに特化した言語/単語を使いつつ、クラス図にて抽象化(一般化した名前)に落ち着けるか、ドメインに特化した名前をそのまま使うか、の判断を行います。このプロセスは、2 の辞書化の部分にもあてはまります。

そんな訳で、「今」しか学ばないと名称に混乱するでしょうが、「歴史」的なGoF 等のパターンにでてくる名称と統一プロセスを知っておくと、特に悩むことはありません。抽象化と具象化の部分が区別されますからね。なんか、ちょっと Twitter でのコメントが気になったので、書いておきます。

~~

余談ですが、名称をつけるときのテクニックは「明確」にあります。昔よりも IDE のインテリセンス/補完機能が働いているので、統一的な名称を単語の前に出すことで、うまくマッチせることができます。たとえば、本に関係するメソッドは、

  • ListBook
  • SortByBookID
  • GetBookTitle

とするよりも

  • BookList
  • BookSortByID
  • BookTitleGet

な風にすると揃えられます。Get/Set のように動詞を前にするか後ろにするかは異論があるでしょうが、それは業務ドメインによって判断してください。もちろん、この「Book」の場合、Book クラスを作っておいて、メソッド/プロパティでまとめる、ってのが王道ですが(そのあたりが、オブジェクト指向なんですけど)。

カテゴリー: 開発 | クラス名はUMLスタンダードに則って作るのがベター はコメントを受け付けていません

OpenCvSharp を使ってカメラキャプチャをする

画像解析まわりを F# で組もうとする場合、OpenCV の Mat と相互変換しなくちゃいけません。OpenCV 自体は C++ ベースなのでロジックを C++ で組むのがベータなのですが、ちょっとトライ&エラーがやりづらいんですよね。OpenCV の機能自体を使うのであれば、そのまま OpenCvSharp を使えばいいのですが、内部ロジックを弄る必要がある(特にテンプレートマッチの関数とか特徴量の関数とか)予定なので、できるだけデータ変換はおこしたくないな、と思っていました。

■OpenCvSharp は導入が簡単である

shimat/opencvsharp
http://github.com/shimat/opencvsharp
OpenCvSharpをつかう その17(NuGetで導入) – schima.hatenablog.com
http://schima.hatenablog.com/entry/2013/12/15/110513

OpenCvSharp 自体を知ったのは検討したのは3年前ぐらいで、その頃ちょうど .NETラボで亀川さんが講演していて、OpenCvSharp あたりを使った画像解析の話だったんですよね。データの変換自体が、C++ のネイティブなポインタと、.NET Framework のラッピングされたポインタでは、点単位ではひどく遅くなるだろうという会話をしたのを覚えています。その頃は、導入が結構面倒(OpenCvのDLL自体が面倒)だったので、C++ で組んでも C# で組んでもあまり変わりがなかったのですが、いつの間にか(失礼)、NuGet で設定ができるようになっていました。

Nuget で「OpenCvSharp」で検索してインストールすると、必要な DLL とプロジェクトの設定が自動的に行われます。

現状の OpenCV は 2.49(3のほうは、まだアルファ版です)なのですが、これらが一気に dll フォルダにダウンロードされます。

C++ プロジェクトで組むときに、最初に面倒なのがこの DLL を導入する(いちいちバージョンを変えないといけない)ことなので、プログラミング時の敷居がぐっと下がっています。

■カメラの画像をキャプチャする

サンプル自体は、先の github の中
https://github.com/shimat/opencvsharp/tree/master/sample

にあるので、目的のものを動かしてみるとよいでしょう。もともと OpenCV に含まれているサンプルが C#/VB 版に書き直されている感じです。

サンプルコードはこちら
http://1drv.ms/1lREkUV

■カメラへの接続

カメラへの接続は VideoCapture で設定すれば ok です。capture.Read あたりも C++ 版と同じなので、OpenCv を使っている場合はだいたい想像がつくでしょう。このウィンドウは OpenCv が勝手に作るウィンドウなので、Cv2.WaitKey でキー待ちをしてエスケープキーで閉じるようにしておきます。また、このウィンドウはスレッドで動いているらしく、async/await とか createwindows しなくても、モーダレスで動きます。これはこれで便利。

void Camera()
{
    var capture = new VideoCapture();
    // 最初のカメラを取得
    capture.Open(0);
    if (!capture.IsOpened())
        throw new Exception("capture initialization failed");
    var fs = FrameSource.CreateCameraSource(0);
    using (var normalWindow = new Window("normal"))
    {
        var normalFrame = new Mat();
        var srFrame = new Mat();
        while (true)
        {
            capture.Read(normalFrame);
            if (normalFrame.Empty())
                break;

            _frame = normalFrame;
            normalWindow.ShowImage(normalFrame);
            int key = Cv2.WaitKey(100);
            if (key == 27) break;   // ESC キーで閉じる
        }
    }
}

■カメラから画像をキャプチャ

カメラから Bitmap へ落とすのも非常に簡単です。BitmapConverter.ToBitmap でフレームを渡してやれば Bitmap で落とせます。これをそのままピクチャボックスに張り付ければ画像が出ます。ここの BitmapConverter は以前とは違って OpenCvSharp.Extensions の中に入っているそうです。

private void button2_Click(object sender, EventArgs e)
{
    // カメラからキャプチャ
    Bitmap bitmap = BitmapConverter.ToBitmap(_frame);
    this.pictureBox1.Image = bitmap;
}

■ファイルに保存する

以前作ったツールから流用して、Bitmap を PNG 形式にして保存します。日付と連番でファイル名を付けるので

/// <summary>
/// マイピクチャに保存
/// </summary>
/// <param name=&quot;sender&quot;></param>
/// <param name=&quot;e&quot;></param>
private void button3_Click(object sender, EventArgs e)
{
    var bmp = (Bitmap)pictureBox1.Image;
    var fmt = ImageFormat.Png;
    var path = MakeFileName( fmt );
    bmp.Save(path);
    label1.Text = Path.GetFileName(path);
}

string FOLDER = @&quot;d:workblogimage&quot;;

string MakeFileName(ImageFormat fmt)
{
    string path = FOLDER;
    DateTime dt = DateTime.Now;
    string dname = string.Format(&quot;{0:0000}{1:00}{2:00}&quot;, dt.Year, dt.Month, dt.Day);
    var files = System.IO.Directory.GetFiles(FOLDER);
    var file = files.Where(f => f.IndexOf(dname + &quot;_&quot;) > 0).Max();
    var ext = fmt.ToString();
    if (ext == ImageFormat.Jpeg.ToString()) // 拡張子を変更
    {
        ext = &quot;jpg&quot;;
    }
    if (file == null)
    {
        path += dname + &quot;_01.&quot; + ext;
    }
    else
    {
        var mat = new Regex(@&quot;_(\d+)&quot;).Match(file);
        int num = int.Parse(mat.Groups[1].Value);
        num++;
        path += dname + &quot;_&quot; + num.ToString(&quot;00&quot;) + &quot;.&quot; + ext;
    }
    return path;
}

と、こんな感じにしておけば、手軽に Web カメラから画像へ落とせるようになります。
あとで、テンプレートマッチング用の関数のスピードも確認しておきましょう。これができると、ほぼ F# で組めるので。

カテゴリー: C#, OpenCV | 2件のコメント