امتحان کردن codelab AppCard

این صفحه نحوه پیاده‌سازی AppCard را شرح می‌دهد.

مرحله ۱: افزودن همه واردات

  1. برای افزودن واردات:

    static_libs: [
         …
         "car-app-card",
    ],
    

مرحله ۲: SimpleAppCardContentProvider را به مانیفست خود اضافه کنید

  1. حتماً android:authorities (به‌صورت پررنگ) را با نام بسته‌تان جایگزین کنید.

    <!-- App Card Content Provider that is required to communicate
         relevant App Cards with the system
         android.car.permission.BIND_APP_CARD_PROVIDER is an essential permission
         that will only allow the system to communicate with the
         AppCardContentProvider
         The content provider must also be exported and enabled -->
         <provider android:name=".SimpleAppCardContentProvider"
             android:authorities="com.example.appcard.sample" 
             android:permission="@string/host_permission"
             android:exported="true"
             android:enabled="true">
             <intent-filter>
                <!-- This intent filter will allow the system to find and
                     connect with the application's AppCardContentProvider -->
                 <action android:name="com.android.car.appcard.APP_CARD_PROVIDER" />
                </intent-filter>
         </provider>
    

مرحله ۳: SimpleAppCardContentProvider را به مانیفست اضافه کنید

  1. برای افزودن SimpleAppCardContentProvider به مانیفست:

    class SimpleAppCardContentProvider : AppCardContentProvider() {
      /** Must return same authority as defined in manifest */
      override val authority: String = AUTHORITY
    
      /** Setup [AppCardContentProvider] and its constituents */
      override fun onCreate(): Boolean {
        return super.onCreate()
      }
    
      /** Setup an [AppCard] that is being requested */
      override fun onAppCardAdded(id: String, ctx: AppCardContext): AppCard {
        return when (id) {
          APPCARD_ID -> //TODO: create app card
    
          else -> throw IllegalStateException("Unidentified app card ID: $id")
        }
      }
    
      /** List of supported [AppCard] IDs */
    ​   ​override val appCardIds: List<String> = listOf(APPCARD_ID).toMutableList()
    
      /** Clean up when an [AppCard] is removed */
      override fun onAppCardRemoved(id: String) {
        when (id) {
          APPCARD_ID -> //TODO: create app card
        }
      }
    
      /** Handle an [AppCardContext] change for a particular [AppCard] ID */
      override fun onAppCardContextChanged(
        id: String,
        appCardContext: AppCardContext
      ) {
         when (id) {
          APPCARD_ID -> //TODO: update AppCardContext
        }
      }
    
      companion object {
        private const val AUTHORITY = "com.example.appcard.sample"
        private const val APPCARD_ID = "sampleAppCard"
      }
    }
    

مرحله ۴: ایجاد AppCard

  1. برای ایجاد AppCard:

    override fun onAppCardAdded(id: String, ctx: AppCardContext): AppCard {
      return when (id) {
        APPCARD_ID -> createAppCard(ctx)
    
        else -> throw IllegalStateException("Unidentified app card ID: $id")
      }
    }
    
    private fun createAppCard(appCardContext: AppCardContext): ImageAppCard {
      return ImageAppCard.newBuilder(APPCARD_ID)
        .setPrimaryText("Hello")
        .setSecondaryText("World")
        .setHeader(
          Header.newBuilder("header")
            .setTitle("Code Lab")
            .build()
        )
        .addButton(
          Button.newBuilder(
            "button",
            Button.ButtonType.PRIMARY,
            object : OnClickListener {
              override fun onClick() {
                //no-op
              }
            }
          )
            .setText("Click me!")
            .build()
        )
       .build()
    }
    

برای مثال:

ایجاد AppCard

شکل ۱. ‫AppCard بساز.

مرحله ۵: فعال کردن کلیک‌کننده دکمه

