Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Deep Dive into Slices

Deep Dive into Slices

SliceはContentProviderをどう使っているか

kobito-kaba

June 14, 2018
Tweet

More Decks by kobito-kaba

Other Decks in Programming

Transcript

  1. slice { slice (horizontal, list_item) { slice (list_item) { Icon(...)

    (no_tint), “Mon” (title), “62°”, }, slice (list_item) { … }, ... } } UIを構 化されたデータで表現
  2. Step1: SliceProviderをつくる class MySliceProvider : SliceProvider() { ... override fun

    onBindSlice(sliceUri: Uri): Slice? { val context = context ?: return null return if (sliceUri.path == "/") { return ListBuilder(context, sliceUri, ListBuilder.INFINITY) .addRow { it.setTitle("URI found.") } .build() } else { ListBuilder(context, sliceUri, ListBuilder.INFINITY) .addRow { it.setTitle("URI not found.") } .build() } } ... }
  3. Step1: meta-dataを追加 <manifest> ... <application> ... <activity android:name="com.example.some.MainActivity" android:label="Slices Test">

    <meta-data android:name="android.metadata.SLICE_URI" android:value="content://com.example.some/mydefault"/> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> … </application> </manifest>
  4. Step2: intent-filterを追加 <manifest> ... <application> ... <provider android:name=”.MySliceProvider” android:authority=”com.example.slicesample” android:exported=”true”>

    <intent-filter> <action android:name=”android.intent.action.VIEW” /> <category android:name=”android.intent.category.SLICE” /> <data android:scheme=”https” android:host=”example.com” /> </intent-filter> </provider> ...
  5. Step3: App IndexingのURIを SliceProviderのURIに変換する class MySliceProvider : SliceProvider() { …

    fun onMapIntentToUri(intent: Intent): Uri? { val path = intent.getData().getPath().replace(“/slice”, “”) return Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) .authority(“com.example.slicesample”) .path(path) .build() } … }
  6. class SingleSliceViewerActivity : AppCompatActivity() { … private fun bindSlice(uri: Uri)

    { sliceView.bind( context = this, lifecycleOwner = this, uri = uri, scrollable = true ) viewModel.selectedMode.observe(this, Observer { sliceView.mode = it ?: SliceView.MODE_LARGE }) uriValue.text = uri.toString() } … }
  7. fun SliceView.bind( context: Context, lifecycleOwner: LifecycleOwner, uri: Uri, ... )

    { ... val intent = Intent(Intent.ACTION_VIEW, uri) val sliceLiveData = SliceLiveData.fromIntent(context, intent) sliceLiveData?.removeObservers(lifecycleOwner) try { sliceLiveData?.observe(lifecycleOwner, Observer { updatedSlice -> … }
  8. private final Runnable mUpdateSlice = new Runnable() { @Override public

    void run() { Slice s = mUri != null ? mSliceManager.bindSlice(mUri) : mSliceManager.bindSlice(mIntent); ... } }; ... } public final class SliceLiveData { ... private static class SliceLiveDataImpl extends LiveData<Slice> { private SliceLiveDataImpl(Context context, Uri uri) { super(); mSliceManager = SliceManager.getInstance(context); ... } @Override protected void onActive() { AsyncTask.execute(mUpdateSlice); ... } ...
  9. public abstract class SliceManager { /** * Get a {@link

    SliceManager}. */ @SuppressWarnings("NewApi") public static @NonNull SliceManager getInstance(@NonNull Context context) { if (BuildCompat.isAtLeastP()) { return new SliceManagerWrapper(context); } else { return new SliceManagerCompat(context); } } … } SystemServiceが爆誕 P未満はこっち
  10. class SliceManagerCompat extends SliceManagerBase { … @Override public Slice bindSlice(@NonNull

    Intent intent) { return SliceProviderCompat.bindSlice(mContext, intent, SUPPORTED_SPECS); } … } ※Sliceを要求する側も、  Sliceを返す側も、  SliceProviderCompatを使います
  11. public class SliceProviderCompat { … public static Slice bindSlice(Context context,

    Intent intent, Set<SliceSpec> supportedSpecs) { ... ContentResolver resolver = context.getContentResolver(); ... // Check if the intent has data for the slice uri on it and use that final Uri intentData = intent.getData(); if (intentData != null && SLICE_TYPE.equals(resolver.getType(intentData))) { return bindSlice(context, intentData, supportedSpecs); } … } … }
  12. public class SliceProviderCompat { … public static Slice bindSlice(Context context,

    Uri uri, Set<SliceSpec> supportedSpecs) { ProviderHolder holder = acquireClient(context.getContentResolver(), uri); ... try { ... final Bundle res = holder.mProvider.call(METHOD_SLICE, null, extras); ... return new Slice((Bundle) bundle); } … } ContentProviderClientを 取得し ContentProviderClient #call()を呼び 結果をSliceに変換する
  13. public class SliceProviderCompat { … public Bundle call(String method, String

    arg, Bundle extras) { if (method.equals(METHOD_SLICE)) { ... Slice s = handleBindSlice(uri, specs, getCallingPackage()); Bundle b = new Bundle(); b.putParcelable(EXTRA_SLICE, s != null ? s.toBundle() : null); return b; } else if (method.equals(METHOD_MAP_INTENT)) { … } … } Sliceを作成し Bundleに変換し Bundleに詰めて返す
  14. public class SliceProviderCompat { … private Slice handleBindSlice(final Uri sliceUri,

    final Set<SliceSpec> specs, final String callingPkg) { ... return onBindSliceStrict(sliceUri, specs); } … }
  15. public class SliceProviderCompat { … private Slice onBindSliceStrict(Uri sliceUri, Set<SliceSpec>

    specs) { … mHandler.postDelayed(mAnr, SLICE_BIND_ANR); try { StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectAll() .penaltyDeath() .build()); ... try { return mProvider.onBindSlice(sliceUri); } ... ANR 2秒 重たい処理マジおこ onBindSlice()
  16. public final class SliceLiveData { ... private static class SliceLiveDataImpl

    extends LiveData<Slice> { … private final Runnable mUpdateSlice = new Runnable() { @Override public void run() { Slice s = mUri != null ? mSliceManager.bindSlice(mUri) : mSliceManager.bindSlice(mIntent); ... postValue(s); } }; ... }
  17. fun SliceView.bind( context: Context, lifecycleOwner: LifecycleOwner, uri: Uri, ... )

    { ... try { sliceLiveData?.observe(lifecycleOwner, Observer { updatedSlice -> if (updatedSlice == null) return@Observer slice = updatedSlice ... }) …
  18. public class SliceView extends ViewGroup implements Observer<Slice>, View.OnClickListener { …

    public void setSlice(@Nullable Slice slice) { ... mCurrentSlice = slice; reinflate(); } … } SliceからViewを生成 ※省略