がりらぼ

WindowsRuntimeの応援ブログ

DirectXとC++で書いたWindowsPhoneアプリを実機デバッグする

DirectXC++で書いたWindowsPhoneアプリは実機でデバッグしようとしてもDeviceの選択肢がなく、

エミュレータの選択肢しかないため実機デバッグができません。

f:id:garicchi:20141203222022p:plain

ソリューションのターゲットプラットフォームがWin32なのが問題なのでARMに変更することで解決します。

ソリューションエクスプローラーから、ソリューションを右クリック→プロパティを開きます。

f:id:garicchi:20141203222146p:plain

構成プロパティのところに自分の作っているプロジェクトがあるのでプラットフォームと書いてある列のセルをWin32→ARMに変更します。

f:id:garicchi:20141203222225p:plain

これでDeviceが選択できるので実機デバッグができます。

f:id:garicchi:20141203222351p:plain

deployment - Deploying Universal DirectX app to Windows Phone 8.1 device - Stack Overflow

WindowsPhone AdventCalender2014が開催されています

f:id:garicchi:20141202142328p:plain

今年もアドベントカレンダーの季節がやってきましたね!

各界隈でさまざまなアドベントカレンダーが開催されていますがWindowsPhoneは今年もやるそうです。

WindowsPhoneマニア日本代表(主観)のod_10zさんが開催されるWindowsPhoneアドベントカレンダー2014、なかなか盛り上がりそうな感じがあります。

現在2日目ですがod_10zさんはscheduledTileNotificationについて。とkazuakixさんはOffice365をWindowsPhoneで使うについて記載されています。

僕は15日担当ですが、WindowsPhoneでDirectxによるゲームプログラミングについて初心者向けに書けたらいいなと思います。

ぜひクリスマスはWindowsPhoneで!

Windows Phone Advent Calendar 2014 - Adventar

Cortanaで音声コマンドを使うには(WindowsPhone8.1)

注意

日本でWindowsPhoneをインターネットにつなげることは禁止されています。(そもそも起動すら禁止されているらしい)

Cortanaはインターネットに繋げないと実行できません。

私はシアトルに滞在していたときにテストしましたが、日本でのCortana使用は実質禁止されています。

あと日本語の音声認識もできません。

なんで使っちゃダメかについてはここらへんを参照してください。

Windows Phoneへの道 - SIMフリー端末ってなに? その2 ラスボスは技適マーク

Cortana

WindowsPhone8.1から、Cortanaという音声認識エージェントが導入され、Cortanaに向かって話すと、様々な処理をしてくれます。

 

10

 

 

Cortanaには音声コマンドを登録することができ、予め特定の音声コマンドを登録しておくとあとはユーザーがCortanaにむかって話しかけるだけでアプリを起動してくれます。

11

 

Cortanaを使用する

WindowsPhone8.1では日本語ではCortanaを使用することができません。

もちろん日本語の音声認識や音声コマンドは現在利用することができません。

 

Cortanaは現在日本語では使用できないのでシステムの設定から、言語を英語、地域をUnitedStatesに変更するとCortanaがWindowsPhoneで使えるようになります。

 

設定→言語から、Englishを選択し、電話を再起動のボタンを押し、再起動します。

1

 

 

電話を再起動できたら、次に地域の設定をします。

設定からregion→UnitedStatesに変更し、再起動します。

 

 

2

 

これでアプリ一覧にCortanaが出現します。

3

 

VCD(Voice Command Definition)を記述する

VCDファイルとはCortanaに認識させたい音声コマンドを記述したxmlファイルです。

以下にサンプルのVCDを示します。

