tmytのらくがき

個人の日記レベルです

Windows 10 2004のエクスペリエンスっていう項目が気になったので探した

Windows 10 2004 May 2020 Updateにするとシステムのバージョン情報に"Windows Feature Experience Pack"というのが表示されるようになりました。らしい。この環境はInsider Fast Ringなので若干バージョンが違うけれども、2004でも同じことができます。

f:id:tmyt:20200620205541p:plain

Feature on Demand版のドキュメントを見てみると

This Feature on Demand package includes features critical to Windows functionality. Do not remove this package.

  • Capability Name: Windows.Client.ShellComponents~~~~0.0.1.0
  • Sample package name: Microsoft-Windows-UserExperience-Desktop-Package~31bf3856ad364e35~amd64~~.cab

と、いうことらしい…中身が気になったので探してみます。

おそらくこれはAppX形式だろう。ということで、PowerShellでパッケージを探してたのがこれ。

PS> Get-AppxPackage | Where-Object { $_.Version -eq "120.13701.0.0" }
Name              : MicrosoftWindows.Client.CBS
Publisher         : CN=Microsoft Windows, O=Microsoft Corporation, L=Redmond, S=Washington, C=US
Architecture      : X64
ResourceId        :
Version           : 120.13701.0.0
PackageFullName   : MicrosoftWindows.Client.CBS_120.13701.0.0_x64__cw5n1h2txyewy
InstallLocation   : C:\Windows\SystemApps\MicrosoftWindows.Client.CBS_cw5n1h2txyewy
IsFramework       : False
PackageFamilyName : MicrosoftWindows.Client.CBS_cw5n1h2txyewy
PublisherId       : cw5n1h2txyewy
IsResourcePackage : False
IsBundle          : False
IsDevelopmentMode : False
NonRemovable      : True
Dependencies      : {Microsoft.VCLibs.140.00_14.0.27810.0_x64__8wekyb3d8bbwe}
IsPartiallyStaged : False
SignatureKind     : System
Status            : Ok

このパッケージは C:\Windows\SystemApps に入ってるらしいので、管理者権限のコマンドラインから様子を見てみることにします。

PS> dir

    ディレクトリ: C:\Windows\SystemApps\MicrosoftWindows.Client.CBS_cw5n1h2txyewy

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        2020/06/14      7:20                AppxMetadata
d-----        2020/06/14      7:20                Assets
d-----        2020/06/14      7:20                InputApp
d-----        2020/06/14      7:19                pris
d-----        2020/06/14      7:20                ScreenClipping
d-----        2020/06/14      7:20                WindowsInternal.ComposableShell.Experiences.SuggestionUIUndocked
-a----        2020/06/13      7:19         877169 AppxBlockMap.xml
-a----        2020/06/13      7:19          58385 AppxManifest.xml
..省略..

AppXらしいデータが入ってました。中身が気になるのでAppxManifest.xmlを眺めてみます。

> Get-Content .\AppxManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3" xmlns:uap4="http://schemas.microsoft.com/appx/manifest/uap/windows10/4" xmlns:uap6="http://schemas.microsoft.com/appx/manifest/uap/windows10/6" xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10" xmlns:wincap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/windowscapabilities" IgnorableNamespaces="mp uap uap3 uap4 uap6 uap10 wincap">
        <Identity Name="MicrosoftWindows.Client.CBS" Publisher="CN=Microsoft Windows, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" Version="120.13701.0.0" ProcessorArchitecture="x64"/>
        <mp:PhoneIdentity PhoneProductId="3898fac3-1f84-4040-83ce-ef30739c0a64" PhonePublisherId="6145b212-a274-4517-8abe-1e015f21ff03"/>
        <Properties>
                <DisplayName>Windows Feature Experience Pack</DisplayName>
                <PublisherDisplayName>Microsoft Windows</PublisherDisplayName>
                <Logo>Assets\StoreLogo.png</Logo>
        </Properties>
..省略..

こういうのがだらだら続くのだけれども、めっちゃ長いのでピックアップして。

どうやらInputMethod的ななにかだそうです。

