Fire TV対応アプリの開発には、既に検討すべきことが山ほどあります。Fire TVリモコンによるナビゲーション、部屋の離れた場所からでも見やすいUIレイアウト、モバイルアプリとは基本的に異なるUX動作に対応する必要があります。
認証の実装やバックエンドデータの管理、メディアアセットの保存や配信の方法の検討などに時間を費やしている余裕はありません。このような作業の負担を、開発者に優しく、簡単に統合できるサービスにすべて任せることができれば、開発効率は大きく向上します。
AWS Amplifyはまさにそのためのソリューションです。AWS Amplifyは、開発者がAWSのサービスを最小限のバックエンド設定で利用できる開発プラットフォームです。Webアプリやモバイルアプリを素早く構築、デプロイ、スケールすることができます。Amplifyを使用することで、認証、クラウドストレージ、データ永続化など、バックエンドのあらゆる要素の処理が容易になります。これにより、開発者はFire TVで違和感なく機能するエクスペリエンスの構築に専念することができます。
このFire TV対応アプリのチュートリアルに沿って作業を進めるにあたって、以下の準備が必要です。
このデモでは、Fire OSアプリ開発におけるAmplifyの活用方法を紹介することが目的です。本番環境用のアプリではないため、セキュリティのベストプラクティスは含まれていません。
Fire TVアプリの開発には独自の課題があります。D-Padのナビゲーション、TV向けに最適化されたレイアウト、オーディオフォーカスの制御などです。これらの課題にバックエンドのインフラ構築が加わると、UIが完成する前に開発の勢いは容易に失速してしまいます。
Amplifyは問題解決の糸口になります。Amplifyにより、定型処理のコード作成を省略して、アプリの開発に集中できます。(英語のみ)
このガイドで作成するアプリでは、上記3つの機能を中心に説明します。これ以外にも、Amplifyはサーバーレス関数を提供しており、インフラを管理することなく、バックグラウンド処理用のLambda関数を追加できます。
つまり、クラウドサービスの構築に時間をかけずに、サーバーレスアプリの開発についても学習するため、Fire TV体験の構築に注力できるようになります。
まずはじめに、アプリの認証、ストレージ、データ処理を担うAmplifyのバックエンドのセットアップをします。
Amplify CLIを使用してリソースをプロビジョニングするには、十分な権限を持つIAM Identity Centerの認証情報が必要です。まずAWSコンソールにログインし、IAM Identity Centerで新しいユーザーを作成します。
ユーザーを作成すると、新規ユーザーのアクセスポータルのURL、ユーザー名、ワンタイムパスワードが表示されます。
こちらのページの残りの手順に従って、ユーザーの設定を完了します。また、コマンドラインからAmplify関連の呼び出しを実行する際に使用するローカルAWSプロファイルも設定します。本ガイドのこれ以降の説明では、amplify-app-developerという名前のローカルAWSプロファイルが存在し、このプロファイルにarn:aws:iam::aws:policy/service-role/AmplifyBackendDeployFullAccessパーミッションが付与されていることを前提としています。
projectフォルダから、次のコマンドを実行します。
~/project$ $ npm create amplify@latest
Need to install the following packages:
create-amplify@1.2.0
Ok to proceed? (y) y
> npx
> create-amplify
✔ Where should we create your project? .
Installing devDependencies:
- @aws-amplify/backend
- @aws-amplify/backend-cli
- aws-cdk-lib@2.189.1
...
Welcome to AWS Amplify!
- Get started by running npx ampx sandbox.
- Run npx ampx help for a list of available commands.
これにより、amplifyという名前のサブフォルダが作成されます。
作成済みのAWSプロファイルを使用してAmplifyサンドボックスを作成します。これにより、開発に使用できるリソース(ストレージ用のS3、データ管理用のマネージド型GraphQL、認証用のCognitoなど)がプロビジョニングされます。プロセスが進行すると、amplifyサブフォルダ内のファイルの変更が検出され、それに応じてサンドボックスが更新されます。
$ npx ampx sandbox --profile amplify-app-developer
The region us-east-1 has not been bootstrapped. Sign in to the AWS console as a Root user or Admin to complete the bootstrap process, then restart the sandbox.
If this is not the region you are expecting to bootstrap, check for any AWS environment variables that may be set in your shell or use --profile <profile-name> to specify a profile with the correct region.
Amplifyを初めて使用する場合は、ブラウザが開き、Amplifyの環境設定の手順が表示されます。表示される手順をクリックしながら進めて、設定を完了します。
最終的に、このブートストラッププロセスでは、AWS CloudFormationを使用して、AWS Cloud Development Kitで使用されるリソースをプロビジョニングします。CloudFormationスタックは、S3、ECR、IAMの各リソースを操作します。
ブートストラップが完了したら、もう一度sandboxコマンドを実行します。
$ npx ampx sandbox --profile amplify-app-developer
Amplify Sandbox
Identifier: firedev
Stack: amplify-project-firedev-sandbox-c212f661f8
Region: us-east-1
To specify a different sandbox identifier, use --identifier
WARNING: Schema is using an @auth directive with deprecated provider 'iam'. Replace 'iam' provider with 'identityPool' provider.
✔ Backend synthesized in 1.47 seconds
✔ Type checks completed in 3.38 seconds
✔ Built and published assets
✔ Deployment completed in 210.98 seconds
AppSync API endpoint = https://hw734z3v7bhknhxjpvxf4xrnvu.appsync-api.us-east-1.amazonaws.com/graphql
[Sandbox] Watching for file changes...
Amplifyサンドボックスプロセスは、このターミナルで実行したままにしておくことができます。
Amplifyの設定が完了したので、Androidプロジェクトを作成しましょう。
Android Studioを開きます(インストールの手順)。[File] > [New] > [New Project] の順にクリックします。
[Television] テンプレートで [Empty Activity] を選択します。
[Package name] を指定します(com.example.amplifyfiretvなど)。[Save Location] をプロジェクトのルートフォルダに設定します。使用する最小バージョンのSDKを選択します。この例では、API 29を使用します。
これにより、新規プロジェクトが初期化され、多くの新規ファイルが作成されます。Android Studioでプロジェクトを初めて開くと、依存関係のダウンロードに数分かかります。
アプリをテストするために、Androidエミュレーターを使用します。Android TVエミュレーターとFire TV対応アプリについて詳しくは、こちらのページを参照してください。
Android Studioで、[Device Manager] に移動します。[Create Virtual Device] をクリックします。[New Hardware Profile] をクリックします。以下の設定でプロファイルを作成します。
仮想デバイスの名前を指定します。この例では、「Fire TV Emulator」という名前を指定していますが、任意の名前を付けることができます。
[Finish] をクリックします。次に、新規作成したプロファイルを選択して [Next] をクリックします。APIレベルが23以上のシステムイメージを選択します。このガイドでは、Q(29)を使用します。イメージをダウンロードし、この仮想デバイス用に選択します。
仮想デバイスの作成を終了します。次に、エミュレーターを起動します。
セットアップした最初のプロジェクトを使用して、エミュレーターでテストすることができます。
Android StudioのAndroid TVアプリ用の初期設定では、Android TV向けJetpack Composeが使用されます。Jetpack Composeは、Android StudioでUIを作成するために使用されるデフォルトのツールキットです。ただし、Fire TV対応アプリでは、AndroidのLeanbackライブラリを使用します。これは、Fire TVがLeanbackのUIコンポーネントや機能を強力にサポートしているためです。そこで、app/build.gradle.ktsファイルを次のように変更します。
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
}
android {
namespace = "com.example.amplifyfiretv"
compileSdk = 35
defaultConfig {
applicationId = "com.example.amplifyfiretv"
minSdk = 29
targetSdk = 35
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
lint {
abortOnError = false
checkReleaseBuilds = false
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation("androidx.leanback:leanback:1.0.0")
implementation("com.github.bumptech.glide:glide:4.12.0")
implementation(libs.androidx.lifecycle.runtime.ktx)
// 依存関係をテストします
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
次に、 app/src/main/java/com/example/amplifyfiretv/MainActivity.ktを修正し、Leanbackを使用して「Hello Android」というシンプルなテキストを表示するように変更します。
package com.example.amplifyfiretv
import android.os.Bundle
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.FragmentActivity
import androidx.leanback.app.BrowseSupportFragment
import androidx.leanback.widget.*
class MainActivity : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.replace(R.id.main_browse_fragment, MainFragment())
.commit()
}
}
}
class MainFragment : BrowseSupportFragment() {
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setupUIElements()
loadContent()
}
private fun setupUIElements() {
title = "Amplify Fire TV"
headersState = HEADERS_DISABLED
}
private fun loadContent() {
val textView = TextView(requireContext()).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
text = "Hello Android"
textSize = 24f
}
val container = requireActivity().findViewById<ViewGroup>(R.id.main_browse_fragment)
container.addView(textView)
}
}
次に、メインアクティビティページに、それぞれがビデオを表すカードを追加します。さしあたり、すべてのビデオとサムネイルの参照を、cards.jsonというローカルJSONファイルにハードコードします。こうすることで、ビデオの再生やナビゲーションが正常に動作することを保証できます。
JSONのカードデータは、CardData.ktというファイルで定義されている、CardDataProviderによってリアルタイムで読み込まれます。
package com.example.amplifyfiretv.data
import android.content.Context
import android.util.Log
import com.google.gson.Gson
data class CardData(
val title: String,
val subtitle: String,
val imageUrl: String,
val videoUrl: String
)
data class CardDataResponse(
val cards: List<CardData>
)
object CardDataProvider {
private const val TAG = "CardDataProvider"
private var cards: List<CardData> = emptyList()
fun initialize(context: Context) {
try {
val jsonString = context
.assets
.open("cards.json")
.bufferedReader()
.use { it.readText() }
val response = Gson()
.fromJson(jsonString, CardDataResponse::class.java)
cards = response.cards
} catch (e: Exception) {
cards = emptyList()
}
}
fun getCards(): List<CardData> {
return cards
}
}
ビデオの再生にはMedia3 ExoPlayerを使用します。ビデオプレーヤーの作成とビデオの読み込みに関するすべての機能は、VideoPlayerActivity.ktに記述されています。
エミュレーターでテストすると、現在、アプリは次のように表示されます。
Amplifyのセットアップは完了していますが、Amplifyをプロジェクトに追加する必要があります。
まず、Amplifyの依存関係をapp/build.gradle.ktsに追加します。
implementation("com.amplifyframework:aws-api:2.24.0")
implementation("com.amplifyframework:aws-datastore:2.24.0")
implementation("com.amplifyframework:aws-storage-s3:2.24.0")
implementation("com.amplifyframework:aws-auth-cognito:2.24.0")
次に、AmplifyApp.ktという新しいファイルを作成します。内容は次のとおりです。
package com.example.amplifyfiretv
import android.app.Application
import android.util.Log
import com.amplifyframework.AmplifyException
import com.amplifyframework.api.aws.AWSApiPlugin
import com.amplifyframework.auth.cognito.AWSCognitoAuthPlugin
import com.amplifyframework.core.Amplify
import com.amplifyframework.datastore.AWSDataStorePlugin
import com.amplifyframework.storage.s3.AWSS3StoragePlugin
import com.example.amplifyfiretv.data.CardDataProvider
class AmplifyApp : Application() {
companion object {
private const val TAG = "AmplifyApp"
}
override fun onCreate() {
super.onCreate()
CardDataProvider.initialize(applicationContext)
try {
Amplify.addPlugin(AWSApiPlugin())
Amplify.configure(applicationContext)
} catch (error: AmplifyException) {
Log.e(TAG, "Amplifyを初期化する際のエラー", error)
} catch (error: Exception) {
Log.e(TAG, "初期化中の予期しないエラー", error)
}
}
}
次に、app/src/main/AndroidManifest.xmlを修正し、AndroidでApplicationクラスとしてAmplifyAppクラスを使用するように設定します。
<application
android:name=".AmplifyApp"
Amplifyを使用する利点の1つは、AWS S3にアセットを保存して取得できることです。これを確認するために、cards.jsonファイルをS3バケットに移動してみます。これにより、アプリはコンパイル時にローカルファイルを参照する代わりに、S3からリアルタイムでカードデータを取得するようになります。
Amplifyを使用してAndroidプロジェクトでストレージをセットアップする方法については、こちらを参照してください。
amplify/storage/resource.ts:を作成します。
import { defineStorage } from '@aws-amplify/backend';
export const storage = defineStorage({
name: 'my-bucket',
access: (allow) => ({
'data/*': [
allow.guest.to(['get']),
allow.authenticated.to(['get'])
]
})
});
import { defineAuth } from '@aws-amplify/backend';
export const auth = defineAuth({
loginWith: {
email: true,
},
});
次に、amplify/backend.ts:を作成し、独自の認証とストレージの定義を使用することを指定します。
import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
import { storage } from './storage/resource';
defineBackend({
auth,
storage,
});
これらのファイルを作成すると、実行中のAmplifyサンドボックスプロセスにより、AWSリソースが更新され、認証とストレージのニーズが処理されます。
Amplifyサンドボックスプロセスはクライアント構成ファイル(amplify_outputs.json)を生成します。このファイルは、Amplifyが作成するさまざまなリソースにアプリが接続する際に使用されます。このファイルは、実行中のプロセスが、これまでの手順で行ったようなAmplifyの構成の変更を検出したときに自動的に更新されます。
デフォルトでは、amplify_outputs.jsonはAmplifyサンドボックスプロセスが実行されているディレクトリに直接書き込まれます。ただし、Fire TV対応アプリでは、このファイルがapp/src/main/res/raw/amplify_outputs.jsonに保存されている必要があります。このファイルを今後保存する場所のパスを明示的に指定して、Amplifyサンドボックスプロセスを再起動します。
$ npx ampx sandbox \
--profile amplify-app-developer \
--outputs-out-dir app/src/main/res/raw/
Amplify Sandbox
Identifier: firedev
Stack: amplify-project-firedev-sandbox-c212f661f8
Region: us-east-1
To specify a different sandbox identifier, use --identifier
WARNING: Schema is using an @auth directive with deprecated provider 'iam'. Replace 'iam' provider with 'identityPool' provider.
✔ Backend synthesized in 1.47 seconds
✔ Type checks completed in 3.38 seconds
✔ Built and published assets
✔ Deployment completed in 210.98 seconds
AppSync API endpoint = https://hw734z3v7bhknhxjpvxf4xrnvu.appsync-api.us-east-1.amazonaws.com/graphql
[Sandbox] Watching for file changes…
File written: app/src/main/res/raw/amplify_outputs.json
前の手順で生成されたamplify_outputs.jsonファイルを見ると、Amplifyプロジェクト用に作成されたS3バケットの名前を確認できます。
"storage": {
"aws_region": "us-east-1",
"bucket_name": "amplify-project-firedev-sandb-mybucket-ovpsejk2htya",
...
AWSコンソールで、このS3バケットに移動して、dataというサブフォルダを作成し、このフォルダにcards.jsonをアップロードします。これにより、このファイルのS3 URIは次のようになります。
s3://amplify-project-firedev-sandb-mybucket-ovpsejk2htya/data/cards.json
次に、CardData.kt内のコードを修正します。このコードは、ローカルにあるcards.jsonから読み込むのではなく、amplify_outputs.jsonに記載されているS3バケット名を参照し、Amplifyリソースを呼び出してリモートファイルをダウンロードした後、ローカルキャッシュに保存します。initialize関数の主要なコード行は次のとおりです。
// ダウンロード用のローカルファイルを設定します
val localFile = File(cacheDir, FILE_NAME)
// AmplifyOutputsからバケット名を取得します
val outputs = AmplifyOutputs.fromResource(R.raw.amplify_outputs)
val jsonString = context
.resources
.openRawResource(R.raw.amplify_outputs)
.bufferedReader()
.use { it.readText() }
val jsonObject = Gson().fromJson(jsonString, JsonObject::class.java)
val storageObject = jsonObject.getAsJsonObject("storage")
val bucketName = storageObject.get("bucket_name").asString
// S3パスを設定 - パーミッションに合わせてdata/プレフィックスを使用します
val s3Path = StoragePath.fromString("$DATA_SUBFOLDER/$FILE_NAME")
val options = StorageDownloadFileOptions.builder().build()
Amplify.Storage.downloadFile(
s3Path,
localFile,
options,
{ result ->
try {
val jsonString = localFile.readText()
val response = Gson()
.fromJson(jsonString, CardDataResponse::class.java)
cards = response.cards
onCardsLoaded?.invoke()
} catch (e: Exception) {
e.printStackTrace()
throw e
}
},
{ error ->
error.printStackTrace()
throw error
}
)
次に、MainActivity.ktを更新します。カードの読み込みをリッスンして、適切なタイミングでupdateContent()を呼び出すように修正します。
CardDataProvider.setOnCardsLoadedListener {
updateContent()
}
最後に、AmplifyApp.ktのコードを、CardDataProvider、ストレージ用のS3、認証用のCognitoを初期化するように更新します。
override fun onCreate() {
super.onCreate()
try {
Amplify.addPlugin(AWSS3StoragePlugin())
Amplify.addPlugin(AWSCognitoAuthPlugin())
val outputs = AmplifyOutputs.fromResource(R.raw.amplify_outputs)
Amplify.configure(outputs, applicationContext)
applicationScope.launch {
try {
CardDataProvider.initialize(applicationContext)
} catch (e: Exception) {
e.printStackTrace()
}
}
...
Amplifyのセットアップ、アプリの修正、cards.jsonファイルのS3へのアップロードが完了したので、再びアプリをビルドして実行し、エミュレーターでテストします。
カードの画像表示とビデオの再生は期待どおりに機能しています。Amplifyを使用したS3によるストレージの統合が正常に完了しました。
コードリポジトリを使用して作業を進めている場合、現在、 Retrieves card data via remote file with Amplify and S3というタイトルのコミットの状態です。
AmplifyとS3を利用してストレージを使用するために、既に認証サービスを作成してデプロイしました。ここでは、ユーザーのサインインを処理し、セッション情報を保存する認証フローを作成します。
Amplifyを使用してAndroidプロジェクトで認証をセットアップする方法について詳しくは、こちらを参照してください。
テストするための準備として、Cognitoユーザープールでユーザーを作成します。amplify_outputs.jsonを見ると、user_pool_idを確認できます。たとえば次のようになります。
"auth": {
"user_pool_id": "us-east-1_8Riey238F",
"aws_region": "us-east-1",
…
AWSコンソールで、Cognitoに移動し、このIDのユーザープールを検索します。該当するユーザープール内で、[User management] -> [Users] の順に選択します。数人の新規ユーザーを作成します。このガイドでは、2人のユーザーを作成しています。
Eメールアドレス | パスワード |
joe@example.com | My password is 1 number. |
jen@example.com | My password has 0 numbers. |
これらのユーザーを作成すると、その確認ステータスが「Force change password」になっていることがわかります。 テストユーザーとして使用するだけであるため、上記のパスワードでユーザーアカウントを確認します。この作業はAWS CLIで実行できます。たとえば次のようになります。
$ aws cognito-idp admin-set-user-password \
--profile amplify-app-developer \
--user-pool-id us-east-1_8Riey238F \
--username jen@example.com \
--password "パスワードに数字が含まれていません。" \
--permanent
これで、2人のテストユーザーが確認されました。
ユーザー向けに、メイン画面にシンプルサインインのステータス表示を追加します。Amplify.Auth.fetchAuthSessionを呼び出してユーザーがサインインしているかどうかを確認するcheckAuthSession()関数を作成します。ユーザーがサインインしていない場合は、[Sign In] が表示されます。ユーザーがサインインしている場合は、サインインしているユーザーのEメールアドレスと [Sign Out] が表示されます。
private fun checkAuthSession() {
Amplify.Auth.fetchAuthSession(
{ session ->
val cognitoSession = session as AWSCognitoAuthSession
if (cognitoSession.isSignedIn) {
handleSignedInState()
} else {
Handler(Looper.getMainLooper()).post {
updateAuthUI(false)
}
}
},
{ error ->
Handler(Looper.getMainLooper()).post {
updateAuthUI(false)
}
}
)
}
エミュレーターでは、ステータスに応じて、次のように異なるページが表示されます。
次に、SignInActivity.ktを作成します。これは、サインインのアクティビティ画面を定義します。app/src/main/res/layout/activity_sign_in.xmlで、Eメールアドレスとパスワードのフィールドを含むレイアウトを定義します。この画面は、フォームの送信時にAmplify.Auth.SignInを呼び出します。これに対応するためのAndroid組み込みの接続コンポーネントについては、こちらを参照してください。
このアクティビティ画面は、必ずAndroidManifest.xml:に追加してください。
<activity
android:name=".SignInActivity"
android:exported="false"
android:theme="@style/Theme.AppCompat.NoActionBar" />
アプリをビルドして実行し、エミュレーターでテストします。認証機能の準備は完了しているため、Cognitoのユーザープールに登録した2人のテストユーザーの認証情報を使用してサインインとサインアウトを実行できます。
ここでは、Amplifyを使用してサインイン済みユーザーのデータを保存・取得する方法を紹介します。そのために、サインイン済みユーザーに特定のビデオをお気に入りとしてマークしたり、マークを解除したりするよう指示します。 ユーザーがサインインすると、アプリはユーザーが選択したビデオを表示します。
ユーザー設定のデータはGraphQLデータベースに保存され、アプリはAmplify Data(英語のみ)を介してこのデータにアクセスします(内部ではAppSyncが使用されます)。
amplify/data/resource.tsを作成してAmplify Dataの使用を初期化し、データスキーマを定義(英語のみ)します。
import { type ClientSchema, a, defineData } from '@aws-amplify/backend';
const schema = a.schema({
Favorite: a
.model({
userId: a.string().required(),
videoId: a.string().required()
})
.authorization(allow => [
allow.authenticated()
]),
});
export type Schema = ClientSchema<typeof schema>;
export const data = defineData({
schema,
authorizationModes: {
defaultAuthorizationMode: 'userPool'
}
});
次に、これをamplify/backend.ts:にあるAmplifyのバックエンドの定義に追加します。
import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
import { storage } from './storage/resource';
import { data } from './data/resource';
defineBackend({
auth,
storage,
data,
});
最後に、AmplifyApp.ktでアプリにAWSApiPluginを追加(英語のみ)します。
...
import com.amplifyframework.api.aws.AWSApiPlugin
...
class AmplifyApp : Application() {
private val TAG = "AmplifyApp"
private val applicationScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
override fun onCreate() {
super.onCreate()
try {
Amplify.addPlugin(AWSS3StoragePlugin())
Amplify.addPlugin(AWSCognitoAuthPlugin())
Amplify.addPlugin(AWSApiPlugin())
...
これらの更新を行うと、実行中のAmplifyサンドボックスプロセスによりバックエンドリソースが更新され、Amplify Dataが利用できるようになります。AWSコンソールでAWS AppSyncサービスを確認すると、アプリ用にセットアップされたAPIがスキーマと共に表示されます。
Amplifyアプリの構成を更新したため、構成ファイル(amplify_outputs.json)は自動的に更新されます。更新済みのファイルには、Amplify Dataを利用するための詳細情報が記述されていることがわかります。
…
"data": {
"url": "https://kvvm75giyvdk5kjdttewbdfvry.appsync-api.us-east-1.amazonaws.com/graphql",
"aws_region": "us-east-1",
"default_authorization_type": "AMAZON_COGNITO_USER_POOLS",
"authorization_types": [
"AWS_IAM"
],
"model_introspection": {
"version": 1,
"models": {
"Favorite": {
"name": "Favorite",
"fields": {
"id": {
"name": "id",
"isArray": false,
"type": "ID",
"isRequired": true,
"attributes": []
},
"userId": {
"name": "userId",
"isArray": false,
"type": "String",
"isRequired": true,
"attributes": []
},
"videoId": {
"name": "videoId",
"isArray": false,
"type": "String",
"isRequired": true,
"attributes": []
},
…
次に、GraphQLクライアントコードを生成します。アプリはこのコードを使用することで、Amplifyを通じてデータバックエンドに送信するリクエストを簡単に作成できます。
~project/$ npx ampx \
--profile amplify-app-developer generate graphql-client-code \
--format modelgen \
--model-target java \
--out app/src/main/java/
上記のコマンドは、アプリがビルドして利用する3つのJavaファイルを生成します。
~/project/app/src/main/java/com$ tree amplifyframework
amplifyframework/
└── datastore
└── generated
└── model
├── AmplifyModelProvider.java
├── Favorite.java
└── FavoritePath.java
DetailsSupportFragment(英語のみ)を使用するように画面を修正します。これにより、一覧でビデオカードを選択すると、VideoDetailsFragment.ktで定義されている詳細ビューが表示されます。ビデオを再生する代わりに、選択可能な2つのアクションとして、ビデオの再生(三角形)とビデオのお気に入り登録/登録解除(ハート)のアイコンが表示されます。
次に、MainActivity.ktでカードの一覧ビューを更新して、お気に入りのステータスが表示されるように設定します。
また、ユーザーがサインインしている場合にのみ、各カードのお気に入りのステータスが表示されるようにチェックを追加できます。
ビデオがサインイン済みユーザーのお気に入りに登録されているかどうかを確認するには、Amplify.API.queryを使用します。リストアイテム(英語のみ)の呼び出しは簡単で、生成済みのFavoriteモデル用のGraphQLクライアントコードを利用できます。VideoCardPresenter.ktのコードは次のようになります。
private fun checkFavoriteStatus(
cardView: ImageCardView,
cardData: CardData) {
val userId = AuthStateManager.getInstance().getCurrentUserId()
val query = ModelQuery.list(Favorite::class.java,
Favorite
.VIDEO_ID.eq(cardData.videoId)
.and(Favorite.USER_ID.eq(userId))
)
Amplify.API.query(
query,
{ response ->
if (response.hasData()) {
val isFavorite = response.data.items.count() > 0
cardView.post {
updateFavoriteIcon(cardView, isFavorite)
}
}
},
{ error ->
Log.e(TAG, "お気に入りのステータスを確認する際のエラー: ${error.message}", error)
}
)
}
val favorite = Favorite.builder()
.userId(userId)
.videoId(videoData.videoId)
.build()
val createFavorite = ModelMutation.create(favorite)
Amplify.API.mutate(createFavorite,
{ response ->
checkFavoriteStatus()
},
{ error ->
Log.e(TAG, "お気に入りを作成する際のエラー: ${error.message}", error)
}
)
ビデオのお気に入り設定を解除するには、レコードのid を取得し、deleteミューテーション(英語のみ)を実行します。
val query = ModelQuery.list(Favorite::class.java,
Favorite
.VIDEO_ID.eq(videoData.videoId)
.and(Favorite.USER_ID.eq(userId))
)
Amplify.API.query(
query,
{ response ->
if (response.hasData() && response.data.items.any()) {
val favorite = response.data.items.first()
val deleteMutation = ModelMutation.delete(favorite)
Amplify.API.mutate(
deleteMutation,
{
isFavorited = false
updateFavoriteIcon()
},
{ error ->
Log.e(TAG, "お気に入りを削除する際のエラー: ${error.message}", error)
}
)
}
},
{ error ->
Log.e(TAG,
"お気に入りの削除についてクエリする際のエラー: ${error.message}",
Error
)
}
)
すべてのコードの準備ができたので、アプリをビルドして、エミュレーターでテストしてみましょう。個々のビデオをお気に入りとして登録したり、登録解除したりすることができます。
サインアウトして再びサインインすると、ユーザーのお気に入りが表示され、バックエンドデータが永続化されていることがわかります。
これでアプリは完成です。
1時間足らずで、Amplifyを利用して以下の処理を行うFire TV対応アプリを作成できました。
Amplifyに複雑な処理を任せることで、開発者はFire TVで快適に使用できるアプリの作成に集中できます。
Fire TV対応アプリの開発が初めての場合でも、短期間で試作品を作成する場合でも、Amplifyには適切なツールが用意されており、基礎を押さえながら迅速に開発を進めることができます。ここからさらにAmplifyを活用して、パーソナライズされたおすすめ機能、分析機能、プッシュ通知などを追加し、アプリの機能を充実させることができます。