tmytのらくがき

個人の日記レベルです

UWPでExpression.UnaryPlusが使えない

知っていても得しない豆知識です。UWPでこれを実行すると例外が出ます。

Expression.Lambda(Expression.UnaryPlus(Expression.Constant(10)))
    .Compile();

f:id:tmyt:20180504012545p:plain

どうやらUWPの.NETはUnaryPlusをサポートしていないようです。 UnaryPlusは何もしない演算子とほぼ等価なので未実装なんでしょう。おそらく。

たまたまFreesiaでUnaryPlusを呼び出していて、例外が出て初めて気づきました。

Windows 10 17134で追加されたRefreshContainerを使う

Windows 10 April 2018 Update (version 1803)*1でUWPにも引っ張って更新コントロールが標準実装されました。Windowsストアアプリと言われていた時代から数えて、約6年かかりました…

使いですが、docs.comを参照するのが手っ取り早いです。

Pull-to-refresh - UWP app developer | Microsoft Docs

このページに記載のサンプルをみると、なんだかうじゃうじゃ書いてますが必要なところだけ切り出すとこれだけです。

<RefreshContainer>
    <ListView />
</RefreshContainer>

サンプルにHeightプロパティが設定されていたのが気になったのですが、これを指定しなくてもとりあえず問題にはならないようです。*2

今回実装されたRefreshContainerは、引っ張れればなんでもOKなのでListView以外に、今回追加されたTreeViewや、独自実装のItemsControl、ItemsControlですらないCanvasやGridでもOKです。 引っ張る方向も、上から下、右から左…と4種類前パターン選択可能です。

次に更新イベントのハンドリングですが、こんな感じになります。

private async void RefreshContainer_OnRefreshRequested(RefreshContainer sender, RefreshRequestedEventArgs args)
{
    using (args.GetDeferral())
    {
        await _vm.Refresh();
    }
}

using GetDeferral()の中で更新処理を呼び出す。するとusingを抜けるまで、画面上で更新中のステートが表示される。という仕組みです。ここにDeferral突っ込んでくるなんて、UWP的だなぁと思いました。


さて、実践投入したいのはやまやまなんですが、このコントロールが動くのは17134以降。リリースされてからまだ3日。さすがにターゲットバージョンを17134にはできない。ということで、15063でこっそり追加されていた"条件付きXAML"という機能を使って 17134未満は"Windows Community Toolkit"のPullToRefreshListViewを、17134以上はRefreshContainerを使うようにしてみます。 プロジェクトには"Microsoft.Toolkit.Uwp.UI.Controls"をNuGetからインストールしておいてください。

プロジェクトのプロパティでターゲットバージョンを17134、最小バージョンを15063にします。

f:id:tmyt:20180503180213p:plain

17134はRefreshContainerに必要、15063は"条件付きXAML"に必要です。

次にXAML書きます。

<Page
    x:Class="App2.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:is16299="using:Microsoft.Toolkit.Uwp.UI.Controls?IsApiContractNotPresent(Windows.Foundation.UniversalApiContract, 6)"
    xmlns:is17134="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 6)"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <is16299:PullToRefreshListView RefreshRequested="PullToRefreshListView_OnRefreshRequested" ItemsSource="{Binding Items}"/>
        <is17134:RefreshContainer RefreshRequested="RefreshContainer_OnRefreshRequested" x:Name="RefreshContainer">
            <ListView ItemsSource="{Binding Items}" />
        </is17134:RefreshContainer>
    </Grid>
</Page>

?IsApiContractPresentとかついてるあたりが条件付きXAML、この場合は条件付き名前空間と呼びます。is16299の行が波線になったりしますが実行できます。大丈夫です。 またReSharperが有効な環境では、条件付き名前空間が指定されたタグの中ではコード補完が効かなくなりますが大丈夫です。ちゃんと実行できます。 さらにさらに、条件付き名前空間のタグにx:Nameで名前を付けると、C#からその名前は見つかるけどメンバがReSharperのコード補完に出てこない。とかも起きますがちゃんと実行できます。コンパイルも大丈夫です。

