봄날은 갔다. 이제 그 정신으로 공부하자

Slice? 어디에 사용하는거지? 본문

Android jetpack

Slice? 어디에 사용하는거지?

길재의 그 정신으로 공부하자 2021. 2. 8. 11:11

Slice는 Google 검색 앱 내 및 Google 어시스턴트와 같은 다른 요소에 서식 있는 동적 양방향 콘텐츠를 표시할 수 있는 UI 템플릿 입니다.

Slice를 사용하면 사용자가 전체 화면 앱 환경 외부에서 참여하여 작업을 더 빠르게 실행할 수 있습니다.

Slice 지원 기능은 Android Jetpack에 내장되면 Android 4.4 버전까지 지원하므로 거의 모든 android 사용자(95%)가 Slice 기능을 사용할 수 있습니다. Slice는 라이브 데이터, 스크롤 콘텐츠, 인라인 작업, 앱의 딥 링크를 지원합니다. Slice에는 전환 및 슬라이더와 같은 상호작용 관리도 포함할 수 있습니다.

 이게 무슨 소리냐면 아래와 같이 사용 가능하다는 건데... 이글을 쓰고 있는 지금도 이건 어디에 사용해야할지 모르겠네요.

 

adb 경로 및 및 기기 연결 확인

Slice 뷰어 다운로드 전에 우선 내 컴퓨터의 adb 설치 경로 확인 및 device연결 상태를 확인해줍니다.

adb는 Android studio가 설치된 경우, 아래 경로에 있습니다.

/Users/<USER_NAME>/Library/Android/sdk/platform-tools/

 

위 경로로 이동 후 아래 명령어를 입력하여 내 컴퓨터에 장치가 제대로 연결되어 있는지 확인합니다.

./adb devices

연결된 기기가 있는 경우 아래와 같은 내용을 출력합니다.

List of devices attached
R3CM80S0PEA     device

 

Slice 뷰어 다운로드 및 설치

아래 링크로 이동해서 slice 뷰어를 다운로드 합니다.

https://github.com/android/user-interface-samples/releases

 

다운로드 받은 slice-viewer.apk를 적당한 위치에 옮겨 놓은 후 아래 adb 명령어를 입력하여 slice 뷰어를 연결된 기기에 설치 합니다.

./adb install -r -t ./../../../../slice-viewer.apk

 

그럼 기기에 아래와 같은 icon 생성됩니다.

 

이렇게 설치한 Slice 뷰어를 통해 앱에서 개발하는 Slice 확인 있습니다.

 

이런 방식으로 apk 설치하는게 어려운 분들은 그냥 메일이나 카톡 첨부파일로 보내서 다운받아 설치하면 됩니다.

 

Slice 추가

앱에 Slice Provider 추가

  - 프로젝트에서 Run > Edit Configurations…를 선택합니다.

  - 왼쪽 상단에서 녹색 더하기 버튼을 클릭합니다.

  - Android 앱을 선택합니다.

  - Name 입력란에 Slice를 입력합니다.

  - Module 드롭다운에서 앱 모듈을 선택합니다.

  - Launch options 아래 Launch 드롭다운에서 URL을 선택합니다.

  - URL 입력란에 slice-<YOUR SLICE URL>을 입력합니다.

     slice-content://com.example.your/path

  - 마지막으로 OK 입력합니다.

이제 앱을 빌드해서 기기에 설치한 방금 설치한 slice viewer 확인해보면 아래 그림과 같이 입력한 URL 보여지는 것을 확인할 있습니다.

 

첫번째 Slice 만들기

Slice 빌드하려면 Android 스튜디오 프로젝트를 열고 마우스 오른쪽 버튼으로 src 패키지를 클릭하고 New … > Other > Slice Provider 선택합니다.

이렇게하면 SliceProvider를 확장하고 필요한 제공자 항목을 AndroidManifest.xml에 추가하며 build.gradle을 수정하여 필요한 Slice 종속 항목을 추가하는 클래스가 생성됩니다.

