시작하기

이 페이지는 앱에서 환경을 설정하고 슬라이스를 만드는 방법을 보여줍니다.

참고: Android 스튜디오 3.2 이상에는 슬라이스 개발에 도움이 되는 추가 도구와 기능이 포함되어 있습니다.

  • AndroidX 리팩터링 도구: AndroidX 라이브러리를 사용하는 프로젝트에서 작업하는 경우 필요합니다.
  • 슬라이스 린트 검사: 슬라이스를 만들 때 규정에 어긋나는 일반적인 오류를 포착합니다.
  • SliceProvider 템플릿: 를 만들 때 상용구를 처리합니다.

슬라이스 뷰어 다운로드 및 설치

SliceView API를 구현하지 않고 슬라이스를 테스트하는 데 사용할 수 있는 최신 샘플 슬라이스 뷰어 APK 버전을 다운로드하세요. 슬라이스 뷰어 소스를 복제할 수도 있습니다.

개발자 환경에서 ADB가 제대로 설정되지 않은 경우 자세한 내용은 ADB 가이드를 참조하세요.

다운로드된 slice-viewer.apk와 동일한 디렉터리에서 다음 명령어를 실행하여 기기에 슬라이스 뷰어를 설치하세요.

adb install -r -t slice-viewer.apk
    

슬라이스 뷰어 실행

슬라이스 뷰어는 Android 스튜디오 프로젝트 또는 명령줄에서 실행할 수 있습니다.

Android 스튜디오 프로젝트에서 슬라이스 뷰어 실행

  1. 프로젝트에서 Run > Edit Configurations...를 선택합니다.
  2. 왼쪽 상단에서 녹색 더하기 기호를 클릭합니다.
  3. Android 앱 선택

  4. 이름 입력란에 슬라이스를 입력합니다.

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

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

  7. URL 입력란에 slice-<your slice URI>를 입력합니다.

    예: slice-content://com.example.your.sliceuri

  8. OK를 클릭합니다.

ADB(명령줄)를 통해 슬라이스 뷰어 도구 실행

Android 스튜디오에서 앱을 실행합니다.

adb install -t -r <yourapp>.apk
    

다음 명령어를 실행하여 슬라이스를 확인합니다.

adb shell am start -a android.intent.action.VIEW -d slice-<your slice URI>
    

단일 WiFi 슬라이스를 보여주는 슬라이스 뷰어

한 곳에서 모든 슬라이스 보기