<Application Id="InputApp" Executable="TextInputHost.exe" EntryPoint="WindowsInternal.ComposableShell.Experiences.TextInputUndocked.InputApp.App">
..省略..
  <Extensions>
    <!-- VS's manifest validation won't accept an empty Extensions section but it is required for
  fragment merging, so add a dummy protocol entry -->
    <uap:Extension Category="windows.protocol">
      <uap:Protocol Name="ms-inputapp" DesiredView="useMinimum">
        <uap:DisplayName>Input App</uap:DisplayName>
      </uap:Protocol>
    </uap:Extension>
    <uap3:Extension Category="windows.appExtension">
      <uap3:AppExtension Name="com.microsoft.windows.input.app" Id="InputApp" PublicFolder="Public" DisplayName="Input Experience">
      </uap3:AppExtension>
    </uap3:Extension>
  </Extensions>
</Application>

Windpws+Shift+Sで出てくるあれの実体がここに入っているそうです。

<Application Id="ScreenClipping" Executable="ScreenClippingHost.exe" EntryPoint="ScreenClippingHost.App">
..省略..
  <Extensions>
    <uap:Extension Category="windows.protocol">
      <uap:Protocol Name="ms-screenclip">
        <uap:DisplayName>ms-screenclip</uap:DisplayName>
      </uap:Protocol>
    </uap:Extension>
    <uap3:Extension Category="windows.appExtension">
      <uap3:AppExtension Name="com.microsoft.windows.app.screenclip" Id="ScreenClippingApp" PublicFolder="Public" DisplayName="Screen Snipping Experience"/>
    </uap3:Extension>
    <uap3:Extension Category="windows.appExtensionHost">
      <uap3:AppExtensionHost>
        <uap3:Name>com.microsoft.windows.protocoloverride</uap3:Name>
      </uap3:AppExtensionHost>
    </uap3:Extension>
  </Extensions>
</Application>

今のところこれくらいしか中身はないのだけれども、EntryPointの名前とかパッケージの名前とかを見るとそのうちいろいろな実体がここに移されてくるんだろうなぁ…という感じでした。

シーリングライトを力押しでGoogle Assistantに対応させる (その2)

4か月前に作ったこれを、毎日使っていたのですが、今日ふと電話置き場を眺めてみるとこんなことに。あらやだ。

f:id:tmyt:20200501005509p:plain

電池って4か月ちょいでこんなことになってしまうんですね、こわいこわい。

さて、我が家のシーリングライト危機が訪れました。解決方法として考えられたのは

  1. 別のAndroidを生贄にする
  2. バッテリーのないAndroidを手に入れる

1は、まだまだあるので大丈夫といえば大丈夫なんですが4か月ごとに電話が死んでいくとなるとそれはさすがに困ります。 2は、あまり数は無く、あっても大抵が産業用なのでかなり高い。

そこで3つ目の解決方法がふと思いつきました。Raspberry Pi 3にAndroidをインストールして、そこで今までのシステムを動かす。 全開の図はこんな感じにアップデートされます。

f:id:tmyt:20200501010401p:plain

かなりスマートになりました。adbを使っていた部分はもはやAndroidの内側になってしまったのでADBは不要になり、ローカルのコマンドを実行するだけになりました。

Raspberry Pi 3 でAndroid を実行する

この作戦はRaspberry Pi 3 でAndroid が起動してかつBluetoothが比較的まともに動くことが条件でした。 今回は2個試して、ちゃんと使えたLineageOS 16.0を使います。

konstakang.com

インストールはビルド済みイメージをダウンロードして、SDに書き込み、Raspberry Pi 3にカードを入れて起動すれば数分後にはAndroidの初回セットアップ画面が起動します。

セットアップはそこそこにすすめて、いつものAndroidと同じように開発者オプションを有効にして、ADBを有効にします。加えて、Rootアクセスも有効にしておきました。 これで基本的なセットアップはおしまい。

シーリングライトの専用アプリをインストールする

専用アプリがないとライトを操作できないので、専用アプリをどうにかしてインストールします。

Gappsをインストールする方法もあるのですが、このアプリはGappsなしでも動作するので ほかのAndroidバイスからapkをコピーしました。コピー方法は割愛。