ここまでで環境によって表示されるListViewを切り替えることができたので、更新処理とかを書いておきます。

using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;

namespace App2
{
    public sealed partial class MainPage : Page
    {
        public class ViewModel
        {
            public ObservableCollection<string> Items { get; } =
                new ObservableCollection<string>(new[] { "0", "1", "2" });

            public async Task Refresh()
            {
                await Task.Delay(2000);
                Items.Insert(0, DateTime.Now.ToString());
            }
        }

        private ViewModel _vm = new ViewModel();

        public MainPage()
        {
            this.InitializeComponent();
            DataContext = _vm;
        }

        private async void RefreshContainer_OnRefreshRequested(RefreshContainer sender, RefreshRequestedEventArgs args)
        {
            using (args.GetDeferral())
            {
                await _vm.Refresh();
            }
        }

        private async void PullToRefreshListView_OnRefreshRequested(object sender, EventArgs e)
        {
            _vm.Refresh();
        }
    }
}

PullToRefreshListViewは更新処理をCommandで書けるのでそちらを使うのがいいかと思います。 RefreshContainerはイベントしかないので、x:Bindでイベントバインディング*3するといいんじゃないでしょうか。

ItemTemplateもリソースに切り出して共有すればそんなに面倒なことにもならなさそうです。RefreshContainerと条件付きXAML、ぜひ活用してください。

*1:ビルド17134

*2:例えば仮想化されなくなる。とか考えられましたが外してみても特に問題はなかったです。

*3:https://docs.microsoft.com/ja-jp/windows/uwp/xaml-platform/x-bind-markup-extension#event-binding

技術書典4にサークル参加してきました

去る2018年4月22日に、秋葉原UDX AKIBA SQUAREで開催された『技術書典4』にサークル参加してきました。

今回の新刊はこれ。

ドコモ Z-01K (ZTE)の端末レビューあり、アプリの動作検証あり、非公開APIの呼び出しありの34ページ本でした。

今回のこだわりポイントはこれ!でした。

  • 判型が210mmx100mm (Z-01Kの約1.4倍のサイズ!)
  • 表紙の紙が和風!

最後に感想。

  • 技術書典4すごいたのしかった
  • 人多かった、びっくり
  • また参加しようと思いました

Let's EncryptがWildcard証明書に対応したから発行してみた

Let's Encryptが2018年1月末頃にはWildcard証明書を発行する予定!と言っていたのがやっとリリースされました!(わーぱちぱち)

せっかくなので発行してみました。いまのところ、コマンドラインから--serverとv02エンドポイントを明示する必要があるそうです。

途中さらっと流してますが、Wildcard証明書はDNS-01でドメインを検証しないといけないので、Certbotの指示に従ってDNSのTXTレコードを設定しています。 このドメインはConoHaでホストされているのでAPIで設定することもできますが、今回はWebから手動で変更しました。

**@onsen-tech:~/certbot$ ./certbot-auto --server https://acme-v02.api.letsencrypt.org/directory -d *.onsen.tech --manual --preferred-challenges dns-01 certonly
Requesting to rerun ./certbot-auto with root privileges...
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): **@**.com

-------------------------------------------------------------------------------
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
-------------------------------------------------------------------------------
(A)gree/(C)ancel: a

-------------------------------------------------------------------------------
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about EFF and
our work to encrypt the web, protect its users and defend digital rights.
-------------------------------------------------------------------------------
(Y)es/(N)o: y
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for onsen.tech

-------------------------------------------------------------------------------
NOTE: The IP of this machine will be publicly logged as having requested this
certificate. If you're running certbot in manual mode on a machine that is not
your server, please ensure you're okay with that.

Are you OK with your IP being logged?
-------------------------------------------------------------------------------
(Y)es/(N)o: y

