Status bar に表示されるアプリの通知アイコンを気合いでアニメーションさせてみた(Android)

3 min read読了の目安(約3100字

Android 端末にプリインストールされている Google Play ストアが新規インストール or アプリ更新中に表示する通知アイコンのように GIF っぽい挙動をさせることはできないか気になりました 🤔

サンプル

ドキュメントに発行済み通知を更新する方法が載っていたためゴリ押しで実装してみました 🙇‍
setOnlyAlertOnce() を設定すると発行済みの通知を NotificationManagerCompat.notify() で更新してもサウンドを鳴らさないようにすることができます 🤫

実プロダクトに投入できる完成度ではないです 💦

class MainActivity : AppCompatActivity() {

  private val scope = CoroutineScope(Dispatchers.Default)

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)

    createNotificationChannelIfNeeded()

    val builder = NotificationCompat.Builder(this, CHANNEL_ID).also { builder ->
      builder.setContentTitle("タイトル")
      builder.setContentText("メッセージ")
      builder.setSmallIcon(R.drawable.ic_outline_star_24)
      builder.priority = NotificationCompat.PRIORITY_LOW
      builder.setOnlyAlertOnce(true)
    }

    val notificationManager = NotificationManagerCompat.from(this)

    scope.launch {
      var smallIconRes = R.drawable.ic_outline_star_half_24
      while (true) {
        val deferred = async(Dispatchers.IO) {
          Thread.sleep(500) // 0.5 秒待つ

          when (smallIconRes) { // アイコンは https://fonts.google.com/icons から拝借しました
            R.drawable.ic_outline_star_outline_24 -> R.drawable.ic_outline_star_half_24
            R.drawable.ic_outline_star_half_24 -> R.drawable.ic_outline_star_24
            else -> R.drawable.ic_outline_star_outline_24
          }
        }

        withContext(Dispatchers.Main) {
          smallIconRes = deferred.await()
          builder.setSmallIcon(smallIconRes)
          notificationManager.notify(NOTIFICATION_ID, builder.build())
        }
      }
    }
  }

  /**
   * チャンネル作成の必要があれば実施
   */
  private fun createNotificationChannelIfNeeded() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
      if (notificationManager.getNotificationChannel(CHANNEL_ID) == null) {
        val name = getString(R.string.channel_name)
        val importance = NotificationManager.IMPORTANCE_DEFAULT
        val channel = NotificationChannel(CHANNEL_ID, name, importance).also { channel ->
          channel.description = getString(R.string.channel_description)
          channel.setSound(null, null) // 通知音を鳴らさない
        }
        notificationManager.createNotificationChannel(channel)
      }
    }
  }

  companion object {
    private const val CHANNEL_ID = "CHANNEL_ID"
    private const val NOTIFICATION_ID = 0
  }
}

実行結果

通知アイコンを動いているように見せることができました ⭐︎