as

Settings
Sign out
Notifications
Alexa
亚马逊应用商店
AWS
文档
Support
Contact Us
My Cases
设备
构建
测试
应用发布
相关资源
文档

利用AWS Amplify不到一小时即可构建Fire TV应用

Share:
How to Fire TV
Blog_Header_Post_Img

构建Fire TV应用已经需要考虑诸多事项。您需要处理Fire TV远程控制导航、需要适应远距离观看的用户界面布局,以及与移动应用有着根本性不同的用户体验行为。

如果还要花费时间去处理其他一些事项,例如连接身份验证、管理后端数据或明确如何存储和提供媒体资产等,可能会令您无法承受。若能将上述工作都转移到对开发者友好且易于集成的服务上,那么您将节省大量时间,避免不必要的麻烦。

这正是AWS Amplify的用武之地。AWS Amplify帮助开发者使用AWS服务以很少的后端配置快速构建、部署和扩展全栈网页和移动应用。Amplify可以助您处理所有后端工作,包括身份验证、云存储和数据持久化。这样一来,您就可以专注于打造Fire TV上的流畅体验。

先决条件

要遵照本Fire TV应用教程,您需要准备好以下项目:

  • 下载并安装好的Android Studio
  • 物理Fire TV设备或Android模拟器(我们将向您展示如何设置该模拟器)
  • 具有资源配置权限的AWS账户
  • 安装好的Amplify CLI (Gen 2)
  • 基本的Kotlin/Android经验,以便针对AWS Amplify Android应用开发,轻松编辑布局并关联屏幕

请一定记住,这是个演示,重点介绍Amplify以及相关的Fire OS应用开发 - 这并非生产级应用,因此没有纳入安全最佳实践。

为什么Amplify是Fire TV应用开发者的颠覆性利器

Fire TV应用开发存在着其特有的一些用户体验挑战,包括:方向键导航专门针对电视优化的布局以及音频焦点处理。如果在应对上述挑战之外还要处理后端基础架构,可能在用户界面工作开始之前您就已耗费大量心力,倍感沮丧。

Amplify可以带给您便捷。它可让您跳过下列繁琐的后端工作,直接专注于应用的打造:

  • 身份验证使用Amazon AWS Cognito添加安全的登录流程。
  • 存储使用Amazon S3存储和检索资产。
  • 数据和API使用Amplify Data持久化应用数据,Amplify Data使用AppSync和GraphQL来完成这一工作。

利用本指南创建应用,主要可在上述三个特性上获益,但Amplify还为您提供无服务器功能,可让您在不用管理基础架构的情况下为后台处理添加Lambda函数。

这意味着您还将学习无服务器应用开发,这样您就可以专注于打造自己的Fire TV体验,而不用从头开始连接云服务

步骤1:设置Amplify后端

第一步是设置Amplify后端,为我们的应用提供身份验证、存储和数据方面的支持。

创建具有足够权限的IAM用户

在使用Amplify CLI配置资源之前,您需要具有足够权限的IAM Identity Center凭证。首先登录AWS控制台并在IAM Identity Center创建新用户。

Amazon Appstore Android developer's guide to React Native blog image

创建用户后,您将看到新用户的访问门户URL、用户名和一次性密码。

Amazon Appstore Android developer's guide to React Native blog image

按照此处(仅提供英文版)的其余说明完成用户设置,并从命令行进行Amplify相关调用时将使用的本地AWS配置文件设置。在本指南的其余部分,我们将假设存在一个名为amplify-app-developer的本地AWS配置文件,并且该配置文件具有arn:aws:iam::aws:policy/service-role/AmplifyBackendDeployFullAccess权限。

初始化Amplify项目

在您的项目文件夹中,运行以下命令:

Copied to clipboard
~/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的文件夹。
 

启动您的Amplify沙盒