AndroidManifest.xml의 수정 사항은 아래와 같습니다. (아래 내용은 자동으로 추가되는 내용으로 직접 추가할 필요는 없습니다.)

    package="com.example.android.app">
    ...
    <application>
        ...
        <provider android:name="MySliceProvider"
            android:authorities="com.example.android.app"
            android:exported="true" >
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.app.slice.category.SLICE" />
            </intent-filter>
        </provider>
        ...
    </application>

</manifest>

아래 종속항목을 build.gradle에 추가합니다.

dependencies { 
    ...
    implementation "androidx.slice:slice-builders-ktx:$slice_version" // 1.0.0-alpha07
    ...
}

 

각 Slice에는 연결된 URI가 있습니다. 화면에서 Slice를 표시하려고 할 때 이 URI를 사용하여 앱에 결합 요청을 보냅니다.

그런 다음 앱에서 이 요청을 처리하고 onBindSlice 메서드를 통해 동적으로 Slice를 빌드합니다.

그러면 표면에서 적절할 때 Slice를 표시할 수 있습니다.

아래는 “/hello” URI 경로를 확인하고 Hello World Slice를 바환하는 onBindSlice 메서드의 예입니다.

override fun onBindSlice(sliceUri: Uri): Slice? {
    // Note: you should switch your build.gradle dependency to
    // slice-builders-ktx for a nicer interface in Kotlin.
    val context = context ?: return null
    val activityAction = createActivityAction() ?: return null
    return if (sliceUri.path == "/path") {
        // Path recognized. Customize the Slice using the androidx.slice.builders API.
        // Note: ANR and StrictMode are enforced here so don"t do any heavy operations. 
        // Only bind data that is currently available in memory.
        ListBuilder(context, sliceUri, ListBuilder.INFINITY)
            .addRow(
                ListBuilder.RowBuilder()
                    .setTitle("HELLO WORLD")
                    .setPrimaryAction(activityAction)
            )
            .build()
    } else {
        // Error: Path not found.
        ListBuilder(context, sliceUri, ListBuilder.INFINITY)
            .addRow(
                ListBuilder.RowBuilder()
                    .setTitle("URI not found.")
                    .setPrimaryAction(activityAction)
            )
            .build()
    }
}

 

추가로 주석으로 처리된 createActivityAction() 함수도 아래와 같이 수정합니다.

private fun createActivityAction(): SliceAction? {
    return SliceAction.create(
        PendingIntent.getActivity(
            context, 0, Intent(context, MainActivity::class.java), 0
        ),
        IconCompat.createWithResource(context, R.drawable.ic_launcher_foreground),
        ListBuilder.ICON_IMAGE,
        "Open App"
    )
}

 

Slice 뷰어를 실행 “com.kiljae.mysample/path” 입력하면 아래 그림과 같이 생성한 Slice 보여지는 것을 확인할 있습니다.

위 그림의 Slice 뷰어에 보여지는 HELLO WORLD를 클릭하면 MySample앱이 실행됩니다.

 

두 번째 Slice 만들기

이번에는 앱 실행이 아니라 Toast 보여주도록 만들어보도록 하겠습니다.

onBindSlice() 함수에 아래 코드를 추가합니다.

override fun onBindSlice(sliceUri: Uri): Slice? {
    …
    return if (sliceUri.path == "/path") {
        …
    } else if (sliceUri.path == "/toast") {
        ListBuilder(context, sliceUri, ListBuilder.INFINITY)
            .addRow(
                ListBuilder.RowBuilder()
                    .setTitle("TOAST POPUP")
                    .setSubtitle("click here~~~")
                    .setPrimaryAction(createToastAction())
            )
            .build()
    } else {
        …
    }
}

private fun createToastAction(): SliceAction {
    return SliceAction.create(
        PendingIntent.getBroadcast(
            context, 0, Intent(context, MyBroadcastReceiver::class.java), 0
        ),
        IconCompat.createWithResource(context, R.drawable.ic_launcher_foreground),
        ListBuilder.ICON_IMAGE,
        "Open App"
    )
}

 

이제 Intent를 수신할 MyBroadcastReceiver class를 만들어 줍니다.