단일 슬라이스를 실행하는 것 외에 슬라이스의 영구 목록을 볼 수 있습니다.

  • 검색창을 사용하여 URI를 통해 슬라이스를 수동으로 검색합니다(예: content://com.example.android.app/hello). 검색할 때마다 슬라이스가 목록에 추가됩니다.
  • 슬라이스 URI를 사용하여 슬라이스 뷰어 도구를 시작할 때마다 슬라이스가 목록에 추가됩니다.
  • 슬라이스를 스와이프하여 목록에서 삭제할 수 있습니다.
  • 슬라이스의 URI를 탭하면 슬라이스만 포함된 페이지가 표시됩니다. 이는 슬라이스 URI를 사용하여 슬라이스 뷰어를 실행하는 것과 효과가 동일합니다.

슬라이스 목록을 보여주는 슬라이스 뷰어

다른 모드로 슬라이스 보기

슬라이스를 표시하는 앱은 런타임에 SliceView#mode를 수정할 수 있으므로 슬라이스가 각 모드에서 예상대로 표시되는지 확인해야 합니다. 모드를 변경하려면 페이지의 오른쪽 상단에서 메뉴 아이콘을 선택하세요.

모드가 '작게'로 설정된 단일 슬라이스 뷰어

첫 번째 슬라이스 만들기

슬라이스를 만들려면 Android Studio 프로젝트를 열고 마우스 오른쪽 버튼으로 src 패키지를 마우스 오른쪽 버튼으로 클릭하고 New... > Other > Slice Provider를 선택하세요. 이렇게 하면 SliceProvider를 확장하고 필요한 제공자 항목을 AndroidManifest.xml에 추가하고 필요한 슬라이스 종속성을 추가하기 위해 build.gradle을 수정하는 클래스가 생성됩니다.

AndroidManifest.xml의 수정 사항은 다음과 같습니다.

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        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에 추가됩니다.

Kotlin

    dependencies {
    // ...
        implementation "androidx.slice:slice-builders-ktx:(latest version)"
    // ...
    }
    

Java

    dependencies {
    // ...
        implementation "androidx.slice:slice-builders:(latest version)"
    // ...
    }
    

각 슬라이스에는 연결된 URI가 있습니다. 표면에서는 슬라이스를 표시하려고 할 경우 이 URI를 사용하여 앱에 바인딩 요청을 보냅니다. 그런 다음 앱에서 이 요청을 처리하고 onBindSlice 메서드를 통해 동적으로 슬라이스를 만듭니다. 그런 다음 표면에서 적절할 때 슬라이스를 표시할 수 있습니다.

다음은 /hello URI 경로를 확인하고 Hello World 슬라이스를 반환하는 onBindSlice 메서드의 예입니다.

Kotlin

    override fun onBindSlice(sliceUri: Uri): Slice? {
        val activityAction = createActivityAction()
        return if (sliceUri.path == "/hello") {
            list(context, sliceUri, ListBuilder.INFINITY) {
                row {
                    primaryAction = activityAction
                    title = "Hello World."
                }
            }
        } else {
            list(context, sliceUri, ListBuilder.INFINITY) {
                row {
                    primaryAction = activityAction
                    title = "URI not recognized."
                }
            }
        }
    }
    

Java

    @Override
    public Slice onBindSlice(Uri sliceUri) {
        if (getContext() == null) {
            return null;
        }
        SliceAction activityAction = createActivityAction();
        ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY);
        // Create parent ListBuilder.
        if ("/hello".equals(sliceUri.getPath())) {
            listBuilder.addRow(new ListBuilder.RowBuilder()
                    .setTitle("Hello World")
                    .setPrimaryAction(activityAction)
            );
        } else {
            listBuilder.addRow(new ListBuilder.RowBuilder()
                    .setTitle("URI not recognized")
                    .setPrimaryAction(activityAction)
            );
        }
        return listBuilder.build();
    }
    

위의 슬라이스 뷰어 섹션에서 만든 슬라이스 실행 구성을 사용하여 Hello World 슬라이스의 슬라이스 URI(예: slice-content://com.android.example.slicesample/hello)를 전달하고 슬라이스 뷰어에서 확인하세요.

양방향 슬라이스

알림과 마찬가지로 사용자 상호작용에서 트리거되는 PendingIntent 개체를 첨부하여 슬라이스 내에서 클릭을 처리할 수 있습니다. 아래의 예에서는 이러한 인텐트를 수신하고 처리할 수 있는 Activity를 시작합니다.

Kotlin

    fun createSlice(sliceUri: Uri): Slice {
        val activityAction = createActivityAction()
        return list(context, sliceUri, INFINITY) {
            row {
                title = "Perform action in app"
                primaryAction = activityAction
            }
        }
    }

    fun createActivityAction(): SliceAction {
        val intent = Intent(context, MainActivity::class.java)
        return SliceAction.create(
            PendingIntent.getActivity(context, 0, Intent(context, MainActivity::class.java), 0),
            IconCompat.createWithResource(context, R.drawable.ic_home),
            ListBuilder.ICON_IMAGE,
            "Enter app"
        )
    }
    

Java

    public Slice createSlice(Uri sliceUri) {
        if (getContext() == null) {
            return null;
        }
        SliceAction activityAction = createActivityAction();
        return new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY)
                .addRow(new ListBuilder.RowBuilder()
                        .setTitle("Perform action in app.")
                        .setPrimaryAction(activityAction)
                ).build();
    }

    public SliceAction createActivityAction() {
        if (getContext() == null) {
            return null;
        }
        return SliceAction.create(
                PendingIntent.getActivity(
                        getContext(),
                        0,
                        new Intent(getContext(), MainActivity.class),
                        0
                ),
                IconCompat.createWithResource(getContext(), R.drawable.ic_home),
                ListBuilder.ICON_IMAGE,
                "Enter app"
        );
    }
    

