Wear OS에서 알림 만들기

이 페이지에서는 시계에 관한 알림을 만드는 방법을 설명합니다. 또한 연결된 스마트폰에서 시계에 연결된 각 알림에 관해 추가 콘텐츠를 제공하는 방법을 설명합니다.

시계의 알림은 스마트폰의 알림과 동일한 구조입니다. 또한 사용자에게 최적의 환경을 제공하기 위해 Wear OS by Google은 웨어러블 관련 기능을 알림에 추가하기 위한 API를 제공합니다.

앱에서 내보내는 각 알림은 알림 스트림에 카드로 나타납니다.

그림 1. 스마트폰과 시계에 표시된 동일한 알림

시계와 스마트폰 모두 알림의 소스가 될 수 있습니다. 웨어러블용 알림을 만들려면 NotificationCompat.Builder 클래스를 사용합니다. 이 클래스를 사용하여 알림을 만들면 시스템에서 알림을 올바르게 표시합니다.

참고: RemoteViews를 사용하는 알림에서는 맞춤형 레이아웃이 제거되어, 웨어러블에는 텍스트와 아이콘만 표시됩니다. 그러나 시계에서 실행되는 웨어러블 앱을 만들어 맞춤형 카드 레이아웃을 사용하는 맞춤형 알림을 만들 수 있습니다.

필요한 클래스 가져오기

필요한 패키지를 가져오려면 다음 줄을 build.gradle 파일에 추가합니다.

    compile "com.android.support:support-v13:27.0.2"
    

이제 프로젝트가 필요한 패키지에 액세스할 수 있으므로 지원 라이브러리에서 필요한 클래스를 가져옵니다.

Kotlin

    import android.support.v4.app.NotificationCompat
    import android.support.v4.app.NotificationManagerCompat
    import android.support.v4.app.NotificationCompat.WearableExtender
    

자바

    import android.support.v4.app.NotificationCompat;
    import android.support.v4.app.NotificationManagerCompat;
    import android.support.v4.app.NotificationCompat.WearableExtender;
    

알림 빌더로 알림 만들기

v4 지원 라이브러리를 사용하면 작업 버튼 및 아이콘과 같은 최신 알림 기능을 사용하여 알림을 만드는 한편, Android 1.6(API 레벨 4) 이상과의 호환성을 유지할 수 있습니다.

참고: Android 8.0(API 레벨 26)부터, 표시하고자 하는 각 알림 유형에 관해 알림 채널을 만들어야 합니다.

지원 라이브러리로 알림을 만들려면 다음 단계를 따르세요.

  1. NotificationCompat.Builder의 인스턴스를 만듭니다.

    Kotlin

        val notificationId = 1
        // The channel ID of the notification.
        val id = "my_channel_01"
        // Build intent for notification content
        val viewPendingIntent = Intent(this, ViewEventActivity::class.java).let { viewIntent ->
            viewIntent.putExtra(EXTRA_EVENT_ID, eventId)
            PendingIntent.getActivity(this, 0, viewIntent, 0)
        }
    
        // Notification channel ID is ignored for Android 7.1.1
        // (API level 25) and lower.
        val notificationBuilder = NotificationCompat.Builder(this, id)
                .setSmallIcon(R.drawable.ic_event)
                .setContentTitle(eventTitle)
                .setContentText(eventLocation)
                .setContentIntent(viewPendingIntent)
        

    자바

        int notificationId = 001;
        // The channel ID of the notification.
        String id = "my_channel_01";
        // Build intent for notification content
        Intent viewIntent = new Intent(this, ViewEventActivity.class);
        viewIntent.putExtra(EXTRA_EVENT_ID, eventId);
        PendingIntent viewPendingIntent =
                PendingIntent.getActivity(this, 0, viewIntent, 0);
    
        // Notification channel ID is ignored for Android 7.1.1
        // (API level 25) and lower.
        NotificationCompat.Builder notificationBuilder =
            new NotificationCompat.Builder(this, id)
                .setSmallIcon(R.drawable.ic_event)
                .setContentTitle(eventTitle)
                .setContentText(eventLocation)
                .setContentIntent(viewPendingIntent);
        
  2. 알림 ID가 있는 알림 객체를 notify()에 전달하여 알림을 내보냅니다.

    Kotlin

        NotificationManagerCompat.from(this).apply {
            notify(notificationId, notificationBuilder.build())
        }
        

    자바

        // Get an instance of the NotificationManager service
        NotificationManagerCompat notificationManager =
                NotificationManagerCompat.from(this);
    
        // Issue the notification with notification manager.
        notificationManager.notify(notificationId, notificationBuilder.build());
        

