tmytのらくがき

個人の日記レベルです

Essential Phone PH-1を買いました

去る2018年7月16日(太平洋夏時間)にアメリカのAmazon.comでPH-1が249USDだったのでせっかくだから買ってみました。

Amazon.com向けのHalo Grayっていうやつ。フロント全面ガラスはやっぱりいいものだ…

とりあえずPを焼く

探せばすぐに出てくるけどPの焼き方

  1. https://www.essential.com/developer/developer-preview からOTAパッケージをダウンロードする
  2. USBデバッグを有効にする
  3. adb reboot recoverty
  4. ”!”が出たら、下のどちらかの操作をする*1
    • 音量下を押しながら電源を押して、さらに音量上
    • 電源を押しながら音量上
  5. Apply update from ADBを選択
  6. adb sideload <<ZIPのパス>>

そのほか

  • ガラスは素敵
  • さらっとした裏面も素敵
  • モノクロカメラはちょっと楽しい
    • アプリから使う方法は謎

*1:僕は2個目だったけど、白を買った友人は上の操作だったらしい。

Windows Phone 8.1向けのAristeaについて

Aristeaは新規ダウンロードを停止していますが既存ユーザのみなさま向けにちまちまバージョンアップを続けています。

5月半ばごろに、Windows向けリリースはWindows 10 15063以降のみのサポートに変更しました。 ですが、Windows 10 MobileおよびWindows Phone 8.1向けは歴史的経緯によりWindows Phone 8.1以降を継続していました。

いままでサポートが続いていたWindows 8.1向けリリースですが、次の次のリリースをもってサポートOSを次のように変更する予定をしています。

平たく言うと、Windows 10 Mobile (ビルド15063)未満がサポート対象外となって、アップデートが配信されなくなります。 15063が配信されてない端末の皆様には残念なお知らせですが、さすがにそろそろ古い環境と整合性を取りつつ最新の機能の取り込みが苦しくなってきた*1のでお許しください…

14393サポートないと困るよ!という声がたくさんあれば考え直すかもしれません…

*1:のとUWP移行が整ってきた

InlineUIContainerで追加したImageがOverflowしたときに非表示にする

UWPのRichTextBlockとInlineUIContainer周りでなんだか微妙な気持ちになりました。せっかくなのでエントリしておきます。

TL;DR

  • RichTextBlockに追加したUIElementはOverflowしても非表示にならない
  • InlineUIContainerからGetCharacterRectで矩形を取得して表示判定をする
  • SizeChangedとかで表示非表示コードをいい感じに実行する

いい感じに動いてほしいコード

UWPのRichTextBlockはInlineUIContainerクラスを経由すると任意のUIElementを子要素として持つことができます。 たとえば、RichTextBlockの中に画像をインライン表示したい。とかがよくある要件かと思います。

これを簡単に実現するとこんな風なXAMLになります。

<RichTextBlock x:Name="Text" TextTrimming="CharacterEllipsis" TextWrapping="NoWrap" IsTextSelectionEnabled="False">
    <Paragraph>
        <Run Text="aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa"></Run>
        <Run Text="aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa"></Run>
        <InlineUIContainer>
            <Border Background="Blue">
                <Image Source="Assets/Square150x150Logo.png"
                       Height="{Binding ElementName=Text, Path=FontSize}"
                       Stretch="Uniform" MinWidth="5"/>
            </Border>
        </InlineUIContainer>
    </Paragraph>
</RichTextBlock>

なんかうじゃうじゃ書いてますが、実行するとこういう画面が表示されます。

f:id:tmyt:20180702013821p:plain

期待する結果は表示しきれなくなって文字列が"..."で省略された時にこの画像部分が非表示になってほしいのですが、 何もしないとこうなります。

f:id:tmyt:20180702013850p:plain

釈然としませんが、SizeChangedあたりであふれたかどうか判定をしたうえで、Opacity = 0にするようなコードを書くと期待した結果になります。

うまく動かすコード

このコードをSizeChangedあたりで実行すると、なんかいい感じになります。