Bluetoothも正しそうに動いてBLEでライトと通信できる状態になりました。

NodeJSを実行する

この仕組みはインターネット上で動作しているNodeJSサーバが、LANの内側で動作しているNodeJSプロセスにSocket.IO通してGoogle Assistantからのコマンドを転送する仕組みになっていました。 大きく書き直すのは大変、そしてAndroidはなんだかんだLinuxなのでNodeJSが動くでしょう。ということでAndroid上でNodeJSを実行できるようにします。

Androidで使えるNodeJSのバイナリを探してみたものの、今風のバージョンは見つからず、ソースからビルドするのもいまいちうまくいかない。 そこで、Termuxをインストールし、その中で apt install nodejs した結果インストールされたバイナリを使うことにしました。

TermuxはAndroid上で動作するLinux環境のようなものです。公式にAPKが配布されているのでダウンロードしてadb install しました。

termux.com

インストールが終わったら、Termuxを立ち上げてそこからはLinuxの世界なのでてきとうにNodeJSをインストールします。

$ apt update
$ apt install nodejs

これでNodeJSがインストールできました。使う時は /data/data/com.termux/files/usr/bin/node を参照するといいです。

おしまい

あとはこのNodeJS環境でこのプログラムを実行するだけで今まで通りシーリングがON/OFFできます。うれしいですね!

const socket = require('socket.io-client')('https://***.com');
const { exec } = require('child_process');
const util = require('util');

const X = [136, 248];
const Y = [189, 373, 555];

const on = `${X[0]} ${Y[0]}`;
const off = `${X[1]} ${Y[2]}`;
const dimm = `${X[0]} ${Y[2]}`;

socket.on('connect', () => {
  console.log('connect');
});
socket.on('execute', data => {
  const input = data.inputs[0];
  for(const execution of input.payload.commands[0].execution){
    if(execution.command === 'action.devices.commands.OnOff'){
      const arg = execution.params.on ? on : off;
      exec(`input touchscreen tap ${arg}`);
    }
    if(execution.command === 'action.devices.commands.BrightnessAbsolute'){
      const arg = execution.params.brightness ? on : dimm;
      exec(`input touchscreen tap ${arg}`);
    }
  }
  console.log(util.inspect(data, true, null));
});

一つだけ問題があって、HDMIに何もつながってないと起動しません。そして起動後HDMIを抜くと、