-------------------------------------------------------------------------------
Please deploy a DNS TXT record under the name
_acme-challenge.onsen.tech with the following value:

********

Before continuing, verify the record is deployed.
-------------------------------------------------------------------------------
Press Enter to Continue
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/onsen.tech-0001/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/onsen.tech-0001/privkey.pem
   Your cert will expire on 2018-06-11. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot-auto
   again. To non-interactively renew *all* of your certificates, run
   "certbot-auto renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

**@onsen-tech:~/certbot$

これをNginxに設定して…ってアクセスするとサブドメイン付きのドメインしか指定されてないのでサブドメインのついてないところにアクセスするとエラー。なのでSANで作り直しました。

**@onsen-tech:~/certbot$ ./certbot-auto --server https://acme-v02.api.letsencrypt.org/directory -d onsen.tech -d *.onsen.tech --manual --preferred-challenges dns-01 certonly
Requesting to rerun ./certbot-auto with root privileges...
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None

-------------------------------------------------------------------------------
You have an existing certificate that contains a portion of the domains you
requested (ref: /etc/letsencrypt/renewal/onsen.tech-0001.conf)

It contains these names: *.onsen.tech

You requested these names for the new certificate: onsen.tech, *.onsen.tech.

Do you want to expand and replace this existing certificate with the new
certificate?
-------------------------------------------------------------------------------
(E)xpand/(C)ancel: e
Renewing an existing certificate
Performing the following challenges:
dns-01 challenge for onsen.tech
dns-01 challenge for onsen.tech

-------------------------------------------------------------------------------
NOTE: The IP of this machine will be publicly logged as having requested this
certificate. If you're running certbot in manual mode on a machine that is not
your server, please ensure you're okay with that.

Are you OK with your IP being logged?
-------------------------------------------------------------------------------
(Y)es/(N)o: y

-------------------------------------------------------------------------------
Please deploy a DNS TXT record under the name
_acme-challenge.onsen.tech with the following value:

*******

Before continuing, verify the record is deployed.
-------------------------------------------------------------------------------
Press Enter to Continue

-------------------------------------------------------------------------------
Please deploy a DNS TXT record under the name
_acme-challenge.onsen.tech with the following value:

********

Before continuing, verify the record is deployed.
-------------------------------------------------------------------------------
Press Enter to Continue
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/onsen.tech-0001/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/onsen.tech-0001/privkey.pem
   Your cert will expire on 2018-06-11. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot-auto
   again. To non-interactively renew *all* of your certificates, run
   "certbot-auto renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

**@onsen-tech:~/certbot$

無事発行できました。

f:id:tmyt:20180314082906p:plain

Windows版のChromiumに高精度タッチパッド対応がマージされた

最近のWindowsラップトップには高精度タッチパッドってのがついてたりついてなかったりするわけですが、これに対応するパッチがChromeに取り込まれました。 なにが嬉しいかって言うと、Chromeタッチパッドでピンチイン/アウトしたときにCtrl+Wheelを生成していたのがタッチパネルで拡大したときと同じ挙動になります。Mac版のChromeとかEdgeみたいな挙動です。

2018年3月14日現在、Chrome Canaryでしか使えませんし、コマンドラインからフィーチャーを有効にする必要があります。

chrome.exe --enable-features=PrecisionTouchpad

とすると使えるようになります。このパッチめちゃくちゃ嬉しいですね。

Twitterはクソ(2)

Aristeaその他のサスペンドについて、現状のご報告です。

AAAPIを申請したら、その申請に対する返答としてアプリとアカウントが凍結されてしまったわけですが、その後異議を申し立てたところ2週間無視されました。 その後、アカウント凍結の窓口から新規に問い合わせたところ、回答はこれ。

Hello,

Your account has been suspended because multiple applications registered to your account (or accounts) have been found in violation of the Developer Agreement and Policy, including:

  • Developer Agreement: Sections II.A.3, II.A.5
  • Developer Policy: Section I.F.5.a

