詳細トピック

これらのセクションは参照用であり、目を通す必要はありません。 あります。

フレームワーク API を使用する:

これらの API は、より一貫性のある API サーフェス( UserHandle オブジェクトを避けます)。現時点では、これらを直接呼び出すことができます。

実装は簡単です。操作できる場合は、先に進んでください。そうでない場合 ユーザーにメッセージ、バナー、ツールチップなどをリクエストして表示できます。ユーザーが [Settings] に移動し、リクエスト インテントを作成して、 Context#startActivity: ユーザーをそこに移動します。ブロードキャストを使用して このアビリティがいつ変更されたかを検出したり、ユーザーが 戻ります。

これをテストするには、仕事用プロファイルで TestDPC を開く必要があります。 接続中のアプリの許可リストにパッケージ名を追加することを選択します。この 管理者の「許可リストへの登録」を模倣する説明します。

用語集

このセクションでは、クロス プロファイル開発の開発に関連する主な用語を定義します。

クロス プロファイル構成

クロス プロファイル設定では、関連するクロス プロファイル プロバイダをグループ化します。 クラス。クロス プロファイル機能の一般的な構成を提供します。 通常、コードベースごとに 1 つの @CrossProfileConfiguration アノテーションがあります。 複雑なアプリケーションでは複数のファイルが存在する場合があります。

プロファイル コネクタ

コネクタはプロファイル間の接続を管理します。通常は各クロス プロファイル 特定のコネクタを指します。1 つの 1 つのクロス プロファイル タイプで、 同じコネクタを使用する必要があります。

クロス プロファイル プロバイダのクラス

クロス プロファイル プロバイダ クラスは、関連するクロス プロファイル タイプをグループ化します。

Mediator

メディエーターは高レベルと下位レベルのコードの間に位置し、コードの呼び出しを分散します。 正しいプロファイルと、結果を統合します。このコードを プロファイル対応です。これはアーキテクチャの概念であり、 説明します。

クロス プロファイル タイプ

クロス プロファイル型は、アノテーション付きのメソッドを含むクラスまたはインターフェースです。 @CrossProfile。この型のコードはプロファイル対応である必要はなく、 ローカルデータで処理するのが理想的です

プロファイル タイプ

プロファイルの種類
現在実行中のアクティブなプロファイル。
その他(存在する場合)実行していないプロファイル。
パーソナルユーザー 0: デバイス上のプロファイル オフにしました。
職場通常はユーザーが 10 ですが、これより多い場合もあります。オンにしたり、 オフ。仕事用のアプリとデータを格納するために使用します。
プライマリ必要に応じてアプリケーションで定義されます。どのプロファイルが 両方のプロフィールが統合されて表示されます。
セカンダリプライマリが定義されている場合、セカンダリは プライマリではありません。
サプライヤープライマリプロファイルのサプライヤーは両方のプロファイルです。 セカンダリ プロファイルのサプライヤーは、セカンダリ プロファイル自体のみになります。

プロファイル ID

プロファイルの種類(個人用または仕事用)を表すクラス。これらは 複数のプロファイルで実行されるメソッドから返されるメソッドです。これを使用して、 プロファイルにコードを書きますこれらは、便宜上、int にシリアル化できます。 ストレージです。

このガイドでは、効率的で信頼性の高いインフラストラクチャを構築するための 維持可能なクロス プロファイル機能を Android アプリ内で実現できます。

CrossProfileConnector をシングルトンに変換する

ライフサイクル全体で 1 つのインスタンスのみを使用する必要があります。 そうしないと、並列接続が作成されます。これは Dagger などの依存関係インジェクション フレームワークを使用するか、 クラシック シングルトン パターン、 作成することもできます。

生成されたプロファイル インスタンスをメソッド内で作成するのではなく、呼び出しの際に、クラスに挿入または渡す

これにより、自動生成された FakeProfile インスタンスを 役立つことがあります。

メディエーター パターンを検討する

この一般的なパターンでは、既存の API の 1 つ(getEvents() など)を作成します。 すべての呼び出し元がプロファイルを認識します。この場合、既存の API は単に 「メディエーター」になる新しい呼び出しを含むメソッドまたはクラスです。 クロスプロファイルコードです

こうすれば、クロス プロファイルで電話をかけることをすべての発信者に知らなくて済むようになります。 API の一部になります

プロバイダで実装クラスを公開しなくても済むように、インターフェース メソッドに @CrossProfile のアノテーションを付けるかどうかを検討する

これは、依存関係インジェクション フレームワークとうまく連携します。

クロス プロファイル通話からデータを受け取る場合は、どのプロファイルのものを参照するフィールドを追加するか検討する