스마트폰에 이 알림이 나타나면 사용자는 알림을 터치하여 setContentIntent() 메서드에서 지정한 PendingIntent를 호출할 수 있습니다. 이 알림이 웨어러블에 나타나면 알림 스트림에도 나타납니다. 연결된 알림의 경우 사용자는 알림을 클릭하여 확장된 알림을 보고 열기 작업과 같은 정의된 작업을 트리거할 수 있습니다. 일반적으로 이러한 작업은 스마트폰 앱에서 활동을 엽니다.

확장된 알림 구성

확장된 알림은 각 알림에 대한 실질적인 추가 콘텐츠 및 작업을 제공합니다. 알림에 관한 추가 콘텐츠 페이지 및 작업을 지정하면, 확장된 알림 내에서 사용자가 이를 이용할 수 있습니다. 확장된 각 알림은 Wear OS용 머티리얼 다지인을 따르므로 사용자에게 앱과 같은 환경이 제공됩니다.

확장된 알림의 첫 번째 작업에 RemoteInput(예: 답장 작업)이 있으면 setChoices()로 설정한 선택 사항이 첫 번째 작업 아래의 확장된 알림 내에 나타납니다.

사용자는 다음 중 하나가 참일 경우 알림을 탭하여 확장된 알림을 볼 수 있습니다.

  • 알림이 페어링된 스마트폰에서 앱에 의해 생성되어 Wear에 연결됩니다.
  • 알림에 contentIntent가 없습니다.

참고: setColor() 메서드로 알림에 관해 설정한 앱 관련 배경색은 알림을 확장할 경우에만 표시됩니다.

확장된 알림의 권장사항

확장된 알림을 사용할 시기를 결정하려면 다음 지침을 따르세요.

  • 페어링된 스마트폰에서 Wear 기기에 연결된 모든 알림은 확장된 알림을 사용합니다.
  • 알림이 Wear에서 로컬로 실행되는 앱에 의해 생성된 경우, setContentIntent()를 호출하여 알림의 터치 타겟이 앱 내에서 알림 ID가 있는 알림 객체를 시작하도록 해야 합니다. Wear에서 로컬로 실행되는 앱에 의해 생성된 알림에 관해서는 확장된 알림을 사용하지 않는 것이 좋습니다.

확장된 알림 추가

확장된 알림을 사용하면 알림에 관해 추가 콘텐츠 및 작업을 포함할 수 있습니다. 앱의 알림에서 제공할 세부정보의 수준을 선택할 수 있습니다. 그러나 알림에 포함할 세부정보의 양을 신중하게 조정해야 합니다.

추가 콘텐츠 포함

확장된 알림에 추가 텍스트를 표시하려면 BigTextStyle을 사용합니다.

확장된 알림에 이미지를 추가하려면 BigPictureStyle을 사용하면 됩니다. 확장된 알림에 이미지를 두 개 이상 추가하려면 BigPictureStyle과 함께 addPage() 메서드를 사용합니다.

기본 작업

확장된 알림에는 하나의 기본 작업이 포함됩니다. setContentAction()을 사용하여 다른 작업을 지정하지 않는 한 이것이 알림의 첫 번째 작업입니다.

추가 작업

추가 작업을 지정하려면 addAction() 또는 addActions()를 사용합니다. 확장된 알림의 작업 창에는 사용 가능한 모든 작업이 포함됩니다.

알림 작업 추가

setContentIntent()로 정의한 기본 콘텐츠 작업 외에도 PendingIntentaddAction() 메서드에 전달하여 다른 작업을 추가할 수 있습니다.

예를 들어 다음 코드는 위와 동일한 알림 유형을 표시하지만, 지도에 이벤트 위치를 표시하는 작업을 추가합니다.

Kotlin

    // Build an intent for an action to view a map
    val mapIntent = Intent(Intent.ACTION_VIEW)
    // The channel ID of the notification.
    val id = "my_channel_01"
    val mapPendingIntent = Intent(Intent.ACTION_VIEW).let { mapIntent ->
        mapIntent.data = Uri.parse("geo:0,0?q=" + Uri.encode(location))
        PendingIntent.getActivity(this, 0, mapIntent, 0)
    }

    val notificationBuilder = NotificationCompat.Builder(this, id)
            .setSmallIcon(R.drawable.ic_event)
            .setContentTitle(eventTitle)
            .setContentText(eventLocation)
            .setContentIntent(viewPendingIntent)
            .addAction(R.drawable.ic_map, getString(R.string.map), mapPendingIntent)
    