<?xml version="1.0" encoding="utf-8"?>
<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.1">
 <CommandSet xml:lang="en-us" Name="commandSet_en-us">
 <CommandPrefix>TestCortana</CommandPrefix>
 <Example>Tell me WindowsPhone</Example>
 

 <Command Name="voiceCommand1">
 <Example> Tell me you </Example>
 <ListenFor> [Tell me] {msgText} </ListenFor>
 <Feedback> OK, Tell you {msgText} </Feedback>
 <Navigate Target="TestPage1.xaml"/>
 </Command>

 <PhraseTopic Label="msgText" Scenario="Short Message"/>
 </CommandSet>

</VoiceCommands>

Comandタグ

一つの音声コマンドはCommandタグで定義されます。

Exampleタグはサンプルを指定します。

ListenForタグ

ListenForは認識したい音声コマンドを指定します。

でくくられた部分は固定値を表します。また、{}は同じxml内のLabel属性を参照します。

 

でTell meをくくることによって、Tell meという固定値を表し、{}でmsgTextをくくることで同じxml内のPharaseTopicタグのmsgTextラベルを指定します。

Label属性がmsgTextなタグは、Senario属性にShort Messageとあります。こう指定することで任意のショートメッセージとなります。

 

つまり、ListenForの部分を [Tell me ]{msgText} とすることで、"アプリ名 Tell me 任意のメッセージ"というパターンを認識することができます。

Feedbackタグ内では、認識し終わった後に返すメッセージです。

Navigateタグ

Navigateタグは音声認識後、遷移したいxamlページを指定します。

しかしNavigateタグにxamlを指定しても実際に遷移してくれることはなく、認識後にパラメータとして渡されるだけです。

 

Cortanaにコマンドを登録する

VCDファイルを作成することができたら、アプリ起動時にCortanaに音声コマンドを登録するようにしましょう。

StorageFile file=await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///assets/testvcd.xml"));
await VoiceCommandManager.InstallCommandSetsFromStorageFileAsync(file);

StorageFileのGetFileFromApplicationUriAsyncメソッドVDCファイルを取得し、

VoiceCommandManagerのInstallCommandFromStorageFileAsyncメソッドで音声コマンドを登録します。

 

 

音声コマンドによるアプリの起動を制御する

Cortanaに上記の処理でコマンドを登録したあとは、Cortanaに向かって音声コマンドをいうと特殊な操作でアプリを起動することができます。

App.xaml.csのOnActivatedメソッドをオーバーライドし、下記の処理を追加します。

protected override void OnActivated(IActivatedEventArgs args)
{
    base.OnActivated(args);
    if (args.Kind == ActivationKind.VoiceCommand)
    {
        var commandArgs = args as VoiceCommandActivatedEventArgs;
        SpeechRecognitionResult speechRecognitionResult = commandArgs.Result;

        var text = speechRecognitionResult.Text;
        var status = speechRecognitionResult.Status;

        string navigateTarget = speechRecognitionResult.SemanticInterpretation.Properties["NavigationTarget"][0];
        string commandMode=speechRecognitionResult.SemanticInterpretation.Properties["commandMode"][0];
        string msgText = speechRecognitionResult.SemanticInterpretation.Properties["msgText"][0];

        if (navigateTarget == "TestPage1.xaml")
        {
            Frame rootFrame = new Frame();
            Window.Current.Content = rootFrame;
            
            rootFrame.Navigate(typeof(TestPage1),msgText);
            Window.Current.Activate();
        }
        
        

    }
}

Cortanaの音声コマンドによるアプリの起動は、引数としてActivationKind.VoiceCommandという値が送られてくるので判断することができます。

SpeechRecognitionResult型の値を取得し、Textプロパティは送られてきた音声コマンドを表し、Statusプロパティは認識できたかどうかを表します。

 

SemanticInterpretation.PropertiesはVCDに指定した様々な値を取得することができます。

その中でもNavigationTargerはVCDに指定したxamlページの文字列が返ってくるのでそれに応じてページ遷移を行いましょう。

 

Cortanaで音声コマンドを実行してみる

ここまでできたら、マニフェストファイルの機能タブにマイクの使用許可を与え、実行してみます。

2

 

 

