0

I'm trying to build a Slice host app A that can display Slices defined at uris provided by other apps, lets say one other app B. I know this functionality is possible because the SliceViewer.apk works fine here: https://developer.android.com/guide/slices/getting-started

A's fragment that is loading the slice into a SliceView

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)

            val sliceView = view.findViewById<SliceView>(R.id.slice)

            context?.let { ctx ->

                val uri = Uri.parse("content://org.test.plugin2/")
                val intent = Intent(Intent.ACTION_VIEW, uri).apply {
                    //component = ComponentName("org.test.plugin2", "org.test.plugin2.MySliceProvider")
                    //setPackage("org.test.plugin2")
                }

                SliceLiveData.fromIntent(ctx, intent).observe(viewLifecycleOwner) { slice ->
                    sliceView.slice = slice
                }
            }
        }

App B provider manifest definition:

<provider
            android:name=".MySliceProvider"
            android:authorities="org.test.plugin2"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.app.slice.category.SLICE" />

                <data
                    android:host="plugin2.test.org"
                    android:pathPrefix="/"
                    android:scheme="https" />
            </intent-filter>
        </provider>

App B's Slice provider implementation:

class MySliceProvider : SliceProvider() {
    /**
     * Instantiate any required objects. Return true if the provider was successfully created,
     * false otherwise.
     */
    override fun onCreateSliceProvider(): Boolean {
        return true
    }

    /**
     * Converts URL to content URI (i.e. content://org.test.plugin2...)
     */
    override fun onMapIntentToUri(intent: Intent): Uri {
        // Note: implementing this is only required if you plan on catching URL requests.
        // This is an example solution.
        var uriBuilder: Uri.Builder = Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
        if (intent == null) return uriBuilder.build()
        val data = intent.data
        val dataPath = data?.path
        if (data != null && dataPath != null) {
            val path = dataPath.replace("/", "")
            uriBuilder = uriBuilder.path(path)
        }
        val context = context
        if (context != null) {
            uriBuilder = uriBuilder.authority(context.packageName)
        }
        return uriBuilder.build()
    }

    /**
     * Construct the Slice and bind data if available.
     */
    override fun onBindSlice(sliceUri: Uri): Slice? {     
        val context = context ?: return null
        return createHelloWorldSlice(context, sliceUri)
    }

    private fun createHelloWorldSlice(context: Context, sliceUri: Uri): Slice {
        return list(context, sliceUri, ListBuilder.INFINITY) {
            header {
                title = "Hello World"
            }
        }
    }
}

Slice dependencies

 implementation 'androidx.slice:slice-view:1.1.0-alpha02'
 implementation 'androidx.slice:slice-builders-ktx:1.0.0-alpha6'
 implementation 'androidx.annotation:annotation:1.4.0-alpha02'

From my understanding of the documentation the Hello world row should be populating in my app's fragment layout using the Slice View, here is the layout just for record:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ScrollView
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent">

        <FrameLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

            <androidx.slice.widget.SliceView
                android:id="@+id/slice"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="@dimen/margin_medium"
                android:layout_marginEnd="@dimen/margin_medium"
                android:layout_marginStart="@dimen/margin_medium"
                android:layout_marginTop="@dimen/margin_medium"
                android:background="@color/cardview_light_background"
                android:elevation="@dimen/slice_elevation"
                android:paddingEnd="@dimen/margin_small"
                android:paddingStart="@dimen/margin_small" />
        </FrameLayout>
    </ScrollView>

</androidx.constraintlayout.widget.ConstraintLayout>

I'm also seeing

E/ActivityThread: Failed to find provider info for org.test.plugin2

I can reproduce this by installing app A (host), installing app B (plugin with Slice Provider), switching back to A and I see the error message, and no slice!

Which seems to be the underlying issue. As you can see in the commented code I tried to explicitly set the package name and component which did not help.

Any help or alternative suggestion is much appreciated, thanks.

cashews
  • 26
  • 3

1 Answers1

0

Decrementing the targetSdk from 31 to 27 fixed this. Looking at this code from SliceProvider:

  @Nullable
    @Override
    public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
        if (Build.VERSION.SDK_INT < 19 || Build.VERSION.SDK_INT >= 28) return null;
        if (extras == null) return null;
        return getSliceProviderCompat().call(method, arg, extras);
    }

Then there's this comment above the function: "Handles the call to SliceProvider. This function is unsupported for sdk < 19. For sdk 28 and above the call is handled by android.app.slice.SliceProvider"

Not sure exactly how its handled because it is working for SDK 28 as well.

cashews
  • 26
  • 3