しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

Xamarin.Android で Runtime Permissions を実装した時にはまったのでメモ

最近は Xamarin.Android も少しだけ弄っていますが、Android 6.0 で追加された Runtime Permissions に対応している時に、よく分からない挙動ではまったのでメモしておきます。

その挙動とは RequestPermissions で複数のパーミッションの確認を行い、結果として全てのパーミッションが取れているのかを LINQ でサクッと確認しようとした時でした。ちなみに以下のようなコードを書きました。

[Activity(Label = "PermissionTest", MainLauncher = true, Icon = "@mipmap/icon")]
public class MainActivity : Activity, ActivityCompat.IOnRequestPermissionsResultCallback
{
    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        // Set our view from the "main" layout resource
        SetContentView(Resource.Layout.Main);

        // Get our button from the layout resource,
        // and attach an event to it
        Button button = FindViewById<Button>(Resource.Id.myButton);

        button.Click += (sender, e) =>
        {
            ActivityCompat.RequestPermissions(this, new[]
            {
                Manifest.Permission.WriteExternalStorage, Manifest.Permission.Camera
            }, 0);
        };
    }

    public void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
    {
        if (grantResults.All(x => x == Permission.Granted))
        {
            new AlertDialog.Builder(this)
                           .SetTitle("Runtime Permission")
                           .SetMessage("All Granted")
                           .SetPositiveButton("OK", (_, __) => {  })
                           .Show();
        }
    }
}

実行すると、iOS のようにパーミッションの確認ダイアログが表示されます。今回の場合は複数のパーミッションを要求しているので、複数回表示されます。

そして OnRequestPermissionsResult の中で grantResults が全て Permission.Granted かどうかを確認したつもりですが、実行してみるとアプリケーションが例外で終了してしまいました。

しかも、その例外の内容がとても不思議でした。

[Mono] Assembly Ref addref PermissionTest[0xad36ad80] -> System.Core[0xad36baa0]: 3
[] System.Int32[] doesn't implement interface System.Collections.Generic.IEnumerable<Android.Content.PM.Permission>
[libc] Fatal signal 6 (SIGABRT), code -6 in tid 1807 (.permissiontest)

grantResults は Permission の配列なはずですが、実際には int の配列として渡されてきているようです。デバッガーで止めて確認すると、本当に何故か int が渡されていました。

Xamarin.Android の不具合なのかも知れませんが、とりあえず Permission にキャストし直すことにしました。

public void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
{
    if (grantResults.Cast<Permission>().All(x => x == Permission.Granted))
    {
        new AlertDialog.Builder(this)
                       .SetTitle("Runtime Permission")
                       .SetMessage("All Granted")
                       .SetPositiveButton("OK", (_, __) => {  })
                       .Show();
    }
}

これで All がちゃんと動作するので、パーミッションが全て取れていればダイアログが出るようになりました。

かなり気持ち悪い挙動ですが、何度も触る部分では無いので妥協しました。Android 6.0 だけではなく、以前のバージョンでも同じ挙動になるみたいなので、微妙に注意が必要な気がします。