まず起動時にCortanaに音声コマンド登録を行います。

3

 

その後、Cortanaを起動し、音声コマンドを入力します。

今回の場合ListenForに [Tell me] {msgText} と指定したので、"TestCortana Tell me hogehoge"と入力すれば起動できるはずです。

5

今回はTestCortana Tell me WindowsPhoneと入力してみます。

音声コマンドがうけとられ、Cortanaからアプリを起動することができました。

6

Lumia620の保護シートとバックカバーを買った

ちょっとまえに格安で手に入れたWindowsPhone8.1のLumia620の保護シートとバックカバーが売ってたので買っちゃいました。

保護シートはなぜかパッケージがi○honeでしたが下の方にLumia620と表記されていたので若干心配ながら開封しました。

f:id:garicchi:20140717003144j:plain

保護シートはアンチグレアのものを買ったのですが、端末に貼ったあと変な粘着物がめっちゃついて、不良品か!!??って思いましたが普通に粘着物を手でとったら綺麗にとれました。

製品自体は結構ちゃんとしていて、さらさらのさわりごごちになったのですごくお勧めです。

f:id:garicchi:20140717003420j:plain

バックカバーはオレンジのものを購入しました。 思ったよりビビッドなオレンジでしたが、WindowsPhoneっぽくてよいかなと。Lumia620はバックカバーを変更できるので気分によって変えてもいいですね。

生まれ変わったLumia620がこんな感じ

f:id:garicchi:20140717003517j:plain

フロントにでてる黄色い枠がカッコイイです。 結構いい買い物をしたなーと思いました。全国のWindowsPhoneユーザーさん、アンチグレアシートおすすめです。

WindowsPhone8.1でデータを保存する6つの方法

完全に釣りタイトルです。

Office365毎日更新ブログで有名な@kazuakixさんのブログで興味深い記事がありました。

Windows Phone 8.1 でアプリの設定を保存する - kazuakix の日記

そうです、WindowsPhone8.1からWindowsRuntimeAPIに統合されたのでIsolatedStorageはAplicationDataに置き換えられました。

こうすることでWindowsストアアプリとの差がほとんどなくなりました。 (ちなみにIsolatedStorageはWindowsPhone8.1ではつかえなくなってました)

汎用的でレベルの高いデータ保存はつもりんやむっつ氏が書いてくれてるので僕は初歩的なところをまとめていきたいです。

WinRTでアプリの設定を保存する - tmytのらくがき

gist:5fdf58f42800a3b620ae

設定データとファイルデータ

まず、抑えて置かなければいけない概念として、WindowsRuntimeでは設定データとファイルデータの2つのデータ保存方法があります。

単純な設定データなどは設定データとして~Settingに保存します。 大きいデータやファイル単位で保存したいデータは~Folder内にファイルを作って保存します。

設定データは、ApplicationDataContainerクラスで作られていて、キーそれに対応する値のセットでつくられます。

ファイルデータは、StorageFolderクラスとして保存するフォルダーを取得することができるのでそこにファイルとして書き込みます。

具体的には、ApplicationDataクラスの中に、

設定データ(ApplicationDataContainer型)
  • LocalSetting - ローカルの設定データ
  • RoamingSetting - ローミング設定データ
ファイルデータを入れるフォルダー(StorageFolder型)
  • LocalFolder - ローカルファイルフォルダー
  • RoamingFolder - ローミングファイルフォルダー
  • TemporaryFolder - 一時的なデータを保存するファイルフォルダー
  • LocalCacheFolder - 一時データ?

の6つのプロパティがあり、そこにデータを入れることになります。

f:id:garicchi:20140714130709p:plain

LocalCacheFolderについては、いまだMSDNにも情報がなく、おそらくはローカルのキャッシュデータ保存用フォルダだとは思いますが、一時データ保存用フォルダということにしときます。

ローカルデータとローミングデータ