UI レイヤで確認しておくと便利なので、この方法をおすすめします。 (例: 仕事用のバッジアイコンを追加する)。また、データが取り込まれる場合は、 識別子(パッケージ名など)は、識別子がなければ一意ではなくなります。

クロス プロファイル

このセクションでは、独自のクロス プロファイル インタラクションを構築する方法について説明します。

メイン プロフィール

このドキュメントの例では、ほとんどの呼び出しで 実行するプロファイル(仕事用、個人用、その両方)を指定します。

実際には、1 つのプロファイルでのみ統合されたエクスペリエンスを持つアプリでは、この決定を実行しているプロファイルに依存させることが一般的です。そのため、コードベースに if-else プロファイル条件が散らばらないように、これも考慮した便利なメソッドがあります。

コネクタ インスタンスを作成するときに、どのプロファイル タイプを「プライマリ」にするかを指定できます(例:「WORK」)。これにより、 次のとおりです。

profileCalendarDatabase.primary().getEvents();

profileCalendarDatabase.secondary().getEvents();

// Runs on all profiles if running on the primary, or just
// on the current profile if running on the secondary.
profileCalendarDatabase.suppliers().getEvents();

クロス プロファイルのタイプ

@CrossProfile アノテーション付きのメソッドを含むクラスとインターフェースは、 クロス プロファイル タイプと呼ばれます。

クロス プロファイル タイプの実装は、プロファイルに依存しない必要があります。 プロファイルを指定します。これらのオブジェクトは、他のメソッドや 一般的には 1 つのプロファイルで動作しているかのように動作します。彼らは 自分のプロフィールでのみ状態にアクセスできます。

クロス プロファイル タイプの例:

public class Calculator {
  @CrossProfile
  public int add(int a, int b) {
    return a + b;
  }
}

クラス アノテーション

最も強力な API を提供するには、各クロスにコネクタを指定する必要があります 次のように設定します。

@CrossProfile(connector=MyProfileConnector.class)
public class Calculator {
  @CrossProfile
  public int add(int a, int b) {
    return a + b;
  }
}

これは省略可能ですが、生成される API はタイプにより限定されます コンパイル時のチェックが厳格になっています

インターフェース

インターフェースのメソッドに @CrossProfile というアノテーションを付けることで、 このメソッドの実装方法があります。 同じです

クロス プロファイル インターフェースの任意の実装は、Cross Profile Provider には、 この実装は、クロス プロファイルでアクセスできる必要があります。すべての 実装クラスにアノテーションを付けます。

クロス プロファイル プロバイダ

すべてのクロス プロファイル タイプはメソッドで指定する必要があります アノテーション付き @CrossProfileProvider。これらのメソッドは、 クロス プロファイル呼び出しが行われるため、シングルトンを維持し、 作成します。

コンストラクタ

プロバイダにはパブリック コンストラクタが必要です。パブリック コンストラクタは引数を取らないか、 単一の Context 引数。

プロバイダのメソッド

プロバイダ メソッドは、引数なしか、1 つの Context 引数を取る必要があります。

依存関係インジェクション

Dagger などの依存関係注入フレームワークを使用して そのフレームワークでクロス フィルタリングを プロファイル タイプを作成し、それらのタイプを 使用します。その後、@CrossProfileProvider メソッドはそれらを返すことができます。 挿入されます。

プロファイル コネクタ

各クロス プロファイル構成には、1 つのプロファイル コネクタが必要です。 他のプロファイルへの接続を管理します。

デフォルトのプロファイル コネクタ

コードベースにクロス プロファイル構成が 1 つしかない場合は、 独自のプロファイル コネクタを作成することは避け、 com.google.android.enterprise.connectedapps.CrossProfileConnector。これが 何も指定しない場合のデフォルトが使用されます。

クロス プロファイル コネクタを構築する際は、いくつかのオプションを ビルダー:

  • スケジュール設定されたエグゼキュータ サービス

    SDK によって作成されるスレッドを制御する場合は、次のコマンドを使用します。 #setScheduledExecutorService(),

  • Binder

    プロファイル バインディングに関する特定のニーズがある場合は、#setBinder を使用します。この Device Policy Controller でのみ使用される可能性が高くなります。

カスタム プロファイル コネクタ

いくつかの構成を設定するには、カスタム プロファイル コネクタが必要 (CustomProfileConnector を使用)。複数のタスクが必要な場合は 1 つ必要です。 コネクタを単一のコードベースに統合できます(たとえば、複数のプロセスがある場合、 プロセスごとに 1 つのコネクタを推奨)。

ProfileConnector を作成すると、次のようになります。