슬라이스는 또한 전환과 같이 앱으로 전송되는 인텐트에 상태가 포함된 다른 입력 유형도 지원합니다.

Kotlin

    fun createBrightnessSlice(sliceUri: Uri): Slice {
        val toggleAction =
            SliceAction.createToggle(
                createToggleIntent(),
                "Toggle adaptive brightness",
                true
            )
        return list(context, sliceUri, ListBuilder.INFINITY) {
            row {
                title = "Adaptive brightness"
                subtitle = "Optimizes brightness for available light"
                primaryAction = toggleAction
            }
            inputRange {
                inputAction = (brightnessPendingIntent)
                max = 100
                value = 45
            }
        }
    }

    fun createToggleIntent(): PendingIntent {
        val intent = Intent(context, MyBroadcastReceiver::class.java)
        return PendingIntent.getBroadcast(context, 0, intent, 0)
    }
    

Java

    public Slice createBrightnessSlice(Uri sliceUri) {
        if (getContext() == null) {
            return null;
        }
        SliceAction toggleAction = SliceAction.createToggle(
                createToggleIntent(),
                "Toggle adaptive brightness",
                true
        );
        ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY)
                .addRow(new ListBuilder.RowBuilder()
                        .setTitle("Adaptive brightness")
                        .setSubtitle("Optimizes brightness for available light.")
                        .setPrimaryAction(toggleAction)
                ).addInputRange(new ListBuilder.InputRangeBuilder()
                        .setInputAction(brightnessPendingIntent)
                        .setMax(100)
                        .setValue(45)
                );
        return listBuilder.build();
    }

    public PendingIntent createToggleIntent() {
        Intent intent = new Intent(getContext(), MyBroadcastReceiver.class);
        return PendingIntent.getBroadcast(getContext(), 0, intent, 0);
    }
    

그런 다음 수신기에서 수신된 상태를 확인할 수 있습니다.

Kotlin

    class MyBroadcastReceiver : BroadcastReceiver() {

        override fun onReceive(context: Context, intent: Intent) {
            if (intent.hasExtra(Slice.EXTRA_TOGGLE_STATE)) {
                Toast.makeText(context, "Toggled:  " + intent.getBooleanExtra(
                        Slice.EXTRA_TOGGLE_STATE, false),
                        Toast.LENGTH_LONG).show()
            }
        }

        companion object {
            const val EXTRA_MESSAGE = "message"
        }
    }
    

Java

    public class MyBroadcastReceiver extends BroadcastReceiver {

        public static String EXTRA_MESSAGE = "message";

        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.hasExtra(EXTRA_TOGGLE_STATE)) {
                Toast.makeText(context, "Toggled:  " + intent.getBooleanExtra(
                        EXTRA_TOGGLE_STATE, false),
                        Toast.LENGTH_LONG).show();
            }
        }
    }
    

동적 슬라이스

슬라이스에 동적 콘텐츠를 포함할 수도 있습니다. 다음 예에서는 이제 콘텐츠에 수신된 브로드캐트 수가 슬라이스에 포함됩니다.