05-01 00:07:25.610   188   267 I hwc-drm-connector: force mode to 1280x720@0Hz
05-01 00:07:25.613   188   267 I hwc-drm-two: Unplug event @1596363841 for connector 29 on display 0
05-01 00:07:25.634  3546  3546 I HWC2    : Destroying display 0
05-01 00:07:25.637  3546  3546 E HWComposer: isConnected failed for display 0: Invalid display
05-01 00:07:25.637  3546  3546 E HWComposer: getPresentFence failed for display 0: Invalid display
05-01 00:07:25.637  3546  3582 E HWComposer: onVsync Failed to find display 0
05-01 00:07:25.637  3546  3546 E HWComposer: isConnected failed for display 0: Invalid display
05-01 00:07:25.637  3546  3546 E HWComposer: getPresentFence failed for display 0: Invalid display
05-01 00:07:25.651  3616  3639 I DisplayManagerService: Display device removed: DisplayDeviceInfo{"内蔵スクリーン": uniqueId="local:0", 1280 x 720, modeId 3, defaultModeId 3, supportedModes [{id=1, width=1280, height=720, fps=50.0}, {id=2, width=1280, height=720, fps=59.9402}, {id=3, width=1280, height=720, fps=60.0}], colorMode 0, supportedColorModes [0], HdrCapabilities android.view.Display$HdrCapabilities@40f16308, density 160, 54.186 x 53.788 dpi, appVsyncOff 1000000, presDeadline 16666667, touch INTERNAL, rotation 0, type BUILT_IN, state ON, FLAG_DEFAULT_DISPLAY, FLAG_ROTATES_WITH_CONTENT, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS}
05-01 00:07:25.684  3616  3631 E AndroidRuntime: *** FATAL EXCEPTION IN SYSTEM PROCESS: ActivityManager
05-01 00:07:25.684  3616  3631 E AndroidRuntime: java.lang.IllegalArgumentException: Can't remove the primary display.
05-01 00:07:25.684  3616  3631 E AndroidRuntime:        at com.android.server.am.ActivityStackSupervisor.handleDisplayRemoved(ActivityStackSupervisor.java:4337)
05-01 00:07:25.684  3616  3631 E AndroidRuntime:        at com.android.server.am.ActivityStackSupervisor.access$300(ActivityStackSupervisor.java:193)
05-01 00:07:25.684  3616  3631 E AndroidRuntime:        at com.android.server.am.ActivityStackSupervisor$ActivityStackSupervisorHandler.handleMessage(ActivityStackSupervisor.java:4733)
05-01 00:07:25.684  3616  3631 E AndroidRuntime:        at android.os.Handler.dispatchMessage(Handler.java:106)
05-01 00:07:25.684  3616  3631 E AndroidRuntime:        at android.os.Looper.loop(Looper.java:193)
05-01 00:07:25.684  3616  3631 E AndroidRuntime:        at android.os.HandlerThread.run(HandlerThread.java:65)
05-01 00:07:25.684  3616  3631 E AndroidRuntime:        at com.android.server.ServiceThread.run(ServiceThread.java:44)
05-01 00:07:25.685  3616  3703 I InputReader: Reconfiguring input devices.  changes=0x00000004
05-01 00:07:25.694  3616  3631 I Process : Sending signal. PID: 3616 SIG: 9
05-01 00:07:26.130   200   200 I lowmemorykiller: lmkd data connection dropped
05-01 00:07:26.131   200   200 I lowmemorykiller: closing lmkd data connection
05-01 00:07:26.152  3549  3597 W AudioFlinger: power manager service died !!!
05-01 00:07:26.152  3788  3803 W Sensors : sensorservice died [0xaba4dc00]
05-01 00:07:26.155   149   149 I ServiceManager: service 'telecom' died
05-01 00:07:26.155   149   149 I ServiceManager: service 'contexthub' died
(...省略)
05-01 00:07:26.166   149   149 I ServiceManager: service 'imms' died
05-01 00:07:26.166   149   149 I ServiceManager: service 'autofill' died
05-01 00:07:26.166   149   149 I ServiceManager: service 'profile' died

DisplayがなくなったのでDisplayManagerがDisplayを削除して…するとActivityManagerが対応したディスプレイを削除…しようとしたけどPrimaryなので削除できない!!といったあとシステムがダウンしていったのでした。悲しい。

その結果、部屋の真ん中にRaspberry Piが放置されることに…

f:id:tmyt:20200501012531p:plain

G8X WideMode 1.1.2は動きません

おしらせです。

G8X WideMode ver1.1.2というのを今朝6時ごろリリースしたのですが、ワイドモードスイッチが動作しないバグを埋め込みました。 10時頃に修正版をPlayストアにアップロードして現在*1配信処理中です。

そろそろいけるかな?と思っていたのですがまだ配信されていないのでAPKを置いておきます。 1drv.ms

ついでに、アプリの名前が"WideMode for LG" になりました。兄弟機でもぼちぼち動くようなので名前を変えた次第です。中身は変わりません。

*1:2020/3/28 15:00

X1D IIからAndroidでRAWをダウンロードしてみる

専用アプリがなぜかiPadにしかなくて、せめてiPhoneには出してよ…とおもいつつ、いろいろあれこれした結果がこちらです。

github.com

FontAwesome ProのフォントファイルはさすがにGitに乗せられないので、FreeのOTFを入れて参照するフォントを変えるか、ProのDuotoneを入れるとたぶん動きます。

Unoを真面目に使ってみた

Uno Platform、実際に使った話をあまりきかない*1のでちょっくら真面目に使ってみるかってことで使ってみたのがこちらのソースコードになります。

感想

  • 案外動く
  • 仮想化が怪しい
  • TcpClientがまともに動いたのはびっくりしました

ほぼ同じコードで動いていて、違うコードを書かないといけなかったのは

  • Bluetooth LEの接続/通信
  • Wifiの自動接続
  • ファイルの書き出し先の決定(iOSのフォトとか、Androidの外部ストレージとか)