@GeneratedProfileConnector
public interface MyProfileConnector extends ProfileConnector {
  public static MyProfileConnector create(Context context) {
    // Configuration can be specified on the builder
    return GeneratedMyProfileConnector.builder(context).build();
  }
}
  • serviceClassName

    生成されたサービスの名前( AndroidManifest.xml)、serviceClassName= を使用してください。

  • primaryProfile

    プライマリ プロファイルを指定するには、primaryProfile を使用します。

  • availabilityRestrictions

    制限を変更するには 接続とプロファイルの可用性を設定するため、 availabilityRestrictions

Device Policy Controller

アプリが Device Policy Controller である場合は、アプリのインスタンスを指定する必要があります。 DpcProfileBinderDeviceAdminReceiver を参照しています。

独自のプロファイル コネクタを実装する場合:

@GeneratedProfileConnector
public interface DpcProfileConnector extends ProfileConnector {
  public static DpcProfileConnector get(Context context) {
    return GeneratedDpcProfileConnector.builder(context).setBinder(new
DpcProfileBinder(new ComponentName("com.google.testdpc",
"AdminReceiver"))).build();
  }
}

デフォルトの CrossProfileConnector を使用する場合:

CrossProfileConnector connector =
CrossProfileConnector.builder(context).setBinder(new DpcProfileBinder(new
ComponentName("com.google.testdpc", "AdminReceiver"))).build();

クロス プロファイル構成

@CrossProfileConfiguration アノテーションを使用して、すべてのクロス プロファイル タイプをコネクタを使用して作成する必要があります。宛先 そのため、クラスに「@CrossProfileConfiguration」というアノテーションを付けます。 次のように指定します。

@CrossProfileConfiguration(providers = {TestProvider.class})
public abstract class TestApplication {
}

これにより、すべてのクロス プロファイル 種類 同じプロファイル コネクタがあるか、コネクタが指定されていません。

  • serviceSuperclass

    デフォルトでは、生成されたサービスは android.app.Service を あります。別のクラス(それ自体がサブクラスでなければなりません)が必要な場合は、 (android.app.Service など)をスーパークラスにしたい場合は、 serviceSuperclass=

  • serviceClass

    指定すると、サービスは生成されません。これは 使用しているプロファイル コネクタの serviceClassName。カスタム サービスは、生成された _Dispatcher クラスを 例:

public final class TestProfileConnector_Service extends Service {
  private Stub binder = new Stub() {
    private final TestProfileConnector_Service_Dispatcher dispatcher = new
TestProfileConnector_Service_Dispatcher();

    @Override
    public void prepareCall(long callId, int blockId, int numBytes, byte[] params)
{
      dispatcher.prepareCall(callId, blockId, numBytes, params);
    }

    @Override
    public byte[] call(long callId, int blockId, long crossProfileTypeIdentifier,
int methodIdentifier, byte[] params,
    ICrossProfileCallback callback) {
      return dispatcher.call(callId, blockId, crossProfileTypeIdentifier,
methodIdentifier, params, callback);
    }

    @Override
    public byte[] fetchResponse(long callId, int blockId) {
      return dispatcher.fetchResponse(callId, blockId);
  };

  @Override
  public Binder onBind(Intent intent) {
    return binder;
  }
}

これは、アラートの前後に追加のアクションを行う必要がある場合に使用できます。 通話のクロス プロファイル。

  • コネクタ