Kotlin

    fun createDynamicSlice(sliceUri: Uri): Slice {
        return when (sliceUri.path) {
            "/count" -> {
                val toastAndIncrementAction = SliceAction.create(
                    createToastAndIncrementIntent("Item clicked."),
                    actionIcon,
                    ListBuilder.ICON_IMAGE,
                    "Increment."
                )
                list(context, sliceUri, ListBuilder.INFINITY) {
                    row {
                        primaryAction = toastAndIncrementAction
                        title = "Count: ${MyBroadcastReceiver.receivedCount}"
                        subtitle = "Click me"
                    }
                }
            }

            else -> {
                list(context, sliceUri, ListBuilder.INFINITY) {
                    row {
                        primaryAction = createActivityAction()
                        title = "URI not found."
                    }
                }
            }
        }
    }

    fun createToastAndIncrementIntent(s: String): PendingIntent {
        return PendingIntent.getBroadcast(
            context, 0,
            Intent(context, MyBroadcastReceiver::class.java)
                .putExtra(MyBroadcastReceiver.EXTRA_MESSAGE, s), 0
        )
    }
    

Java

    public Slice createDynamicSlice(Uri sliceUri) {
        if (getContext() == null || sliceUri.getPath() == null) {
            return null;
        }
        ListBuilder listBuilder = new ListBuilder(getContext(), sliceUri, ListBuilder.INFINITY);
        switch (sliceUri.getPath()) {
            case "/count":
                SliceAction toastAndIncrementAction = SliceAction.create(
                        createToastAndIncrementIntent("Item clicked."),
                        actionIcon,
                        ListBuilder.ICON_IMAGE,
                        "Increment."
                );
                listBuilder.addRow(
                        new ListBuilder.RowBuilder()
                                .setPrimaryAction(toastAndIncrementAction)
                                .setTitle("Count: " + MyBroadcastReceiver.sReceivedCount)
                                .setSubtitle("Click me")
                );
                break;
            default:
                listBuilder.addRow(
                        new ListBuilder.RowBuilder()
                                .setPrimaryAction(createActivityAction())
                                .setTitle("URI not found.")
                );
                break;
        }
        return listBuilder.build();
    }

    public PendingIntent createToastAndIncrementIntent(String s) {
        Intent intent = new Intent(getContext(), MyBroadcastReceiver.class)
                .putExtra(MyBroadcastReceiver.EXTRA_MESSAGE, s);
        return PendingIntent.getBroadcast(getContext(), 0, intent, 0);
    }
    

이 예에서는 수가 표시되지만 자체적으로 업데이트되지 않습니다. broadcast receiver를 수정하여 ContentResolver#notifyChange를 사용하여 시스템에 변경이 발생했음을 알릴 수 있습니다.

Kotlin

    class MyBroadcastReceiver : BroadcastReceiver() {

        override fun onReceive(context: Context, intent: Intent) {
            if (intent.hasExtra(Slice.EXTRA_TOGGLE_STATE)) {
                Toast.makeText(
                    context, "Toggled:  " + intent.getBooleanExtra(
                        Slice.EXTRA_TOGGLE_STATE, false
                    ),
                    Toast.LENGTH_LONG
                ).show()
                receivedCount++;
                context.contentResolver.notifyChange(sliceUri, null)
            }
        }

        companion object {
            var receivedCount = 0
            val sliceUri = Uri.parse("content://com.android.example.slicesample/count")
            const val EXTRA_MESSAGE = "message"
        }
    }
    

Java

    public class MyBroadcastReceiver extends BroadcastReceiver {

        public static int sReceivedCount = 0;
        public static String EXTRA_MESSAGE = "message";

        private static Uri sliceUri = Uri.parse("content://com.android.example.slicesample/count");

        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.hasExtra(EXTRA_TOGGLE_STATE)) {
                Toast.makeText(context, "Toggled:  " + intent.getBooleanExtra(
                        EXTRA_TOGGLE_STATE, false),
                        Toast.LENGTH_LONG).show();
                sReceivedCount++;
                context.getContentResolver().notifyChange(sliceUri, null);
            }
        }
    }
    

템플릿

슬라이스는 다양한 템플릿을 지원합니다. 템플릿 옵션 및 동작에 관한 자세한 내용은 템플릿을 참조하세요.