foreach (var inline in ((Paragraph)Text.Blocks[0]).Inlines)
{
    if(!(inline is InlineUIContainer container)) continue;
    var uiElement = (FrameworkElement)container.Child;
    var elementStart = container.ElementStart;
    var elementEnd = container.ElementEnd;
    var rect1 = elementStart.GetCharacterRect(elementStart.LogicalDirection);
    var rect2 = elementEnd.GetCharacterRect(elementEnd.LogicalDirection);
    uiElement.Opacity = rect1.Left == rect2.Left ? 0 : 1;
}

ただし、このコードはいくつかの決め打ち要素が含まれています。

  • TextというRichTextBlockがある
  • RichTextBlockのBlocksは1個だけ、しかもそれはParagraph
  • Paragraphの中にネストしたParagraphは存在しない

RichTextBlockが決め打ちなのは各自使いやすくしていただくとして、 Blocksの中身が2個以上だったり、Paragraphじゃない場合があったり、 Windows Runtime環境下では、Blockの派生クラスとして実装されているのはParagraphのみでした。 Paragraph直下以外でInlineUIContainerを含む場合の対応が必要な場合は 各自カスタムして使ってください。

これを実装して、実行するとこんな結果になります。

f:id:tmyt:20180702013934p:plain

UIElementが非表示になっていい感じの結果です。見えないだけで水色っぽいところに配置されています。

ちょっとだけ解説

TextPointer.GetCharacterRectは隣接するテキスト境界のバウンディングボックスを返すような関数らしいです*1。 これを呼び出したとき、コード中のrect1.Leftrect2.Leftは内包するUIElementがあふれていない場合異なる値を返します*2

しかし、あふれている場合に呼び出すとrect1.Leftrect2.Leftは同じ値になります。以降どの文字を調べても同じ値が出てきます。 どうやらこの値は、RichTextBlock.ActualWidth - "...の幅"くらいの値になってるようです*3

という挙動から、2つのRectのLeftプロパティが同値の場合はOpacity = 0とすることで、UIElementを非表示にしています。 ここで、Visibility = Hiddenにすると、2つのLeftプロパティが同値になってしまい再度表示する場合の判定ができなくなります。 また、UIElementのActualWidthが0になると同様に判定に失敗します。今回はMinWidth = 5とし、最低5px確保することでActualWidthが0になることを回避しました。

気が向いたらBehaviorにするかもしれません。

*1:https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.documents.textpointer.getcharacterrect#Windows_UI_Xaml_Documents_TextPointer_GetCharacterRect_Windows_UI_Xaml_Documents_LogicalDirection_

*2:だいたい rect1.Left + UIElement.ActualWidth == rect2.Left になります。厳密にはちょっと違う

*3:内部的には"..."の手前に全部幅0で表示してますよ。という感じなのかもしれない

TaskCompletionSource<T>が覚えられない

.NETのSystem.Threading.Tasks空間に、TaskCompletionSource<T>っていうのがあります。

TaskCompletionSource(TResult) クラス (System.Threading.Tasks)

これを使うと、こういうコードが書けます。

public Task Sleep(int milliseconds)
{
    var source = new TaskCompletionSource<bool>();
    var timer = new Timer(_ =>
    {
        source.SetResult(true);
    }, null, milliseconds, Timeout.Infinite);
    return source.Task;
}

このコードは、実際はTask.Delayと書けばいいので特に意味はないですが、外からTaskをCompleteできる便利なやつです。

最近のECMAScriptだとこう書けるあれです。

function sleep(millis)
{
    return new Promise(done => {
      setTimeout(done, millis);
    });
}

普段使わないけど、1年に1回ぐらい使うことがあって思い出せないのでメモです。

Chrome TimelineのFirefox版を作っておきました

Chrome Timelineを見たひとが、Firefox版ほしいって言ってるを見かけたのでとりあえず作ってみました。

本当は、名前も”Firefox Timeline”にしようと思ったんです。そしたらデベロッパーポリシーに"Firefoxという名前をアドオンに含める場合は「** for Firefox」という表現を使用すること”とあったので、”Timeline for Firefox”になっていますが、中身は一緒です。

最近はChrome Extensionが標準化されてWeb Extensionとして動作するのでほとんどコード修正せずに動くのは楽ちんですね。

Timeline for Firefox – Firefox 向けアドオン