    デフォルトの CrossProfileConnector 以外のコネクタを使用している場合は、 connector= を使用して指定する必要があります。

公開設定

クロス プロファイルとやり取りするアプリケーションのすべての部分が、 選択します

@CrossProfileConfiguration アノテーションを付けたクラスは、すべての API プロバイダを使用します。

同期呼び出し

Connected Apps SDK では、同期(ブロッキング)呼び出しが 避けられません。ただし、事前トレーニング済みモデルの使用には (長時間ブロックする可能性がある場合など)に、 可能な限り同期呼び出しを避けることをおすすめします。用途 非同期呼び出しについては、非同期呼び出し 呼び出し

コネクションホルダー

同期呼び出しを使用する場合は、同期呼び出しを使用するために クロス プロファイル呼び出しを行う前に登録された接続ホルダーか、そうでない場合は 例外がスローされます。詳しくは、接続ホルダーをご覧ください。

接続ホルダーを追加するには、ProfileConnector#addConnectionHolder(Object) を呼び出します。 オブジェクトを任意のオブジェクト(場合によっては、 通話)。これにより、このオブジェクトで 接続の確立を試みます。これは呼び出しのに呼び出す必要があります。 同期呼び出しは行われません。これは非ブロッキング呼び出しなので、 接続の準備が整っていない(または接続できない可能性がある)ことを その場合、通常のエラー処理の動作が適用されます。

通話時に適切なクロス プロファイル権限がない場合 ProfileConnector#addConnectionHolder(Object)、または使用できるプロフィールがありません その場合、エラーはスローされませんが、接続されたコールバックは 呼び出すことができます。後でその権限が付与されるか、他のプロファイルに 接続が確立され、コールバックが呼び出されます。

ProfileConnector#connect(Object) は、次のブロック メソッドです。 オブジェクトを接続ホルダーとして追加し、接続または UnavailableProfileException をスローする。このメソッドは、 UI スレッドに対応する必要があります。

ProfileConnector#connect(Object) などへの呼び出し ProfileConnector#connect は、自動的に終了するオブジェクトを返します。 閉じたら接続ホルダーを取り外します。これにより、次のような用途が可能になります。

try (ProfileConnectionHolder p = connector.connect()) {
  // Use the connection
}

同期呼び出しが終了したら、 ProfileConnector#removeConnectionHolder(Object)。すべての接続ホルダーが 削除されると、接続が閉じられます。

接続

接続リスナーを使用すると、接続状態の更新時に通知を受け取れます connector.utils().isConnected を使用すると、 接続が存在します。例:

// Only use this if using synchronous calls instead of Futures.
crossProfileConnector.connect(this);
crossProfileConnector.registerConnectionListener(() -> {
  if (crossProfileConnector.utils().isConnected()) {
    // Make cross-profile calls.
  }
});

非同期呼び出し

プロファイルの分割で公開されるすべてのメソッドをブロックとして指定する必要がある (同期)または非ブロッキング(非同期)です。出力値を返す任意のメソッド 非同期データ型(ListenableFuture など)、またはコールバックを受け取る パラメータが非ブロックとマークされます。他のすべてのメソッドはブロックとしてマークされます。

非同期呼び出しをおすすめします。同期呼び出しを使用する必要がある場合は、 同期 通話

コールバック

非ブロック呼び出しの最も基本的なタイプは void メソッドです。これは 1 つの呼び出しとして 呼び出されるメソッドを含むインターフェースと、 表示されます。これらのインターフェースを SDK と連携させるには、インターフェースを アノテーション付きの @CrossProfileCallback。例:

@CrossProfileCallback
public interface InstallationCompleteListener {
  void installationComplete(int state);
}

このインターフェースは、アノテーション付きの @CrossProfile のパラメータとして使用できます。 メソッドであり、通常どおり呼び出されます。例:

@CrossProfile
public void install(String filename, InstallationCompleteListener callback) {
  // Do something on a separate thread and then:
  callback.installationComplete(1);
}

// In the mediator
profileInstaller.work().install(filename, (status) -> {
  // Deal with callback
}, (exception) -> {
  // Deal with possibility of profile unavailability
});

このインターフェースに単一のメソッドが含まれている場合、メソッドは 0 または 1 を 複数のプロファイルへの呼び出しで同時に使用することもできます。

コールバックを使用して任意の数の値を渡すことができますが、接続は 最初の値でのみ開いたままになります。詳細については、接続ホルダーをご覧ください。 より多くの値を受け取るために接続を開いたままにします

コールバックを含む同期メソッド

SDK でコールバックを使用する特殊な機能の 1 つとして、 技術的には、コールバックを使用する同期メソッドを記述します。

public void install(InstallationCompleteListener callback) {
  callback.installationComplete(1);
}

この場合、メソッドはコールバックに関係なく、実際には同期しています。この 以下のようになります。

System.out.println("This prints first");
installer.install(() -> {
        System.out.println("This prints second");
});
System.out.println("This prints third");

ただし、SDK を使用して呼び出した場合、同じようには動作しません。Google ただし、インストール メソッドが「 第 3」出力されます。SDK によって非同期とマークされているメソッドを使用する場合は、 メソッドがいつ呼び出されるかについて想定しない。

単純なコールバック

"シンプルなコールバック"より制限の厳しいコールバック形式を使用して、 クロス プロファイル通話の際の追加機能も備わっています。シンプルなインターフェースは には、0 個または 1 個のパラメータを取ることができる単一のメソッドが含まれます。

次のように指定することで、コールバック インターフェースが維持されるように指定できます。 @CrossProfileCallback アノテーション内の simple=true

シンプルなコールバックは、.both().suppliers()、 その他。

コネクションホルダー

コールバックまたは future を使用して非同期呼び出しを行う場合は、 接続ホルダーは呼び出し時に追加され、いずれかの状態が 値が渡されます。

コールバックで複数の結果が渡されることを想定している場合は、 コールバックを接続ホルダーとして手動で追加します。

MyCallback b = //...
connector.addConnectionHolder(b);

  profileMyClass.other().registerListener(b);