class MyBroadcastReceiver : BroadcastReceiver(){
    override fun onReceive(context: Context?, intent: Intent?) {
        intent?.let {
            Toast.makeText(
                context,
                "Toggled: " + it.getBooleanExtra(Slice.EXTRA_SELECTION, false),
                Toast.LENGTH_SHORT)
                .show();
        }
    }
}

 

마지막으로 manifest.xml에 MyBroadcastReceiver를 추가합니다.

<receiver android:name=".MyBroadcastReceiver"
    android:authorities="com.kiljae.mysample"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />

        <category android:name="android.app.slice.category.SLICE" />

        <data
            android:host="mysample.kiljae.com"
            android:pathPrefix="/"
            android:scheme="http" />
    </intent-filter>
</receiver>

 

Slice 뷰어를 실행 해 “com.kiljae.mysample/toast”를 입력하면 아래 그림과 같이 생성한 Slice가 보여지고

해당 slice를 선택하면 Toast가 출력되는 것을 확인할 수 있습니다.

 

세 번째 Slice 만들기

이번에는 Intent를 통해 앱으로 상태를 전송하는 Slice를 만들어 보겠습니다.

간단하게 Toggle되는 스위치를 추가하여 상태가 변경되었을 때 받는 부분만 만들어보겠습니다.

MySliceProvider class의 onBindSlice()함수에 아래 코드를 추가합니다.

}else if (sliceUri.path == "/toggle") {
     val intent = Intent(context, MyBroadcastReceiver::class.java).putExtra(EXTRA_TOGGLE_STATE, true)
     ListBuilder(context, sliceUri, ListBuilder.INFINITY)
           .addRow(
                   ListBuilder.RowBuilder()
                   .setTitle("TOGGLE")
                   .setSubtitle("toggle action")
                   .setPrimaryAction(createToggleAction())
           ).build()
}

 

추가로 아래 함수를 추가해 toggle SliceAction 을 생성해줍니다.

private fun createToggleAction(): SliceAction {
    return SliceAction.createToggle(
            PendingIntent.getBroadcast(
                    context, 0, Intent(context, MyBroadcastReceiver::class.java), 0
            ),
            "Toggle adaptive",
            true)
}

 

마지막으로 MyBroadcastReceiver에 Toggle 이벤트를 받을 수 있도록 아래와 같이 추가해 줍니다.

override fun onReceive(context: Context?, intent: Intent?) {
    intent?.let {
        if(it.hasExtra(EXTRA_TOGGLE_STATE)){
            Toast.makeText(
                    context,
                    "Toggled: " + it.getBooleanExtra(EXTRA_TOGGLE_STATE, false),
                    Toast.LENGTH_SHORT)
                    .show();
        }
        …
}

 

그럼 아래 그림과 같이 추가된 Slice가 보여지고 Toggle 시 Toast 팝업으로 변경된 상태가 표시됩니다.

 

추가 Slice 만들기

아래 코드를 추가하는 경우, Toggle과 0 ~ 100까지 값을 입력 받을 수 있는 슬라이더가 보여지는 slice를 만들 수 있습니다.

ListBuilder(context, sliceUri, ListBuilder.INFINITY)

        .addRow(

                ListBuilder.RowBuilder()

                        .setTitle("TOGGLE")

                        .setSubtitle("toggle action")

                        .setPrimaryAction(createToggleAction())

        )

        .addInputRange(ListBuilder.InputRangeBuilder().setInputAction(

                PendingIntent.getBroadcast(context, 0, intent, 0)

                ).setMax(100)

                .setValue(45)

        ).build()

 

 

정리

언급한 외에도 아래 링크에 다양한 slice 템플릿들이 있습니다.

https://developer.android.com/guide/slices/templates?hl=ko

많은 것을 설명하고 싶은데 제가 slice 활용성에 대해 의구심을 가지고 있어서 더이상 설명하기가 어렵네요.

slice 활용성에 대한 확신이 생기면 그때 추가 글을 작성하도록 하겠습니다.

 

Comments