さらに、WindowsRuntimeでは複数のデバイス間でデータを同期できるようにデータローミングという概念を採用しています。 Roamingがつく設定やフォルダーにデータを入れると、データはOneDriveで同期され、別のデバイスでも同じデータを扱うことができます。 ユニバーサルアプリの場合、「アプリをストアに関連付ける」をした後、WindowsアプリとWindowsPhoneアプリ間でデータがローミングされます。

f:id:garicchi:20140714130531p:plain

ローミングデータは、データサイズに上限があり、それ以上のデータを入れると同期されません。

データサイズの取得方法はRoamingStorageQuotaで取得できます。

ulong size=ApplicationData.Current.RoamingStorageQuota;

では、具体的なデータ保存、復元方法を見て行きましょう。

ローカル設定データ

ローカル設定データは、LocalSettingというコンテナーにキーと値をセットにしてデータを保存します。

public void SaveTextToLocalSetting(string key,string value)
{
    ApplicationDataContainer container = ApplicationData.Current.LocalSettings;
    container.Values[key] = value;
}

データを復元するときは、ContainsKeyを使ってキーがすでにあるかどうかを判定しましょう。

public string LoadTextFromLocalSetting(string key)
{
    ApplicationDataContainer container = ApplicationData.Current.LocalSettings;
    if (container.Values.ContainsKey(key))
    {
        return container.Values[key].ToString();
    }
    else
    {
        return null;
    }
}

ローミング設定データ

データが同期される設定データは、RoamingSettingに保存します。 保存方法はLocalSettingと同じですが、この設定データは異なるデバイス間の同じアプリで同期されます。

public void SaveTextToRoamingSetting(string key, string value)
{
    ApplicationDataContainer container = ApplicationData.Current.RoamingSettings;
    container.Values[key] = value;
}

データ復元

public string LoadTextFromRoamingSetting(string key)
{

    ApplicationDataContainer container = ApplicationData.Current.RoamingSettings;
    if (container.Values.ContainsKey(key))
    {
        return container.Values[key].ToString();
    }
    else
    {
        return null;
    }
}

ローカルファイルデータ

大きいファイルを保存する場合は、LocalFolderにStorageFolderを作って保存しましょう。

StorageFildeのCreateFileAsyncメソッドを使ってファイルを作ります。

今回はFileIOクラスのWriteTextAsyncでテキストデータを書き込みます。

public async Task SaveTextFileToLocalFolderAsync(string fileName, string value)
{
    StorageFolder folder = ApplicationData.Current.LocalFolder;
    StorageFile file = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
    await FileIO.WriteTextAsync(file,value);
    
}

もし、byteデータを書き込みたい場合はこちらを御覧ください

byteデータを書き込むには | garicchi.com

byteデータを読み込むには | garicchi.com

データ復元は、GetFileAsyncでデータを取得します。

その前に、GetFilesAsyncでファイル一覧を取得し、データが保存されているかどうかを判定します。

public async Task<string> LoadTextFileFromLocalFolderAsync(string fileName)
{
    StorageFolder folder = ApplicationData.Current.LocalFolder;
    if ((await folder.GetFilesAsync()).Where(q => q.Name == fileName).Any())
    {
        StorageFile file = await folder.GetFileAsync(fileName);
        return await FileIO.ReadTextAsync(file);
    }
    else
    {
        return null;
    }
}

ローミングファイルデータ

ファイルもデータローミングすることができます。

RoamingFolderに先ほどと同じようにファイルをつくり、書き込みます。

public async Task SaveTextFileToRoamingFolderAsync(string fileName, string value)
{
    StorageFolder folder = ApplicationData.Current.RoamingFolder;
    StorageFile file = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
    await FileIO.WriteTextAsync(file, value);

}

データ復元

public async Task<string> LoadTextFileFromRoamingFolderAsync(string fileName)
{
    StorageFolder folder = ApplicationData.Current.RoamingFolder;
    if ((await folder.GetFilesAsync()).Where(q => q.Name == fileName).Any())
    {
        StorageFile file = await folder.GetFileAsync(fileName);
        return await FileIO.ReadTextAsync(file);
    }
    else
    {
        return null;
    }
}