  // Now the connection will be held open indefinitely, once finished:
  connector.removeConnectionHolder(b);

try-with-resources ブロックと併用することもできます。

MyCallback b = //...
try (ProfileConnectionHolder p = connector.addConnectionHolder(b)) {
  profileMyClass.other().registerListener(b);

  // Other things running while we expect results
}

コールバックまたは Future を使用して電話をかけた場合、接続は開いたままになります 結果が渡されます。結果が渡されないと判断した場合は、 は、接続ホルダーとしてのコールバックまたは Future を削除します。

connector.removeConnectionHolder(myCallback);
connector.removeConnectionHolder(future);

詳しくは、接続ホルダーをご覧ください。

Futures

Future は、SDK でもネイティブにサポートされています。ネイティブにサポートされている唯一の Future 型は ListenableFuture ですが、カスタムの Future です タイプは、 使用できます。Future を使用するには、サポートされている Future 型を戻り値として宣言するだけです。 クロス プロファイル メソッドとして使用し、通常どおり使用してください。

これには同じ「通常とは異なる特徴」があります。あります。同期メソッドは、 Future を返す(immediateFuture を使用するなど)は、異なる動作をします 現在のプロファイルで実行する場合と別のプロファイルで実行する場合の 2 つの違いがあります。Pod の メソッドが非同期とマークされているメソッドの場合、 メソッドが呼び出されます。

スレッド

クロス プロファイルの将来やメインでのコールバックの結果をブロックしない 作成します。そのようにすると、状況によってコードによってブロックが発生する可能性があります。 制限はありませんこれは、他のプロファイルへの接続も メインスレッドで確立され、保留されている場合、これは発生しません。 クロスプロファイル結果です

対象

空き状況リスナーを使用して、空き情報の状態が connector.utils().isAvailable を使用すると、 プロファイルを使用できます。例:

crossProfileConnector.registerAvailabilityListener(() -> {
  if (crossProfileConnector.utils().isAvailable()) {
    // Show cross-profile content
  } else {
    // Hide cross-profile content
  }
});

コネクションホルダー

接続ホルダーは任意のオブジェクトであり、 クロス プロファイル接続が確立され維持されることへの関心を表します。

デフォルトでは、非同期呼び出しを行うと接続ホルダーが追加されます。 結果やエラーが発生すると削除されます。

接続ホルダーを手動で追加または削除して、より詳細な管理を可能に ルーティングされます接続ホルダーは、 connector.addConnectionHolder、次を使用して削除されました: connector.removeConnectionHolder

接続ホルダーが 1 つ以上追加されている場合、SDK は接続の維持を試みます。追加された接続ホルダーがない場合、 閉じることができます。

追加した接続ホルダーへの参照は保持し、削除する必要があります 通知を受け取れます

同期呼び出し

同期呼び出しを行う前に、接続ホルダーを追加する必要があります。これにより、 どのオブジェクトを使用しても実行できますが、そのオブジェクトを追跡して、 同期呼び出しが不要になると削除されます。

非同期呼び出し

非同期呼び出しを行うと、接続ホルダーが自動的に管理される これにより、呼び出しと最初のレスポンスまたはエラーの間の接続が開かれます。 以降も存続するために接続が必要な場合(たとえば、 呼び出す場合は、コールバック自体を ケーブルを取り外す必要がなくなった場合には取り外してください。 できます。

エラー処理

デフォルトでは、他のプロファイルに割り当てられていないプロファイルへの呼び出しが UnavailableProfileException がスローされます(または Future に渡されるか、非同期呼び出しのエラー コールバックになります)。

これを回避するには、デベロッパーが #both() または #suppliers() を使用して、 結果のリスト内の任意の数のエントリを処理するコード( (他のプロフィールが利用できない場合、2)となります。

例外

現在のプロファイルの呼び出し後に発生した未チェック例外は、通常どおり伝播されます。これは、インフラストラクチャを 呼び出し(#current()#personal#both など)。

他のプロファイルへの呼び出し後に発生する未確認の例外は、 ProfileRuntimeException で、元の例外を できます。これは、呼び出しに使用されるメソッド(#other()#personal#both など)。

ifAvailable

UnavailableProfileException をキャッチして処理する代わりに、 .ifAvailable() メソッドを使用して、デフォルトの値(デフォルト値)を UnavailableProfileException をスローする代わりに返されます。

例:

profileNotesDatabase.other().ifAvailable().getNumberOfNotes(/* defaultValue= */ 0);

テスト

コードをテスト可能にするには、プロファイルのインスタンスを注入する必要があります。 コネクタを使用する任意のコードに接続できます(プロファイルの可用性の確認、 手動接続など)が含まれます。また、プロファイルのインスタンスを注入し、 使用される場所を認識します。

テストで使用できる偽のコネクタとタイプが提供されます。

まず、テストの依存関係を追加します。

  testAnnotationProcessor
'com.google.android.enterprise.connectedapps:connectedapps-processor:1.1.2'
  testCompileOnly
'com.google.android.enterprise.connectedapps:connectedapps-testing-annotations:1.1.2'
  testImplementation
'com.google.android.enterprise.connectedapps:connectedapps-testing:1.1.2'

次に、テストクラスに @CrossProfileTest アノテーションを付けて、 テスト対象の @CrossProfileConfiguration アノテーション付きクラス:

@CrossProfileTest(configuration = MyApplication.class)
@RunWith(RobolectricTestRunner.class)
public class NotesMediatorTest {

}

これにより、サービス モジュールで使用されるすべてのタイプとコネクタで偽の できます。

テストでこれらの架空のインスタンスを作成します。

private final FakeCrossProfileConnector connector = new
FakeCrossProfileConnector();
private final NotesManager personalNotesManager = new NotesManager(); //
real/mock/fake
private final NotesManager workNotesManager = new NotesManager(); // real/mock/fake

private final FakeProfileNotesManager profileNotesManager =
  FakeProfileNotesManager.builder()
    .personal(personalNotesManager)
    .work(workNotesManager)
    .connector(connector)
    .build();

プロフィールの状態を設定します。

connector.setRunningOnProfile(PERSONAL);
connector.createWorkProfile();
connector.turnOffWorkProfile();

架空のコネクタとクロス プロファイル クラスをテスト対象のコードに渡します。 電話をかけることができます。

呼び出しは適切なターゲットにルーティングされ、次の場合に例外がスローされます。 切断されたプロフィールや利用できないプロフィールに電話をかけること

サポートされるタイプ

ユーザーが特別な作業を行わなくても、次の型がサポートされている。これらは すべてのクロス プロファイル呼び出しで、引数または戻り値の型として使用できます。

  • プリミティブ(byteshortintlongfloatdoublecharboolean),
  • ボックス型プリミティブ(java.lang.Bytejava.lang.Shortjava.lang.Integerjava.lang.Longjava.lang.Floatjava.lang.Doublejava.lang.Characterjava.lang.Booleanjava.lang.Void
  • java.lang.String
  • android.os.Parcelable を実装するものであれば、
  • java.io.Serializable を実装するものであれば、
  • 単一ディメンションの非プリミティブ配列
  • java.util.Optional
  • java.util.Collection
  • java.util.List
  • java.util.Map
  • java.util.Set
  • android.util.Pair
  • com.google.common.collect.ImmutableMap

サポートされているすべての汎用型(java.util.Collection など)には、 タイプ パラメータとして渡されます。例:

java.util.Collection<java.util.Map<java.lang.String,MySerializableType[]>> は 指定する必要があります。

Futures

次の型は戻り値の型としてのみサポートされています。

  • com.google.common.util.concurrent.ListenableFuture

カスタム Parcelable ラッパー

タイプが前のリストに含まれていない場合は、まず、対象となる可能性がないか検討してください。 android.os.Parcelable または java.io.Serializable を正しく実装する必要があります。条件 その場合、Parcelable ラッパーを 型のサポートを追加します。

カスタムの Future ラッパー

前述のリストにない Future 型を使用する場合は、将来の型を ラッパーを追加してサポートを追加します。

Parcelable ラッパー

Parcelable ラッパーは、非 Parcelable のサポートを SDK で追加する方法です。 定義できます。SDK には、さまざまなリソースのラッパーが 種類がありますが、 含まれていない場合は、独自に記述する必要があります。

Parcelable ラッパーは、別のクラスをラップして Parcelable です。定義された静的コントラクトに従い、SDK に登録されている 特定の型を Parcelable 型に変換するために使用できます。また、 その型を Parcelable 型から抽出します

Annotation

Parcelable ラッパークラスにはアノテーション @CustomParcelableWrapper を付ける必要があります。 ラップされたクラスを originalType として指定します。例:

@CustomParcelableWrapper(originalType=ImmutableList.class)
``` ###
Format

Parcelable wrappers must implement `Parcelable` correctly, and must have a
static `W of(Bundler, BundlerType, T)` method which wraps the wrapped type and a
non-static `T get()` method which returns the wrapped type.

The SDK will use these methods to provide seamless support for the type.

### Bundler

To allow for wrapping generic types (such as lists and maps), the `of` method is
passed a `Bundler` which is capable of reading (using `#readFromParcel`) and
writing (using `#writeToParcel`) all supported types to a `Parcel`, and a
`BundlerType` which represents the declared type to be written.

`Bundler` and `BundlerType` instances are themselves parcelable, and should be
written as part of the parcelling of the parcelable wrapper, so that it can be
used when reconstructing the parcelable wrapper.

If the `BundlerType` represents a generic type, the type variables can be found
by calling `.typeArguments()`. Each type argument is itself a `BundlerType`.

For an example, see `ParcelableCustomWrapper`:

```java
public class CustomWrapper<F> {
  private final F value;

  public CustomWrapper(F value) {
    this.value = value;
  }
  public F value() {
    return value;
  }
}

@CustomParcelableWrapper(originalType = CustomWrapper.class)
public class ParcelableCustomWrapper<E> implements Parcelable {

  private static final int NULL = -1;
  private static final int NOT_NULL = 1;

  private final Bundler bundler;
  private final BundlerType type;
  private final CustomWrapper<E> customWrapper;

  /**
  *   Create a wrapper for a given {@link CustomWrapper}.
  *
  *   <p>The passed in {@link Bundler} must be capable of bundling {@code F}.
  */
  public static <F> ParcelableCustomWrapper<F> of(
      Bundler bundler, BundlerType type, CustomWrapper<F> customWrapper) {
    return new ParcelableCustomWrapper<>(bundler, type, customWrapper);
  }

  public CustomWrapper<E> get() {
    return customWrapper;
  }

  private ParcelableCustomWrapper(
      Bundler bundler, BundlerType type, CustomWrapper<E> customWrapper) {
    if (bundler == null || type == null) {
      throw new NullPointerException();
    }
    this.bundler = bundler;
    this.type = type;
    this.customWrapper = customWrapper;
  }

  private ParcelableCustomWrapper(Parcel in) {
    bundler = in.readParcelable(Bundler.class.getClassLoader());

    int presentValue = in.readInt();

    if (presentValue == NULL) {
      type = null;
      customWrapper = null;
      return;
    }

    type = (BundlerType) in.readParcelable(Bundler.class.getClassLoader());
    BundlerType valueType = type.typeArguments().get(0);

    @SuppressWarnings("unchecked")
    E value = (E) bundler.readFromParcel(in, valueType);

    customWrapper = new CustomWrapper<>(value);
  }

  @Override
  public void writeToParcel(Parcel dest, int flags) {
    dest.writeParcelable(bundler, flags);

    if (customWrapper == null) {
      dest.writeInt(NULL);
      return;
    }

    dest.writeInt(NOT_NULL);
    dest.writeParcelable(type, flags);
    BundlerType valueType = type.typeArguments().get(0);
    bundler.writeToParcel(dest, customWrapper.value(), valueType, flags);
  }

  @Override
  public int describeContents() {
    return 0;
  }

  @SuppressWarnings("rawtypes")
  public static final Creator<ParcelableCustomWrapper> CREATOR =
    new Creator<ParcelableCustomWrapper>() {
      @Override
      public ParcelableCustomWrapper createFromParcel(Parcel in) {
        return new ParcelableCustomWrapper(in);
      }

      @Override
      public ParcelableCustomWrapper[] newArray(int size) {
        return new ParcelableCustomWrapper[size];
      }
    };
}

SDK に登録する

作成したカスタム Parcelable ラッパーを使用するには、ラッパーを登録する必要があります。 説明します

これを行うには、次のいずれかに parcelableWrappers={YourParcelableWrapper.class} を指定します。 クラスの CustomProfileConnector アノテーションまたは CrossProfile アノテーション。

未来のラッパー

Future ラッパーは、SDK がプロファイル間で Future のサポートを追加する方法です。「 SDK はデフォルトで ListenableFuture をサポートしていますが、その他の今後のサポートでは、 自分でサポートを追加できます。

Future ラッパーは、特定の Future 型をラップして、 使用できます。定義された静的なコントラクトに従い、 SDK に登録されている必要があります。

Annotation

将来のラッパークラスには、アノテーション @CustomFutureWrapper を付ける必要があります。 ラップされたクラスを originalType として返します。例:

@CustomFutureWrapper(originalType=SettableFuture.class)
``` ### Format

Future wrappers must extend
`com.google.android.enterprise.connectedapps.FutureWrapper`.

Future wrappers must have a static `W create(Bundler, BundlerType)` method which
creates an instance of the wrapper. At the same time this should create an
instance of the wrapped future type. This should be returned by a non-static `T`
`getFuture()` method. The `onResult(E)` and `onException(Throwable)` methods
must be implemented to pass the result or throwable to the wrapped future.

Future wrappers must also have a static `void writeFutureResult(Bundler,`
`BundlerType, T, FutureResultWriter<E>)` method. This should register with the
passed in future for results, and when a result is given, call
`resultWriter.onSuccess(value)`. If an exception is given,
`resultWriter.onFailure(exception)` should be called.

Finally, future wrappers must also have a static `T<Map<Profile, E>>`
`groupResults(Map<Profile, T<E>> results)` method which converts a map from
profile to future, into a future of a map from profile to result.
`CrossProfileCallbackMultiMerger` can be used to make this logic easier.

For example:

```java
/** A very simple implementation of the future pattern used to test custom future
wrappers. */
public class SimpleFuture<E> {
  public static interface Consumer<E> {
    void accept(E value);
  }
  private E value;
  private Throwable thrown;
  private final CountDownLatch countDownLatch = new CountDownLatch(1);
  private Consumer<E> callback;
  private Consumer<Throwable> exceptionCallback;

  public void set(E value) {
    this.value = value;
    countDownLatch.countDown();
    if (callback != null) {
      callback.accept(value);
    }
  }

  public void setException(Throwable t) {
    this.thrown = t;
    countDownLatch.countDown();
    if (exceptionCallback != null) {
      exceptionCallback.accept(thrown);
    }
  }

  public E get() {
    try {
      countDownLatch.await();
    } catch (InterruptedException e) {
      eturn null;
    }
    if (thrown != null) {
      throw new RuntimeException(thrown);
    }
    return value;
  }

  public void setCallback(Consumer<E> callback, Consumer<Throwable>
exceptionCallback) {
    if (value != null) {
      callback.accept(value);
    } else if (thrown != null) {
      exceptionCallback.accept(thrown);
    } else {
      this.callback = callback;
      this.exceptionCallback = exceptionCallback;
    }
  }
}
/** Wrapper for adding support for {@link SimpleFuture} to the Connected Apps SDK.
*/
@CustomFutureWrapper(originalType = SimpleFuture.class)
public final class SimpleFutureWrapper<E> extends FutureWrapper<E> {

  private final SimpleFuture<E> future = new SimpleFuture<>();

  public static <E> SimpleFutureWrapper<E> create(Bundler bundler, BundlerType
bundlerType) {
    return new SimpleFutureWrapper<>(bundler, bundlerType);
  }

  private SimpleFutureWrapper(Bundler bundler, BundlerType bundlerType) {
    super(bundler, bundlerType);
  }

  public SimpleFuture<E> getFuture() {
    return future;
  }

  @Override
  public void onResult(E result) {
    future.set(result);
  }

  @Override
  public void onException(Throwable throwable) {
    future.setException(throwable);
  }

  public static <E> void writeFutureResult(
      SimpleFuture<E> future, FutureResultWriter<E> resultWriter) {

    future.setCallback(resultWriter::onSuccess, resultWriter::onFailure);
  }

  public static <E> SimpleFuture<Map<Profile, E>> groupResults(
      Map<Profile, SimpleFuture<E>> results) {
    SimpleFuture<Map<Profile, E>> m = new SimpleFuture<>();

    CrossProfileCallbackMultiMerger<E> merger =
        new CrossProfileCallbackMultiMerger<>(results.size(), m::set);
    for (Map.Entry<Profile, SimpleFuture<E>> result : results.entrySet()) {
      result
        .getValue()
        .setCallback(
          (value) -> merger.onResult(result.getKey(), value),
          (throwable) -> merger.missingResult(result.getKey()));
    }
    return m;
  }
}

SDK に登録する

作成したカスタム Future ラッパーを使用するには、それを 説明します。

これを行うには、次のいずれかで futureWrappers={YourFutureWrapper.class} を指定します。 クラスの CustomProfileConnector アノテーションまたは CrossProfile アノテーション。

ダイレクト ブート モード

アプリがダイレクト ブートと モード 場合は、プロフィールのロックが解除される前にクロス プロファイル通話が必要になることがあります。 デフォルトでは、SDK は他のプロファイルがロック解除されている場合にのみ接続を許可します。

この動作を変更するには、カスタム プロファイル コネクタを使用している場合に、 指定 availabilityRestrictions=AvailabilityRestrictions.DIRECT_BOOT_AWARE:

@GeneratedProfileConnector
@CustomProfileConnector(availabilityRestrictions=AvailabilityRestrictions.DIRECT_BO
OT_AWARE)
public interface MyProfileConnector extends ProfileConnector {
  public static MyProfileConnector create(Context context) {
    return GeneratedMyProfileConnector.builder(context).build();
  }
}

CrossProfileConnector を使用している場合は、次のコマンドを使用します。 .setAvailabilityRestrictions(AvailabilityRestrictions.DIRECT_BOOT _AWARE がオン 作成します。

この変更により利用可能状況が通知され、複数のアセットを プロフィールの呼び出し(他のプロフィールのロックが解除されていない場合)。お客様の責任 通話がデバイス暗号化ストレージにのみアクセスするようにします。