使用您创建的AWS配置文件创建Amplify沙盒。这将提供可用于开发的资源(例如用于存储的S3、用于数据的托管GraphQL和用于身份验证的Cognito)。当进程运行时,它会检测到您可对amplify子文件夹中的文件所做的更改,并相应地更新您的沙盒。

Copied to clipboard
$ 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环境的启动过程。进行各个步骤的单击操作以完成此设置。

 

Amazon Appstore Android developer's guide to React Native blog image
Amazon Appstore Android developer's guide to React Native blog image

最终,此启动过程使用AWS CloudFormation来配置AWS云开发工具包使用的资源。CloudFormation堆栈将触及S3、ECR和IAM中的资源。

Amazon Appstore Android developer's guide to React Native blog image

启动完成后,再次运行sandbox命令。

Copied to clipboard
$ 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沙盒进程在此终端中运行。

步骤2:为Fire TV设置Android项目

现在Amplify已经启动并运行,让我们开始构建Android项目。

 

在Android Studio中创建新项目

打开Android Studio(安装说明)。前往File(文件)> New(新建)> New Project(新项目)。

选择带有Empty Activity(空活动)的Television(电视)模板。

Amazon Appstore Android developer's guide to React Native blog image

指定Package name(程序包名称)(例如com.example.amplifyfiretv)。将Save Location(保存位置)设置为项目根文件夹。选择最低SDK。对于本例,我们将使用API 29。

Amazon Appstore Android developer's guide to React Native blog image

这样将初始化您的新项目,创建许多新文件。一旦Android Studio首次打开您的项目,它将花费几分钟时间下载依赖项。

Amazon Appstore Android developer's guide to React Native blog image

创建要在模拟器中使用的Android TV虚拟设备

为了测试我们的应用,我们将使用Android模拟器。您可以在此页面上找到有关Android TV模拟器和Fire TV应用的更多详细信息。

在Android Studio中,转到Device Manager(设备管理器)。单击Create Virtual Device(创建虚拟设备)。创建New Hardware Profile(新的硬件配置文件)。使用以下设置创建配置文件:

  • Device type(设备类型):Android TV
  • Screen size(屏幕尺寸):40 inch
  • Resolution(分辨率):1280 x 720 px
  • Navigation Style(导航方式):D-Pad
  • Supported device states(支持的设备状态):仅Landscape
  • Cameras and Sensors(相机和传感器):全部取消选中

为您的虚拟设备指定名称。在这个示例中,设备将被命名为“Fire TV Emulator”,您可以自行为其指定任何名称。

Amazon Appstore Android developer's guide to React Native blog image

单击Finish(完成)。然后,选择新创建的配置文件并单击Next(下一步)。对于系统映像,将API级别选择为23或更高级别。在本指南中,我们将使用Q (29)。下载映像,并为该虚拟设备选择映像。

 

Amazon Appstore Android developer's guide to React Native blog image

结束虚拟设备的创建。然后启动模拟器。

Amazon Appstore Android developer's guide to React Native blog image

在设置好我们的初始项目后,我们可以在模拟器中对其进行测试。

Amazon Appstore Android developer's guide to React Native blog image

使用Leanback库

适用于Android TV应用的Android Studio初始设置使用适用于Android TV的Jetpack Compose。Jetpack Compose是Android Studio中用于构建用户界面的默认工具包。然而,对于我们的Fire TV应用,我们将使用Android的Leanback库,因为Fire TV对Leanback的用户界面组件和功能有着很好的支持。因此,您可以如下所示修改app/build.gradle.kts文件:

Copied to clipboard
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”开始。

Copied to clipboard
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卡数据由CardDataProvider实时读入,在名为CardData.kt

的文件中定义:

Copied to clipboard
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中。

在模拟器中测试,我们应用的外观如下所示:

AWS Amplify Screenshot
AWS Amplify Screenshot

步骤3:将项目连接至Amplify

我们已经设置了Amplify,但需要将其添加到我们的项目中。

