tafuji's blog

C#, Xamarin, Azure DevOps を中心に書いています。

Xamarin.Essentials 入門 - #9 クリップボード

はじめに

Xamarin.Essentials のクリップボード API について記載します。この API では、アプリのクリップボードのテキストの取得・設定の APIクリップボードのデータが変更したときのイベントハンドラが提供されます。

使ってみよう

使い方

Xamarin.Essentials のクリップボードAPI は、Clipboard クラスで提供されており、以下のことができます。

// クリップボードにテキストが含まれているかを判定する
var hasText = Clipboard.HasText;

// クリップボードにテキストをコピーする
await Clipboard.SetTextAsync("Hello World");

// テキストをクリップボードから取得する
var text = await Clipboard.GetTextAsync()

// クリップボードの内容が変更されたときのイベント処理
Clipboard.ClipboardContentChanged += OnClipboardContentChanged;

void OnClipboardContentChanged(object sender, EventArgs e)
{
    Console.WriteLine($"Last clipboard change at {DateTime.UtcNow:T}";);
}

読んでみよう

Clipboard のコードを読んで、プラットフォーム固有の処理を見ていきます。

iOS

クリップボードの操作は、UIPasteboard を利用して、クリップボードの操作をしていることが分かります。以下は、iOS プラットフォームのコードの抜粋です。

static Task PlatformSetTextAsync(string text)
{
    UIPasteboard.General.String = text;
    return Task.CompletedTask;
}

static NSObject observer;

static bool PlatformHasText
    => UIPasteboard.General.HasStrings;

static Task<string> PlatformGetTextAsync()
    => Task.FromResult(UIPasteboard.General.String);

クリップボードの内容が変更されたときのイベント処理は、NSNotificationCenter.DefaultCenter.AddObserver を利用して、クリップボードが変更を観測する Observer を登録しています。

static void StartClipboardListeners()
{
    observer = NSNotificationCenter.DefaultCenter.AddObserver(
        UIPasteboard.ChangedNotification,
        ClipboardChangedObserver);
}

static void StopClipboardListeners()
    => NSNotificationCenter.DefaultCenter.RemoveObserver(observer);

static void ClipboardChangedObserver(NSNotification notification)
    => ClipboardChangedInternal();

Android

クリップボードの操作は、ClipboardManager を利用して、クリップボードの操作をしていることが分かります。以下は、Android プラットフォームのコードの抜粋です。

 static Task PlatformSetTextAsync(string text)
{
    Platform.ClipboardManager.PrimaryClip = ClipData.NewPlainText("Text", text);
    return Task.CompletedTask;
}

static bool PlatformHasText
    => Platform.ClipboardManager.HasPrimaryClip && !string.IsNullOrEmpty(Platform.ClipboardManager.PrimaryClip?.GetItemAt(0)?.Text);

static Task<string> PlatformGetTextAsync()
    => Task.FromResult(Platform.ClipboardManager.PrimaryClip?.GetItemAt(0)?.Text);

イベント処理は、ClipboardManager.IOnPrimaryClipChangedListener インタフェースを実装したイベントリスナークラスを作成し、ClipboardManager クラスにイベントリスナーを登録、削除していることが分かります。

class ClipboardChangeListener : Java.Lang.Object, IOnPrimaryClipChangedListener
{
    void IOnPrimaryClipChangedListener.OnPrimaryClipChanged() =>
        Clipboard.ClipboardChangedInternal();
}

static void StartClipboardListeners()
    => Platform.ClipboardManager.AddPrimaryClipChangedListener(clipboardListener.Value);

static void StopClipboardListeners()
    => Platform.ClipboardManager.RemovePrimaryClipChangedListener(clipboardListener.Value);

UWP

クリップボードの操作では、Windows.ApplicationModel.DataTransfer.Clipboard クラスの各種メソッドを利用して、アプリの情報を取得していることが分かります。以下は、UWP プラットフォームのコードの抜粋です。

static Task PlatformSetTextAsync(string text)
{
    var dataPackage = new DataPackage();
    dataPackage.SetText(text);
    WindowsClipboard.SetContent(dataPackage);
    return Task.CompletedTask;
}

static bool PlatformHasText
    => WindowsClipboard.GetContent().Contains(StandardDataFormats.Text);

static Task<string> PlatformGetTextAsync()
{
    var clipboardContent = WindowsClipboard.GetContent();
    return clipboardContent.Contains(StandardDataFormats.Text)
        ? clipboardContent.GetTextAsync().AsTask()
        : Task.FromResult<string>(null);
}

イベント処理も、Windows.ApplicationModel.DataTransfer.Clipboard クラスのイベントハンドラに共通のイベント処理を登録していることが分かります。