一時データ

一時データは、TemporaryFolderに保存します。

このフォルダ内のデータは、一時データとはキャッシュのようなもので、システムによってシステムメンテナンスや任意の時点で削除されてしまうので、データの長期保存を保証しません。

データ保存

public async Task SaveTextFileToTemporaryFolderAsync(string fileName, string value)
{
    StorageFolder folder = ApplicationData.Current.TemporaryFolder;
    StorageFile file = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
    await FileIO.WriteTextAsync(file, value);

}

データ復元

public async Task<string> LoadTextFileFromTemporaryFolderAsync(string fileName)
{
    StorageFolder folder = ApplicationData.Current.TemporaryFolder;
    if ((await folder.GetFilesAsync()).Where(q => q.Name == fileName).Any())
    {
        StorageFile file = await folder.GetFileAsync(fileName);
        return await FileIO.ReadTextAsync(file);
    }
    else
    {
        return null;
    }
}

ローカルキャッシュ

ローカルキャッシュはLocalCacheFolderに保存します。 ローカルキャッシュフォルダーの定義としては、 「バックアップと復元の対象にならないファイルの保存先にできるローカルのアプリデータストア内のフォルダーを取得」 とあるのですが、アプリを一度終了してみても、データは復元できたのでTemporaryフォルダーと同じ扱いと考えました。 こちらも長期保存は保証していません。

データ保存

public async Task SaveTextFileToLocalCacheFolderAsync(string fileName, string value)
{
    StorageFolder folder = ApplicationData.Current.LocalCacheFolder;
    StorageFile file = await folder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
    await FileIO.WriteTextAsync(file, value);

}

データ復元

public async Task<string> LoadTextFileFromLocalCacheFolderAsync(string fileName)
{
    StorageFolder folder = ApplicationData.Current.LocalCacheFolder;
    if ((await folder.GetFilesAsync()).Where(q => q.Name == fileName).Any())
    {
        StorageFile file = await folder.GetFileAsync(fileName);
        return await FileIO.ReadTextAsync(file);
    }
    else
    {
        return null;
    }
}

リンク

アプリの設定を保存するには | garicchi.com

アプリの同期設定を保存するには | garicchi.com

アプリの設定データに階層構造を持ったデータを保存するには-ApplicationDataCompositeValue編 | garicchi.com

アプリの設定データに階層構造を持ったデータを保存するには-複数コンテナー編 | garicchi.com

アプリの設定データに階層構造を持ったデータを保存するには-JSONシリアライズ編 | garicchi.com

テキストファイルを書き込むには | garicchi.com

テキストファイルを読み込むには | garicchi.com

byteデータを書き込むには | garicchi.com

byteデータを読み込むには | garicchi.com

アプリの同期ファイルを保存するには | garicchi.com

アプリの一時データを保存するには | garicchi.com

StorageFileをアプリデータとして取り込むには | garicchi.com

Nokia Lumia620を手に入れました

f:id:garicchi:20140608164959j:plain

この間、Lumia630を買おうとしていたら(実際には注文したが)@okazuki さんがLumia620を格安で譲ってくれるとのことだったのでLumia620を手に入れることができました。

個人的にはWPユーザーというよりはWPアプリ開発者のほうなので実機デバッグができればローエンド端末でよかったので格安でいただけて、学生には経済的に助かりました。

f:id:garicchi:20140608165238j:plain

なぜかいただけたのは僕の好きなマゼンタ?ピンク?よくわからないけどそんな感じの色です。

f:id:garicchi:20140608165330j:plain

ノキアのローエンド端末にありがちな厚さです。これくらいあついと持ちやすいです。

f:id:garicchi:20140608165417j:plain