자바

    // Build an intent for an action to view a map
    Intent mapIntent = new Intent(Intent.ACTION_VIEW);
    // The channel ID of the notification.
    String id = "my_channel_01";
    Uri geoUri = Uri.parse("geo:0,0?q=" + Uri.encode(location));
    mapIntent.setData(geoUri);
    PendingIntent mapPendingIntent =
            PendingIntent.getActivity(this, 0, mapIntent, 0);

    NotificationCompat.Builder notificationBuilder =
            new NotificationCompat.Builder(this, id)
            .setSmallIcon(R.drawable.ic_event)
            .setContentTitle(eventTitle)
            .setContentText(eventLocation)
            .setContentIntent(viewPendingIntent)
            .addAction(R.drawable.ic_map,
                    getString(R.string.map), mapPendingIntent);
    

스마트폰에서는 작업이 알림에 첨부된 추가 버튼으로 나타납니다. 웨어러블 기기에서는 작업이 콘텐츠 텍스트 뒤의 확장된 알림에 나타납니다. 사용자가 작업을 탭하면 관련된 인텐트가 스마트폰에서 호출됩니다.

팁: 알림에 '답장' 작업이 포함된 경우(예: 메시징 앱의 경우) 웨어러블에서 직접 음성 입력 답장을 사용하도록 설정하여 동작을 개선할 수 있습니다. 자세한 내용은 알림 작업으로서 음성 입력 추가를 참조하세요.

인라인 작업 추가

인라인 작업을 사용하면 사용자는 알림 스트림 카드 내에서 알림에 관한 작업을 수행할 수 있습니다. Wear에서 인라인 작업은 알림의 하단에 표시되는 추가 버튼으로 나타납니다.

인라인 작업은 선택사항이지만, 사용자가 알림 스트림 카드에서 콘텐츠를 본 이후(확장된 알림으로 이동하지 않고) 알림으로 작업할 가능성이 있는 경우에는 인라인 작업을 사용하는 것이 좋습니다. 알림에서 인라인 작업을 사용하는 좋은 사용 사례로는 문자 메시지에 답장, 피트니스 활동 중지, 이메일 메시지 보관 등이 있습니다.

하나의 알림은 하나의 인라인 작업만 제공할 수 있습니다. 인라인 작업을 알림에서 추가 버튼으로 표시하려면 setHintDisplayActionInline() 메서드를 참으로 설정합니다. 사용자가 인라인 작업을 탭하면 시스템은 알림 작업에 지정된 인텐트를 호출합니다.

다음 코드 스니펫은 작업을 인라인으로 표시하기 위한 힌트를 추가하며, addAction 메서드를 사용하여 알림에 인라인 작업을 추가합니다.

Kotlin

    //Wear OS requires a hint to display the reply action inline.
    val actionExtender = NotificationCompat.Action.WearableExtender()
            .setHintLaunchesActivity(true)
            .setHintDisplayActionInline(true)
    wearableExtender.addAction(actionBuilder.extend(actionExtender).build())
    

자바

    //Wear OS requires a hint to display the reply action inline.
    Action.WearableExtender actionExtender =
        new Action.WearableExtender()
            .setHintLaunchesActivity(true)
            .setHintDisplayActionInline(true);
    wearableExtender.addAction(actionBuilder.extend(actionExtender).build());
    

알림에 웨어러블 관련 기능 추가

알림에 웨어러블 관련 기능(예: 웨어러블 알림에서 앱 아이콘 숨기기 또는 사용자가 음성 입력으로 텍스트 응답을 지정하도록 허용)을 추가해야 하는 경우 NotificationCompat.WearableExtender 클래스를 사용하여 옵션을 지정할 수 있습니다. 이 API를 사용하려면 다음 단계를 따르세요.

  1. WearableExtender의 인스턴스를 만들고 알림에 관해 웨어러블 관련 옵션을 설정합니다.
  2. NotificationCompat.Builder의 인스턴스를 만들고, 앞에서 설명한 것처럼 원하는 알림 속성을 설정합니다.
  3. 알림에서 extend()를 호출하고 WearableExtender로 전달합니다. 이렇게 하면 웨어러블 옵션이 알림에 적용됩니다.
  4. build()를 호출하여 알림을 만듭니다.