首先,将Amplify依赖项添加到app/build.gradle.kts中:

Copied to clipboard
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的新文件。其内容如下所示:

Copied to clipboard
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, "Error initializing Amplify", error)
        } catch (error: Exception) {
            Log.e(TAG, "Unexpected error during initialization", error)
        }
    }
} 

然后,修改app/src/main/AndroidManifest.xml,要求Android使用AmplifyApp类作为Application类。

Copied to clipboard
    <application
        android:name=".AmplifyApp"

步骤4:使用Amplify添加采用S3的存储

使用Amplify的好处之一在于,您可以在AWS S3中存储和检索资产。将您的cards.json文件移动到S3存储桶中,以此作为一个简单的测试。应用在编译时不会在本地引用此文件,而是会实时从S3检索卡片的数据。

有关如何使用Amplify在Android项目中设置存储,请参阅此处的文档(仅提供英文版)。

 

定义Amplify存储

继续操作并创建amplify/storage/resource.ts

Copied to clipboard
import { defineStorage } from '@aws-amplify/backend';

export const storage = defineStorage({
  name: 'my-bucket',
  access: (allow) => ({
    'data/*': [
      allow.guest.to(['get']),
      allow.authenticated.to(['get'])
    ]
  })
});

您需要把cards.json文件上传到S3存储桶的data子文件夹中,并允许未经身份验证的(访客)和经过身份验证的用户访问。

 


定义Amplify身份验证和授权

在本演练中,您还将为Amplify添加身份验证(使用Cognito),因为这是使用存储所必需的。

amplify/auth/resource.ts中的内容如下:

Copied to clipboard
import { defineAuth } from '@aws-amplify/backend';

export const auth = defineAuth({
  loginWith: {
    email: true,
  },
});

定义Amplify后端

然后,创建amplify/backend.ts,规定您将使用自己的身份验证和存储定义。

Copied to clipboard
import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource';
import { storage } from './storage/resource';

defineBackend({
  auth,
  storage,
});

创建这些文件后,正在运行的Amplify沙盒进程将更新AWS资源,以处理您的身份验证和存储需求。

 

指定应用的amplify_outputs.json文件的路径

Amplify沙盒进程生成一个客户端配置文件 (amplify_outputs.json),您的应用在连接到Amplify为您启动的各种资源时将使用该文件。每当运行中的进程检测到您的Amplify配置发生更改时(例如我们在上面所做的更改),这个文件都会自动更新。

默认情况下,amplify_outputs.json直接写入运行Amplify沙盒进程的目录中。但是,您的Fire TV应用需要app/src/main/res/raw/amplify_outputs.json中提供的文件。重新启动Amplify沙盒进程,明确指定从现在开始写入文件应当使用的路径:

Copied to clipboard
$ 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

cards.json 上传至Amplify S3存储桶

在上面生成的amplify_outputs.json文件中,您可以看到为Amplify项目创建的S3存储桶的名称。