言語とロケールを英語にしてコルタナとしゃべったり。結構僕の発音も聞き取ってくれて噂通りすごいです。この認識性能をBingの音声認識APIとして出してくれればいいなとか思ったり

f:id:garicchi:20140608164551j:plain

カメラが5Mpということで結構きれいに撮影できました。

f:id:garicchi:20140608165713j:plain

びっくりしたのは3年前にXNAで作ったゲームがちゃんと動いたことですねー。 わりと楽しい

使ってみて、快適さはスペック相当な感じです。 コアなWPユーザーはもうちょっとハイエンド端末のほうがいいかも? まだデバッグしてませんがデバッグするぐらいのユーザーなら安くていいと思いました。

P.S. 本当に留年しそうです

アプリから簡単にTwitterにつぶやくには

TweetButtonのURL

アプリから簡単に情報をシェアできればアプリの情報をフォロワーに発信できてアプリの告知能力が大幅に広がります。 たとえばゲームアプリなどでスコアなどをシェアするなどがよく使用されている方法です。

しかし、わざわざシェアするためだけにアプリからTwitterOAuth認証を行い、アカウント管理を行ってつぶやくのはめんどくさいです。 WindowsiOSのようにOSレベルでTwitterへのツイートをサポートするAPIはありません。Windowsはそういうときのために共有コントラクトというものを用意して、共有します。

しかし、共有コントラクトになれないユーザーが多いのでもっと直感的にツイートできるようにしてみましょう。

WebページにはTwitterにシェアするTweetButtonというものが設置してあります。 TweetButtonは特定のURLにHTTPリクエストを送ることで簡単に特定のテキストをつぶやくことができます。

TweetButtonからツイートすると、アカウントの管理をしなくてもブラウザからログインし、アカウントを継続して使いたい場合はブラウザのキャッシュを、アカウントを切り替えたい場合はブラウザからログアウト→ログインを行うことでアカウント管理を行わなくてもつぶやけます。

今回は、このTweetButtonをアプリ内で実装して、つぶやかせます。

TweetButtonには特殊なURLがあります。

https://twitter.com/intent/tweet?text=hogehoge

このURLにブラウザからアクセスしてみると、つぶやくことができます。

なので、アプリのWebViewからこのURLにアクセスすれば、簡単につぶやくことができるというわけです。

まず、アプリにWebViewを配置しましょう。

<Page
    x:Class="App4.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App4"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid>
        <WebView x:Name="webViewTweet"></WebView>
    </Grid>
    
</Page>

そして、つぶやきたいタイミングでWebviewにNavigateします。

string url = "http://garicchi.com";
string hashtag = "hashtag";
string text = "hogehoge";
string urlStr = Uri.EscapeUriString("https://twitter.com/intent/tweet?" + "text=" + text + "&hashtags=" + hashtag + "&url=" + url);

Uri shareUri = new Uri(urlStr, UriKind.Absolute);

webViewTweet.Navigate(shareUri);

これだけで簡単につぶやくことができます。

f:id:garicchi:20140522210525p:plain

WindowsPhoneもばっちりです。

f:id:garicchi:20140522210537p:plain

アプリにWebViewなんて配置したらUIがくずれるよ!って人は、Flyoutを使うことでもっと簡単にできます。 FlyoutとWebViewをC#から動的につくって、Buttonを押したらFlyoutを表示させればできます。

string url = "http://garicchi.com";
string hashtag = "hashtag";
string text = "hogehoge";
string urlStr = Uri.EscapeUriString("https://twitter.com/intent/tweet?" + "text=" + text + "&hashtags=" + hashtag + "&url=" + url);

Uri shareUri = new Uri(urlStr, UriKind.Absolute);

var flyout = new Flyout();
flyout.Placement = FlyoutPlacementMode.Full;
var webView = new WebView();
webView.Navigate(shareUri);
flyout.Content = webView;

buttonFlyout.Flyout = flyout;

f:id:garicchi:20140522210713p:plain