참고: 프레임워크의 NotificationManager를 사용하는 경우 NotificationCompat.WearableExtender의 일부 기능이 작동하지 않으므로 NotificationCompat를 사용해야 합니다.

사용자의 기기 전반에서 알림의 해지(취소)를 동기화할 수 있습니다. 해지를 동기화하려면 setDismissalId() 메서드를 사용합니다. setDismissalId() 메서드를 호출할 때 각 알림에 전역적으로 고유 ID를 문자열로서 전달합니다 알림이 해지되면, 시계 및 동반 스마트폰에서 동일한 해지 ID를 가지고 있는 다른 모든 알림도 해지됩니다. 해지 ID를 검색하려면 getDismissalId()를 사용합니다.

참고: setBackground() 메서드는 Wear 2.0에서 지원되지 않습니다. 이미지가 포함된 알림에는 NotificationCompat.BigPictureStyle을 사용할 수 있습니다.

setHintHideIcon() 메서드도 Wear 2.0에서 지원되지 않습니다.

웨어러블 전용 작업 지정

웨어러블에서 수행할 수 있는 작업과 스마트폰에서 수행할 수 있는 작업을 다르게 설정하려면 WearableExtender.addAction()을 사용합니다. 이 메서드로 작업을 추가하면 웨어러블은 NotificationCompat.Builder.addAction()으로 추가된 다른 작업을 표시하지 않습니다. WearableExtender.addAction()으로 추가된 작업은 웨어러블에만 표시되고 스마트폰에는 표시되지 않습니다.

알림 작업으로 음성 입력 추가

음성 작업은 웨어러블 환경의 중요한 부분입니다. 음성 입력을 지원하는 작업을 만들려면 알림 작업에 추가할 수 있는 RemoteInput.Builder의 인스턴스를 만듭니다. 이 클래스의 생성자는 시스템이 음성 입력의 키로 사용하는 문자열을 수락하는데, 나중에 스마트폰 앱에서 입력 텍스트를 검색하는 데 이 문자열이 사용됩니다.

예를 들어 음성 입력 프롬프트의 맞춤 라벨을 제공하는 RemoteInput 객체를 만드는 방법은 다음과 같습니다.

Kotlin

    // Key for the string that's delivered in the action's intent
    const val EXTRA_VOICE_REPLY = "extra_voice_reply"
    ...
    val remoteInput = resources.getString(R.string.reply_label).let { replyLabel ->
        RemoteInput.Builder(EXTRA_VOICE_REPLY)
                .setLabel(replyLabel)
                .build()
    }
    

자바

    // Key for the string that's delivered in the action's intent
    private static final String EXTRA_VOICE_REPLY = "extra_voice_reply";

    String replyLabel = getResources().getString(R.string.reply_label);

    RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
            .setLabel(replyLabel)
            .build();
    

사전 정의된 텍스트 응답 추가

음성 입력을 허용하는 것 외에도 사용자가 빠른 응답을 위해 선택할 수 있는 최대 5개의 텍스트 응답을 제공할 수 있습니다. setChoices()를 호출하고 문자열 배열을 전달합니다.

예를 들어, 리소스 배열에 몇 가지 응답을 정의할 수 있습니다.

res/values/strings.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string-array name="reply_choices">
            <item>Yes</item>
            <item>No</item>
            <item>Maybe</item>
        </string-array>
    </resources>
    

그런 다음 문자열 배열을 부풀려 RemoteInput에 추가합니다.

Kotlin

    // Key for the string that's delivered in the action's intent
    const val EXTRA_VOICE_REPLY = "extra_voice_reply"
    ...
    val remoteInput = resources.getString(R.string.reply_label).let { replyLabel ->
        resources.getStringArray(R.array.reply_choices).let { replyChoices ->
            RemoteInput.Builder(EXTRA_VOICE_REPLY)
                    .setLabel(replyLabel)
                    .setChoices(replyChoices)
                    .build()
        }
    }
    

자바

    public static final String EXTRA_VOICE_REPLY = "extra_voice_reply";
    ...
    String replyLabel = getResources().getString(R.string.reply_label);
    String[] replyChoices = getResources().getStringArray(R.array.reply_choices);

    RemoteInput remoteInput = new RemoteInput.Builder(EXTRA_VOICE_REPLY)
            .setLabel(replyLabel)
            .setChoices(replyChoices)
            .build();
    

음성 입력을 문자열로 받기