Copied to clipboard
 "storage": {
    "aws_region": "us-east-1",
    "bucket_name": "amplify-project-firedev-sandb-mybucket-ovpsejk2htya",
    ...

在AWS控制台中,导航至S3存储桶,创建一个名为data的子文件夹,然后将cards.json上传到该文件夹。得到的该文件的S3 URI为:

Copied to clipboard
s3://amplify-project-firedev-sandb-mybucket-ovpsejk2htya/data/cards.json

修改CardDataProvider以从S3检索cards.json 

接下来,在CardData.kt中修改您的代码。它不会在本地从cards.json读取,而是在amplify_outputs.json中查找S3存储桶名称,然后调用Amplify资源来下载远程文件并将其保存到本地缓存。initialize函数的关键代码行如下所示:

Copied to clipboard
// Set up local file for downloading
val localFile = File(cacheDir, FILE_NAME)

// Get bucket name from 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
                
// Set up S3 path - using data/ prefix to match permissions
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()

Copied to clipboard
CardDataProvider.setOnCardsLoadedListener {
  updateContent()
}

最后,更新AmplifyApp.kt代码以初始化CardDataProvider、用于存储的S3、用于身份验证的Cognito。

Copied to clipboard
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,我们可以构建应用程序并再次运行,在模拟器中进行测试。

AWS Amplify Screenshot

卡片图像和视频播放功能符合预期。您已成功使用Amplify集成了采用S3的存储。

如果您需要代码存储库,敬请期待我们正在编写的题为敬请期待我们正在编写的题为Retrieves card data via remote file with Amplify and S3(使用Amplify和S3通过远程文件检索卡数据)的文章。

步骤5:实现登录流程

为了通过Amplify和S3使用存储,您已经创建并部署了身份验证服务。现在,应该创建一个身份验证流程来处理用户登录和存储会话信息。

有关如何使用Amplify在Android项目中设置身份验证,请参阅此处的文档(仅提供英文版)。

 

在Cognito用户池中创建测试用户

要准备好进行测试,请在Cognito用户池中创建一些用户。在amplify_outputs.json中,可以看到user_pool_id。例如:

Copied to clipboard
"auth": {
    "user_pool_id": "us-east-1_8Riey238F",
    "aws_region": "us-east-1",
    …

在AWS控制台中,导航到Cognito并找到具有此ID的用户池。在用户池中,导航至User management(用户管理)-> Users(用户)然后创建一些新用户。在本指南中,我们创建两个用户:

电子邮件地址 密码
joe@example.com My password is 1 number.
jen@example.com My password has 0 numbers.

 

创建这些用户后,您会注意到该用户的确认状态为“Force change password”(强制更改密码)。 由于您使用的只是测试用户,因此您应该使用这些密码确认这些用户账户。这可以使用AWS CLI来完成。例如:

密码

Copied to clipboard
$ aws cognito-idp admin-set-user-password \
    --profile amplify-app-developer \
    --user-pool-id us-east-1_8Riey238F \
    --username jen@example.com \
    --password "My password has 0 numbers." \
    --permanent

现在,您的两个测试用户已得到确认。

AWS Amplify Screenshot

将登录状态添加到主屏幕

为用户在主页上添加一个简单登录状态。创建一个调用checkAuthSession()函数,以确定用户是否已登录。如果用户未登录,则会显示“Sign In”(登录)。如果用户已登录,则会显示已登录用户的电子邮件地址以及“Sign Out”(登出)。

Copied to clipboard
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)
      }
    }
  )
}

在模拟器中,具有不同状态的页面如下所示:

AWS Amplify Screenshot
AWS Amplify Screenshot

添加登录屏幕

接下来,创建SignInActivity.kt,它定义了登录的活动屏幕。在app/src/main/res/layout/activity_sign_in.xml, 中定义布局,其中应包含电子邮件地址和密码的字段。该屏幕在进行表单提交时会调用Amplify.Auth.SignIn。您可以在此处(仅提供英文版)查看内置的连接Android的组件。

AWS Amplify Screenshot

请务必将此活动屏幕添加至AndroidManifest.xml

Copied to clipboard
<activity
  android:name=".SignInActivity"
  android:exported="false"
  android:theme="@style/Theme.AppCompat.NoActionBar" />

测试应用程序

构建并运行应用程序,在模拟器中进行测试。身份验证已启动并运行,您可以使用Cognito用户池中两个测试用户的凭证进行登录和登出。

步骤6:为已登录用户保存视频收藏夹

我们将展示如何使用Amplify为已登录用户存储和检索数据。为此,我们将允许已登录用户选择特定的视频来标记或取消标记为“favorites”(收藏项)。 当用户登录时,我们的应用将显示用户选择的视频。

有关用户首选项的数据将存储在GraphQL数据库中,我们的应用将通过Amplify数据(其在后台使用AppSync)与该数据库接合。

 