static void StartClipboardListeners()
    => WindowsClipboard.ContentChanged += ClipboardChangedEventListener;

static void StopClipboardListeners()
    => WindowsClipboard.ContentChanged -= ClipboardChangedEventListener;

static void ClipboardChangedEventListener(object sender, object val) => ClipboardChangedInternal();

まとめ

参考

Xamarin.UITest Tips - Xamarin.Forms WebView 利用時のメモ

はじめに

とある案件で、Xamarin.iOS の WKWebView を利用したアプリを Xamarin.UITest で自動化することがあって、 Xamarin.UITest のドキュメントを確認すると、app.Query(c=>c.Class("WKWebView")) で WKWebView を取得することができると記載されていました。

念のため、Xamarin.Forms の場合はどうなるか、確認してみました。

まとめ

Xamarin.Forms の場合でも iOS では、app.Query(x =>x.Class("WKWebView")) のように Class メソッドの引数に WKWebView を指定して WebView を取得する必要がある。

試したこと

Xamarin.Forms で WebView が配置された簡単なサンプルアプリを Xamarin.UITest で WebView が取得できるかを確認しました。 REPL を起動して、app.Query(x => x.WebView()) で試してみましたが、予想通り何も取得できせんでした。

app.Query(c=>c.Class("WKWebView")) で試してみると、下の図のように取得することができました。

Repl

参考:サンプル

以下のサンプルを利用しました。

サンプルの一部を抜粋しておきます。

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="WebViewSample.LoadingLabelXaml"
             Title="Loading Demo">
    <StackLayout>
        <!--Loading label should not render by default.-->
        <Label x:Name="labelLoading" Text="Loading..." IsVisible="false" />
        <WebView HeightRequest="1000" 
             WidthRequest="1000" 
             Source="https://dotnet.microsoft.com/apps/xamarin" 
             Navigated="webviewNavigated" 
             Navigating="webviewNavigating" />
    </StackLayout>
</ContentPage>

Xamarin.Essentials 入門 - #8 アプリのテーマ

はじめに

Xamarin.Essentials のアプリのテーマについて記載します。この API を利用することで、システムから要求されているテーマを取得することができます。

使ってみよう

使い方

Xamarin.Essentials のアプリのテーマの機能は、AppInfo クラスで提供されています。

AppTheme appTheme = AppInfo.RequestedTheme;

返却される AppTheme 列挙体は以下のいずれかの値になります。

namespace Xamarin.Essentials
{
    public enum AppTheme
    {
        Unspecified, // 指定されていない
        Light, // ライトテーマ
        Dark // ダークテーマ
    }
}

読んでみよう

AppInfo のコードを読んで、プラットフォーム固有の処理を見ていきます。

iOS

テーマ情報の取得には、UITraitCollection から UserInterfaceStyle プロパティの値を取得して、システムから要求されているテーマを判定していることが分かります。以下は、iOS プラットフォームのコードの抜粋です。

static AppTheme PlatformRequestedTheme()
{
    if (!Platform.HasOSVersion(13, 0))
        return AppTheme.Unspecified;

    var uiStyle = Platform.GetCurrentUIViewController()?.TraitCollection?.UserInterfaceStyle ??
        UITraitCollection.CurrentTraitCollection.UserInterfaceStyle;

    return uiStyle switch
    {
        UIUserInterfaceStyle.Light => AppTheme.Light,
        UIUserInterfaceStyle.Dark => AppTheme.Dark,
        _ => AppTheme.Unspecified
    };
}

なお、iOS 13 より前のバージョンの環境では、AppTheme.Unspecified が返されます。

Android

Android.Content.Res.Configuration クラスの UiMode プロパティからシステムに要求されているテーマを判定しています。以下は、Android プラットフォームのコードの抜粋です。

static AppTheme PlatformRequestedTheme()
{
    return (Platform.AppContext.Resources.Configuration.UiMode & UiMode.NightMask) switch
    {
        UiMode.NightYes => AppTheme.Dark,
        UiMode.NightNo => AppTheme.Light,
        _ => AppTheme.Unspecified
    };
}

UWP

アプリのテーマの取得には、Windows.UI.Xaml.Application クラスの RequestedTheme プロパティが利用されていることがわかります。このプロパティの値は、Windows.UI.Xaml.ApplicationTheme 列挙体で Dark 又は、Light の値を持ちます。

static AppTheme PlatformRequestedTheme() =>
            Application.Current.RequestedTheme == ApplicationTheme.Dark ? AppTheme.Dark : AppTheme.Light;

まとめ

  • Xamarin.Essentials のアプリのテーマでは、以下の機能が提供される
    • システムから要求されているアプリのテーマに関する情報を取得することができる
  • 内部では、以下のネイティブ固有のAPI を利用している

参考