In order for this account to be unsuspended, you should log into the Twitter Applications Management system (https://apps.twitter.com/) and delete all but one of the applications registered to your account.

Once you have completed that action, respond to this notice indicating that you have complied, and understand the requirements in the Developer Agreement and Policy prohibiting the creation of multiple applications for the same use case.

If you believe your account has been suspended in error, or wish to provide more details about the use cases for these applications, please reply to this message.

Regards, Twitter Platform Operations

これに対して異議を申し立てたところ、これ。

Hello,

Thanks for the follow up. Would you please explain the differences between:

  • Azurea - app ID xxxx
  • Azurea for Windows - app ID xxx
  • Amelloides - app ID xxx

Regards, Twitter Platform Operations

それぞれの違いを説明したところ、返答はこれ。

Hello @tmyt,

Thank you for the additional information. We have reviewed your request and have reactivated your API keys and account. Going forward, carefully ensure that you adhere to Twitter’s policies:

  • Developer Agreement and Policy
  • Automation Rules
  • Twitter Rules

Regards, Twitter Platform Operations

つまり、アカウントとAPIキーは復活させたよ、これからもTwitterのPolicyを遵守しろよ。という注意付き。だから違反してねーって言ってるんだけど。 だけども、1日経ってもAPIキーが復活されないので、APIキーが凍結されている窓口から問い合わせ。そのときに、Ref***でReactivateしたって言ったよね?なんでReactivateされてないの????というコメント付きで。 すると返答はこれ。

Hello @tmyt,

Thank you for reaching out. We have reviewed your request and have reactivated your API key. Going forward, carefully ensure that your application adheres to Twitter’s policies:

  • Developer Agreement and Policy
  • Automation Rules
  • Twitter Rules

Please note, applications found to be violating policy again may be subject to permanent suspension.

Regards, Twitter Platform Operations

問い合わせをレビューして、APIキー復活させたよ、これからもTwitterのPolicyを遵守しろよ。とまた小言付き。けれどもやっぱり復活されないのでもう一度問い合わせたらこれ。

Hello,

Thanks for reaching out. Your app has been restricted or suspended due to one or more violations of our Developer Agreement and Policy, Automation Rules, Display Requirements and/or Twitter Rules. Common reasons for this happening include:

  • Users reporting content posted through your app as spam (Tweets, RTs, favs, DMs, etc.)
  • Abusive use of automations, like auto-following/unfollowing
  • Suspicious API usage, such as repeatedly hitting rate limits
  • Display violations regarding how you must show Tweets

This list is not exhaustive, so review our policy documentation to ensure that your app and your app’s users are following all of our rules.

Please respond to this email if you need further clarification, but note that we will only consider a request to lift this restriction after you provide us with the following information:

  • App ID and consumer key
  • Link to and instructions on how to log into your app to review it for compliance
  • A list of specific changes that you have made to resolve policy non-compliance

We will not respond to requests to lift restrictions that do not include this information.

Regards, Twitter Platform Operations

は????ふざけてんの?そもそもReactivateされてないのにhas been restricted or suspendedってなんなの?あんたらレビューしてReacitvateするって言ったんじゃないの?全く意味がわからないしこれでDeveloper Friendlyとかなめてんのか?ブチギレながらもとりあえず、相手の指示に従って、メールを返したけれどもアメリカ時間で土日だったので返答はまだ。この先どうなるかちょっとわからんです。

大変ご迷惑をおかけして申し訳ありません。

Twitterのサスペンドから復活しました

Twitterから凍結されて2週間とちょっとが経ちましたが、やっと凍結が解除されました。 一応Twitter的にはAPIキーも凍結を解除したといっているのですが、現時点でまだ解除されていません。 アメリカ時間で明日の朝になっても解除されてなければもう一度メールしてみようと思います。

利用者の皆様には大変ご迷惑をおかけいたしました。