で、それ以外は同じコードでできた。まぁまぁいい感じだと思いました。

開発中だし仕方ないんだけど、怪しい箇所はちらほらあって

  • ActualWidthが取れない
  • CommandBarのボタンがなぜか押せない(iOS)
  • ListViewのImageのSourceにBindingすると仮想化の闇に飲まれる(Android)
  • ScrollViewerのZoomを使うと中の要素のレイアウトが壊れる(iOS)

とか確認しました。

*1:Hello Worldしかみんなやらない問題

NEO-M8UでUDRしてみたい!

UDR(Untethered Dead Reckoning)というのがあります。GNSSが受信できない状態で自車位置を推定する技術のうち、車両信号を利用しないものをこう呼ぶそうです。

u-bloxというスイスのメーカーからリリースされているNEO-M8シリーズのうち、NEO-M8UというモジュールがこのUDRをサポートしています。

NEO-M8Uは、3軸ジャイロと3軸加速度センサを内蔵していてセンサを通して車両の挙動を推定してくれます。なんかしらの計算結果を位置情報に反映してNMEA形式で推定結果を出力してくれるのでとても賢くて使いやすいんじゃないかと思ってこれを使います。

改造する

さっそくNEO-M8Uを使えるようにしたいと思います。まず、M5StackのGPSモジュールを用意します。このGPSモジュールはNEO-M8Nというモジュールが使用されています。似たような名前だけどこっちはUDRできないやつ。なんでM5StackのGPSモジュールを用意したかというと

  • M5Stackが使いたかった
  • M8Uの製品情報に "Compatible with all modules of the NEO family" と書いてあるのでNEOシリーズが搭載されているやつを用意した

という理由です。先に上げた通り、M8UはNEOシリーズの全モジュールと完全な互換性があるそうです。つまり、剥がして貼りなおせば使えるはず。

改造に必要なGPSモジュールとM8UはDigi-keyで用意しました。3日くらいで届いたのでDigi-keyさまさま。

さて改造といっても、剥がして

f:id:tmyt:20200202055408p:plain

貼るだけ

f:id:tmyt:20200202055436p:plain

剥がしスキルが低すぎて、PPSのランドが剥がれてしまったのはそのうち適当に配線しよう…

UDR使えるか試す

散々M5Stackがどうこう言っていたのにGPS部分しか持っていないのでまずはUARTをPCへ接続してu-centerで様子を見ます。Navigation Expert 5を確認すると最初からADR*1/UDRが有効になってました。

f:id:tmyt:20200202052710p:plain

次に自動車へ設置。センサーで自動車の挙動を推定するので車両にしっかり固定しろよな!ということなのでとりあえずテープで止めました。

f:id:tmyt:20200202055546p:plain

このままGNSS信号が受信できている状態でしばらく走っていると、センサーのキャリブレーションが終わってUDRが使えるようになるはず…とドキュメントに書いていました。 3D+DGNSSの状態で15分くらい車を走らせていると、キャリブレーションが完了したようです。

f:id:tmyt:20200202055612p:plain

あとはこのままGNSS信号が受信できない場所へ行けばUDRの動作が確認できるはず!!!ということで近場のトンネルへ出かけてみたところ、UDRの動作が確認できました。

f:id:tmyt:20200202055731p:plain

出口付近は推定結果がちょっと怪しめ。距離が長くなると誤差が累積してしまうので仕方ないといえば仕方ない。固定場所とかの条件にもよるのかもしれない。

f:id:tmyt:20200202055753p:plain

ちなみに、UDR中のNMEAメッセージを見ると

$GNGLL,3438.68839,N,13525.04534,E,181743.00,A,E*7D

ちゃんとModeがEになっているのでちゃんと測位情報がDRで計算されてます。

まとめ

  • UDRができました
  • M8Uは仕組み上車両に固定しないといけないからマイコンで遊ぶ用にはなかなかリリースしてもらえなくて寂しい
  • キャリブレーションが割と時間かかるのが難点
  • UDRの性能は割と悪くないと思うけど長いトンネルとかはちょっと苦しそう

