Share
Embed
Transcript
104 slides
-
slice { slice (horizontal, list_item) { slice (list_item) { Icon(...)
(no_tint), “Mon” (title), “62°”, }, slice (list_item) { … }, ... } } UIを構造化されたデータで表現 -
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() } } ... } -
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() } } ... } -
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() } } ... } -
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> メタデータ追加 -
Step2: パーミッションを追加 val manager = SliceManager.getInstance(this) manager.grantSlicePermission( “com.google.android.googlequicksearchbox”, mainUri) manager.grantSlicePermission(
“com.google.android.gms”, mainUri) パーミッション要求 -
val appIndex = FirebaseAppIndex.getInstance() appIndex.update(Indexable.Builder() .setUrl(contentUrl) .setName(“Temperature”) .setKeywords(“living room”, “kitchen”,
“bedroom”) .setMetadata(Indexable.Metadata.Builder() .setSliceUri(sliceUri)) .build()) Step1: Indexを追加 メタデータ追加 -
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> ... intent-filter追加 -
class MySliceProvider : SliceProvider() { … override fun onMapIntentToUri(intent: Intent?):
Uri { val path = intent.data.path.replace(“/slice”, “”) return Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) .authority(“com.example.slicesample”) .path(path) .build() } … } Step3: App IndexingのURIを SliceProviderのURIに変換する onMapIntentToUriを オーバーライド -
class MySliceProvider : SliceProvider() { … override fun onMapIntentToUri(intent: Intent?):
Uri { val path = intent.data.path.replace(“/slice”, “”) return Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) .authority(“com.example.slicesample”) .path(path) .build() } … } Step3: App IndexingのURIを SliceProviderのURIに変換する AppIndexingのURIを -
class MySliceProvider : SliceProvider() { … override fun onMapIntentToUri(intent: Intent?):
Uri { val path = intent.data.path.replace(“/slice”, “”) return Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) .authority(“com.example.slicesample”) .path(path) .build() } … } Step3: App IndexingのURIを SliceProviderのURIに変換する Slice用のURIに変換 -
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() } … } bind開始 -
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 -> … } SliceLiveData 生成 -
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 -> … } 値を監視 -
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); ... } ... SliceManager 取得 -
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); ... } ... bindSlice() を呼ぶ -
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); } } … } -
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); } } … } P以降 -
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以降 -
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); } } … } P未満 -
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); } } … } P未満 今日はこっち -
class SliceManagerCompat extends SliceManagerBase { … @Override public Slice bindSlice(@NonNull
Intent intent) { return SliceProviderCompat.bindSlice(mContext, intent, SUPPORTED_SPECS); } … } SliceProviderCompat呼ぶだけ -
class SliceManagerCompat extends SliceManagerBase { … @Override public Slice bindSlice(@NonNull
Intent intent) { return SliceProviderCompat.bindSlice(mContext, intent, SUPPORTED_SPECS); } … } ※Sliceを返す側も、 SliceProviderCompatを使います -
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); } … } … } -
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); } … } -
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 取得 -
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#call() を呼び -
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); } … } Sliceに変換 -
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)) { … } … } -
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を生成 -
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)) { … } … } Bundleに変換 -
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)) { … } … } Bundleに積む -
public class SliceProviderCompat { … private Slice handleBindSlice(final Uri sliceUri,
final Set<SliceSpec> specs, final String callingPkg) { ... return onBindSliceStrict(sliceUri, specs); } … } -
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); } ... -
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秒 -
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); } ... 重たい処理 マジおこ -
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); } ... SliceProvider #onBindSlice() -
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); } }; ... } -
fun SliceView.bind( context: Context, lifecycleOwner: LifecycleOwner, uri: Uri, ... )
{ ... try { sliceLiveData?.observe(lifecycleOwner, Observer { updatedSlice -> if (updatedSlice == null) return@Observer slice = updatedSlice ... }) … -
public class SliceView extends ViewGroup implements Observer<Slice>, View.OnClickListener { …
public void setSlice(@Nullable Slice slice) { ... mCurrentSlice = slice; reinflate(); } … } SliceからViewを生成 ※省略