답장 작업의 인텐트에서 선언한 활동에서 텍스트로 변환된 사용자의 메시지를 받으려면 getResultsFromIntent()를 호출하고 '답장' 작업의 인텐트를 전달합니다. 이 메서드는 텍스트 응답이 포함된 Bundle을 반환합니다. 그러면 응답을 가져오기 위해 Bundle을 쿼리할 수 있습니다.

참고: 음성 입력은 ClipData로 저장되므로 Intent.getExtras()를 사용하여 음성 결과를 가져오지 마세요. getResultsFromIntent() 메서드를 사용하면 ClipData를 직접 처리할 필요 없이 문자 시퀀스를 편리하게 수신할 수 있습니다.

다음 코드는 인텐트를 수락하고 음성 응답을 반환하는 메서드를 보여줍니다. 이 메서드는 이전 예제에 사용된 EXTRA_VOICE_REPLY 키에 의해 참조됩니다.

Kotlin

    /**
     * Obtain the intent that started this activity by calling
     * Activity.getIntent() and pass it into this method to
     * get the associated voice input string.
     */

    private fun getMessageText(intent: Intent): CharSequence? =
            RemoteInput.getResultsFromIntent(intent)?.run {
                getCharSequence(EXTRA_VOICE_REPLY)
            }
    

자바

    /**
     * Obtain the intent that started this activity by calling
     * Activity.getIntent() and pass it into this method to
     * get the associated voice input string.
     */

    private CharSequence getMessageText(Intent intent) {
        Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
        if (remoteInput != null) {
            return remoteInput.getCharSequence(EXTRA_VOICE_REPLY);
        }
        return null;
    }
    

Kotlin

    // Create an intent for the reply action
    val actionPendingIntent = Intent(this, ActionActivity::class.java).let { actionIntent ->
        PendingIntent.getActivity(this, 0, actionIntent,
                PendingIntent.FLAG_UPDATE_CURRENT)
    }
    // Create the action
    val action = NotificationCompat.Action.Builder(
            R.drawable.ic_action,
            getString(R.string.label),
            actionPendingIntent
    ).build()

    // Build the notification and add the action via WearableExtender
    var notification = NotificationCompat.Builder(context)
            .setSmallIcon(R.drawable.ic_message)
            .setContentTitle(getString(R.string.title))
            .setContentText(getString(R.string.content))
            .extend(NotificationCompat.WearableExtender().addAction(action))
            .build()
    

자바

    // Create an intent for the reply action
    Intent actionIntent = new Intent(this, ActionActivity.class);
    PendingIntent actionPendingIntent =
            PendingIntent.getActivity(this, 0, actionIntent,
                    PendingIntent.FLAG_UPDATE_CURRENT);

    // Create the action
    NotificationCompat.Action action =
            new NotificationCompat.Action.Builder(R.drawable.ic_action,
                    getString(R.string.label), actionPendingIntent)
                    .build();

    // Build the notification and add the action via WearableExtender
    Notification notification =
            new NotificationCompat.Builder(context)
                    .setSmallIcon(R.drawable.ic_message)
                    .setContentTitle(getString(R.string.title))
                    .setContentText(getString(R.string.content))
                    .extend(new WearableExtender().addAction(action))
                    .build();
    

독립 실행형 웨어러블 앱에서 알림 시작

독립 실행형 시계 앱에서 알림을 만드는 것은 연결된 알림을 만드는 것과 다르지 않습니다. 독립 실행형 Wear 앱에서 오는 알림은 연결된 알림과 모양은 유사하지만 동작은 약간 다릅니다. contentIntent가 설정되지 않았거나 페어링된 스마트폰에서 알림이 연결되는 경우, 알림을 탭하면 확장된 알림이 열립니다. 반면 알림이 독립 실행형 시계 앱에서 오는 경우 알림을 탭하면 contentIntent가 실행되어 Wear 앱이 열립니다. 독립 실행형 앱에서 알림을 만들고 확장된 알림 동작을 모방하는 방법을 알아보려면 Wear 알림 샘플을 참조하세요.

기본적으로 알림은 동반 스마트폰의 앱에서 페어링된 시계로 연결됩니다. 독립 실행형 시계 앱을 빌드하고 동반 스마트폰 앱을 가지고 있는 경우, 이 두 앱이 중복 알림을 만들 수 있습니다. 중복 알림 문제를 처리하는 방법에 관한 자세한 내용은 알림을 위한 연결 모드를 참조하세요.