برای فعال کردن کلیکر:

   private var clickCounter = 0

   private fun createAppCard(appCardContext: AppCardContext): ImageAppCard {
      ...
       .addButton(
        Button.newBuilder(
          "button",
           Button.ButtonType.PRIMARY,
           object : OnClickListener {
             override fun onClick() {
               clickCounter++
               sendAppCardUpdate(createAppCard(appCardContext))
             }
           }
         )
           .setText(
             if (clickCounter == 0) "Click me!" else "Clicked: $clickCounter"
           )
           .build()
         )
      ...
    }

    override fun onAppCardRemoved(id: String) {
      when (id) {
        APPCARD_ID -> clickCounter = 0
      }
    }

برای مثال:

فعال کردن کلیک‌کننده دکمه

شکل ۲. کلیک‌کننده دکمه را فعال کنید.

مرحله ۶: به‌روزرسانی تصویر سرایند

  1. برای به‌روزرسانی تصویر در سرصفحه:

      private fun createAppCard(appCardContext: AppCardContext): ImageAppCard {
        val headerImageSize = appCardContext.imageAppCardContext.getMaxImageSize(Header::class.java)
        val logo = resToBitmap(
         android.R.drawable.ic_menu_compass,
           headerImageSize.width,
           headerImageSize.height
        )
    
     ...
       .setHeader(
         Header.newBuilder("header")
           .setTitle("Code Lab")
           .setImage(
             Image.newBuilder("image")
               .setContentScale(Image.ContentScale.FILL_BOUNDS)
               .setColorFilter(Image.ColorFilter.TINT)
               .setImageData(logo)
               .build()
           )
           .build()
        )
      ...
      }
    
      private fun resToBitmap(res: Int, width: Int, height: Int): Bitmap {
        val drawable = context?.getDrawable(res)
    
        return drawableToBitmap(drawable!!, width, height)
      }
    
      private fun drawableToBitmap(d: Drawable, width: Int, height: Int): Bitmap {
        val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
    
        val canvas = Canvas(bitmap)
        val left = 0
        val top = 0
        d.setBounds(left, top, canvas.width, canvas.height)
        d.draw(canvas)
    
        return bitmap
     }
     ```
    
    For example:
    
    ![Change the image in the header](/docs/automotive/unbundled_apps/appcards/images/ceramic-06.png)
    
    **Figure 3.** Change the name to the header.
    
  2. این فرایند را تکرار کنید تا به هر مؤلفه‌ای که از آن پشتیبانی می‌کند تصویر اضافه کنید.

مرحله ۷: افزودن تعامل‌های بیشتر

  1. برای ایجاد نوار پیشرفت با کنترل‌ها:

     private var progressOn = false
    
     private var progressTimer: Timer? = null
    
     private var progressCounter = 0
    
     override fun onAppCardRemoved(id: String) {
       when (id) {
         APPCARD_ID -> {
           clickCounter = 0
           progressCounter = 0
           progressTimer?.cancel()
         }
       }
     }
    
     private fun createAppCard(appCardContext: AppCardContext): ImageAppCard {
       val buttonImageSize = appCardContext.imageAppCardContext.getMaxImageSize(Button::class.java)
       val progressPlayPauseImage = resToBitmap(
         if (progressOn) {
           android.R.drawable.ic_media_pause
         } else {
           android.R.drawable.ic_media_play
         },
         buttonImageSize.width,
         buttonImageSize.height
       )
       ...
         .setProgressBar(
           createProgressBar()
         )
         .addButton(
           Button.newBuilder(
             "progressButton",
             Button.ButtonType.NO_BACKGROUND,
             object : OnClickListener {
               override fun onClick() {
                 progressOn = !progressOn
                 if (progressOn) {
                   progressTimer = Timer()
                   progressTimer?.scheduleAtFixedRate(object : TimerTask() {
                     override fun run() {
                     progressCounter++
                     if (progressCounter > 60) progressCounter = 0
                     sendAppCardComponentUpdate(APPCARD_ID, createProgressBar())
                   }
                 }, SECONDS_TO_MS, SECONDS_TO_MS)
               } else {
                 progressTimer?.cancel()
                 progressTimer = null
               }
               sendAppCardUpdate(createAppCard(appCardContext))
             }
           }
         )
           .setImage(
             Image.newBuilder("buttonImage")
               .setContentScale(Image.ContentScale.FILL_BOUNDS)
               .setColorFilter(Image.ColorFilter.TINT)
               .setImageData(progressPlayPauseImage)
               .build()
           )
           .build()
       )
     ...
     }
    
     private fun createProgressBar(): ProgressBar {
       return ProgressBar.newBuilder(PROGRESS_BAR_ID, 0, 60)
         .setProgress(progressCounter)
         .build()
     }
    
     companion object {
       ...
       private const val PROGRESS_BAR_ID = "progress"
       private const val SECONDS_TO_MS = 1000L
     }
    

دکمه پخش یا مکث را به نوار پیشرفت اضافه کردیم که می‌تواند هر ثانیه به‌روز شود. به‌دلیل محدودیت‌های اندازه، setText را روی دکمه کلیک‌کننده ببینید که برای نشان دادن تعداد کلیک‌ها اضافه شده است، .setText("$clickCounter")

شکل ۴. دکمه‌ای اضافه کنید.

شکل ۵. دکمه پردازش‌شده.

مرحله ۸: راه‌اندازی فعالیت

  1. به‌شرطی که برنامه‌تان با [background-starts#exceptions][1] مطابقت داشته باشد، می‌توانید فعالیتی را از onClickListener دکمه راه‌اندازی کنید.

    class SampleRoutingActivity : AppCompatActivity() {
      override fun onStart() {
        super.onStart()
        val intent = Intent(ACTION_LOCATION_SOURCE_SETTINGS).apply {
          setFlags(FLAG_ACTIVITY_CLEAR_TOP)
        }
        startActivity(intent)
        finish()
      }
    }
    
  2. برای شروع فعالیت، دکمه‌ای در AppCard اضافه کنید. اگر هیچ فعالیتی وجود ندارد، فعالیت مسیریابی به برنامه‌تان اضافه کنید:

    override fun onStart() {
    super.onStart()
    val intent = Intent(ACTION_LOCATION_SOURCE_SETTINGS).apply {
      setFlags(FLAG_ACTIVITY_CLEAR_TOP)
    }
    startActivity(intent)
    finish()
     }
    }
    

    وقتی کلاس فراخوانی می‌شود، کاربر به تنظیمات مکان هدایت می‌شود. setFlags(FLAG_ACTIVITY_CLEAR_TOP) اعمال می‌شود تا اطمینان حاصل شود کاربر نمی‌تواند به فعالیت اصلی برگردد.

  3. دکمه‌ای در AppCard برای شروع فعالیت اضافه کنید:

     private fun createAppCard(appCardContext: AppCardContext): ImageAppCard {
       val locationImage = resToBitmap(
         android.R.drawable.ic_menu_call,
         buttonImageSize.width,
         buttonImageSize.height
       )
       ...
         .addButton(
           Button.newBuilder(
            "activityButton",
            Button.ButtonType.SECONDARY,
            object : OnClickListener {
              override fun onClick() {
                // no-op
              }
            }
           )
            .setImage(
              Image.newBuilder("locationButtonImage")
                .setContentScale(Image.ContentScale.FILL_BOUNDS)
                .setColorFilter(Image.ColorFilter.TINT)
                .setImageData(locationImage)
                .build()
            )
            .setIntent(
              RoutingActivityIntent
                .newBuilder("com.example.appcard.sample.SampleRoutingActivity")
                .build()
            )
            .build()
          )
        ...
    }
    

به‌دلیل محدودیت‌های فضا، دکمه کلیک‌کننده در میزبان AppCard برداشته شد. AppCard به‌صورت زیر نشان داده می‌شود:

دکمه برداشته شد

شکل ۶. AppCard بدون دکمه کلیک‌کننده.

[1]: https://developer.android.com/guide/components/activities/background-starts#exceptions