定义Amplify数据

创建amplify/data/resource.ts以初始化您对Amplify数据的使用,并定义您的数据架构

Copied to clipboard
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后端定义:

Copied to clipboard
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

Copied to clipboard
...
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数据的使用。在AWS控制台中快速检查AWS AppSync服务,可以看到为应用程序设置的API以及架构。

AWS Amplify Screenshot

审查应用的amplify_outputs.json文件

因为您已经更新了Amplify应用程序配置,您的配置文件 (amplify_outputs.json) 将自动更新。您现在可以看到更新的文件中包含有关您使用Amplify数据的详细信息。

Copied to clipboard
…
"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客户端代码

然后,需要生成GraphQL客户端代码,应用可以使用该代码方便地编写您将通过Amplify发送到数据后端的请求。

Copied to clipboard
~project/$ npx ampx \
           --profile amplify-app-developer generate graphql-client-code \
           --format modelgen \
           --model-target java \
           --out app/src/main/java/

上面的命令会生成三个Java文件,您的应用程序将构建和使用这些文件

Copied to clipboard
~/project/app/src/main/java/com$ tree amplifyframework

amplifyframework/
└── datastore
    └── generated
        └── model
            ├── AmplifyModelProvider.java
            ├── Favorite.java
            └── FavoritePath.java

更新屏幕以显示收藏状态

修改屏幕以使用DetailsSupportFragment。现在,当您选择行中的视频卡片时,会打开您在VideoDetailsFragment.kt中定义的详情视图。不是播放视频,而是显示两种可能操作的图标:播放视频(三角形)或收藏/取消收藏视频(心形)。

AWS Amplify Screenshot

接下来,更新MainActivity.kt中卡片行的视图,以显示收藏状态。

AWS Amplify Screenshot

您还可以添加一个复选框,以确保每张卡的收藏状态仅在用户登录时显示。

AWS Amplify Screenshot

实现对Amplify.API.query的调用以检索收藏状态

使用Amplify.API.query查看视频是否为已登录用户的收藏项。列表项目调用很简单,您可以利用为您生成的Favorite模型的GraphQL客户端代码。在VideoCardPresenter.kt代码中的内容如下:

Copied to clipboard
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)
    }
  )
}

实现对Amplify.API.mutate的调用以切换收藏状态

然后,在VideoDetailsFragment.kt中,实现心形图标点击时收藏状态的切换。要将视频设置为收藏项,在后端数据中创建Favorite,然后刷新图标显示:

Copied to clipboard
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 creating favorite: ${error.message}", error)
  }
)

要取消将视频设置为收藏项,请检索记录的id,然后运行delete mutation

Copied to clipboard
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
           )
  }
)

测试应用程序

所有代码就绪后,构建应用并在模拟器中进行测试。您可以将单个视频标记为收藏项或取消标记为收藏项。

AWS Amplify Screenshot

注销并重新登录后,将显示用户的收藏夹,表明后端数据仍然存在。

AWS Amplify Screenshot

应用程序即告完成!

总结

在不到一个小时的时间,我们构建了一个Fire TV应用,该应用使用Amplify来处理以下方面:

  • 使用Cognito对用户进行身份验证
  • 从S3动态地显示内容
  • 使用AppSync和GraphQL存储用户首选项

Amplify替我们处理繁重的工作,让我们专注于最重要的事情:构建在Fire TV上使用体验出色的应用。

如果您是Fire TV开发的新手,或者只想更快地制作原型,Amplify可为您提供一套正确的工具,可以在不跳过基础工作的情况下快速完成工作。以此为基础,您可以通过个性化推荐、分析或推送通知等功能扩展应用,所有这些功能都可获得Amplify的支持。

相关文章

最新文章

 

查看有关亚马逊应用商店、应用开发与盈利、亚马逊服务以及更多主题的最新消息。