*1:車両と電気的接続をするやつ。GNSS以外を使用して自車位置推定をする機能ってことでひとまとめになってる

Uno PlatformでmacOSアプリを作れるかな?

GitHubのissue*1を眺めていると、どうやらNuGetにXamarin Mac対応のバイナリが放流されているらしい?

NuGetのバイナリを手に入れて、展開してみると確かにxamarinmac20向けのバイナリが含まれているみたい。

$ unzip -l 2.0.512-dev.4178.nupkg | grep xamarinmac20
     4039  2019-11-27 03:05   build/xamarinmac20/uno.ui.targets
     4927  2019-11-27 03:11   build/xamarinmac20/Uno.UI.Tasks.targets
  6022624  2019-11-27 03:47   lib/xamarinmac20/Uno.dll
    84960  2019-11-27 03:47   lib/xamarinmac20/Uno.Foundation.dll
    24096  2019-11-27 03:18   lib/xamarinmac20/Uno.Foundation.pdb
   937116  2019-11-27 03:19   lib/xamarinmac20/Uno.pdb
  4546016  2019-11-27 03:47   lib/xamarinmac20/Uno.UI.dll
  1340020  2019-11-27 03:20   lib/xamarinmac20/Uno.UI.pdb
   196576  2019-11-27 03:47   lib/xamarinmac20/Uno.Xaml.dll
   712192  2019-11-27 03:18   lib/xamarinmac20/Uno.Xaml.pdb

macOSではまだHello Worldが動く程度らしいけど、一応ビルドはできるらしい。

ということはcsprojをいい感じに整えてあげれば好きなようにアプリがつくれる?気がしたのでとりあえずプロジェクトテンプレートにmacOSなプロジェクトを追加してみました。

github.com

これを使うとmacOS向けのプロジェクトがソリューションに増えます。

f:id:tmyt:20200130044454p:plain

これをmacでビルドするとiOSと似たような結果が得られて一安心。

f:id:tmyt:20200130044541p:plain

ただ、macOS向けのUno Platformは絶賛開発中なので2020年1月30日現時点で

  • TextBoxが未実装
  • Button, CheckBox, RadioButton は押しても反応しない
  • Selector系コントロールは例外が出る

という感じなのでパッチを書きつつ応援していきましょうね!

G8X ThinQ向け、別画面でブラウザを開くアプリをリリースしました

ダウンロード

今回は最初からPlayストアに公開しました。

play.google.com

機能

  1. デフォルトブラウザにしておくと、カバー画面有効時に別画面でURLを開きます
  2. カバー画面が使えない場合*1時はURLのハンドルを自動で無効にできます*2

機能1について

このアプリをデフォルトブラウザに設定しておくと、ほかのアプリ(ex. Twitter) でURLを開くときにこのアプリがブラウザの代わりになります。

ブラウザとして開かれたこのアプリは、即座に現在アクティブでない側の画面でブラウザを起動します。この時使われるブラウザは、アプリの設定にあるものが使われます。設定される前や、設定した後にそのブラウザが削除された場合は初回のみブラウザ選択画面が出ることがあります。

機能2について

カバー画面の有効/無効に合わせて、このアプリのブラウザの代わりになる機能を一時的に有効/無効を自動的に切り替える機能があります。この機能を有効にすると、Chrome Custom Tabsなどでアプリ内ブラウザを実装しているアプリはカバー画面が無効の時はアプリ内ブラウザが起動するようになります。

まとめるとこういう動作になります。

||自動切換え有効|自動切換え無効| |カバー画面有効|別画面でブラウザ起動|別画面でブラウザ起動| |カバー画面無効|アプリ内ブラウザ|同じ画面でブラウザ起動|

この機能はイベントを受け取るためだけのユーザー補助サービスとして実装されているので必要であれば有効にしてください。*3

設定

今回はランチャにアイコンが追加されてその画面から使用するブラウザの設定ができます。あとはデフォルトのブラウザに設定しておけばOKです。

*1:カバーがない、画面がOff、カバーを開ききってる

*2:ユーザー補助サービスを有効にすると使えます

*3:ユーザー補助サービスは筋悪いかな…とおもいつついったんこれでお願いします。