Loading packages/SystemUI/res/layout/controls_management.xml +1 −0 Original line number Original line Diff line number Diff line Loading @@ -17,6 +17,7 @@ <LinearLayout <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/controls_management_root" android:orientation="vertical" android:orientation="vertical" android:layout_width="match_parent" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_height="match_parent" Loading packages/SystemUI/res/values/dimens.xml +3 −0 Original line number Original line Diff line number Diff line Loading @@ -1014,6 +1014,9 @@ <!-- Margins at the left and right of the power menu and home controls widgets. --> <!-- Margins at the left and right of the power menu and home controls widgets. --> <dimen name="global_actions_side_margin">16dp</dimen> <dimen name="global_actions_side_margin">16dp</dimen> <!-- Amount to shift the layout when exiting/entering for controls activities --> <dimen name="global_actions_controls_y_translation">20dp</dimen> <!-- The maximum offset in either direction that elements are moved horizontally to prevent <!-- The maximum offset in either direction that elements are moved horizontally to prevent burn-in on AOD. --> burn-in on AOD. --> <dimen name="burn_in_prevention_offset_x">8dp</dimen> <dimen name="burn_in_prevention_offset_x">8dp</dimen> Loading packages/SystemUI/res/values/styles.xml +6 −1 Original line number Original line Diff line number Diff line Loading @@ -663,8 +663,13 @@ <!-- Controls styles --> <!-- Controls styles --> <style name="Theme.ControlsManagement" parent="@android:style/Theme.DeviceDefault.NoActionBar"> <style name="Theme.ControlsManagement" parent="@android:style/Theme.DeviceDefault.NoActionBar"> <item name="android:windowActivityTransitions">true</item> <item name="android:windowContentTransitions">false</item> <item name="android:windowIsTranslucent">false</item> <item name="android:windowIsTranslucent">false</item> <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item> <item name="android:windowBackground">@android:color/black</item> <item name="android:colorBackground">@android:color/black</item> <item name="android:windowAnimationStyle">@null</item> <item name="android:statusBarColor">@*android:color/transparent</item> </style> </style> <style name="TextAppearance.Control"> <style name="TextAppearance.Control"> Loading packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt 0 → 100644 +180 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.controls.management import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.AnimatorSet import android.animation.ObjectAnimator import android.annotation.IdRes import android.content.Intent import android.transition.Transition import android.transition.TransitionValues import android.util.Log import android.view.View import android.view.ViewGroup import android.view.Window import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent import com.android.systemui.Interpolators import com.android.systemui.R import com.android.systemui.controls.ui.ControlsUiController object ControlsAnimations { private const val ALPHA_EXIT_DURATION = 167L private const val ALPHA_ENTER_DELAY = ALPHA_EXIT_DURATION private const val ALPHA_ENTER_DURATION = 350L - ALPHA_ENTER_DELAY private const val Y_TRANSLATION_EXIT_DURATION = 183L private const val Y_TRANSLATION_ENTER_DELAY = Y_TRANSLATION_EXIT_DURATION - ALPHA_ENTER_DELAY private const val Y_TRANSLATION_ENTER_DURATION = 400L - Y_TRANSLATION_EXIT_DURATION private var translationY: Float = -1f /** * Setup an activity to handle enter/exit animations. [view] should be the root of the content. * Fade and translate together. */ fun observerForAnimations(view: ViewGroup, window: Window, intent: Intent): LifecycleObserver { return object : LifecycleObserver { var showAnimation = intent.getBooleanExtra(ControlsUiController.EXTRA_ANIMATE, false) init { // Must flag the parent group to move it all together, and set the initial // transitionAlpha to 0.0f. This property is reserved for fade animations. view.setTransitionGroup(true) view.transitionAlpha = 0.0f if (translationY == -1f) { translationY = view.context.resources.getDimensionPixelSize( R.dimen.global_actions_controls_y_translation).toFloat() } } @OnLifecycleEvent(Lifecycle.Event.ON_START) fun setup() { with(window) { allowEnterTransitionOverlap = true enterTransition = enterWindowTransition(view.getId()) exitTransition = exitWindowTransition(view.getId()) } } @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun enterAnimation() { if (showAnimation) { ControlsAnimations.enterAnimation(view).start() showAnimation = false } } } } fun enterAnimation(view: View): Animator { Log.d(ControlsUiController.TAG, "Enter animation for $view") view.transitionAlpha = 0.0f view.alpha = 1.0f view.translationY = translationY val alphaAnimator = ObjectAnimator.ofFloat(view, "transitionAlpha", 0.0f, 1.0f).apply { interpolator = Interpolators.DECELERATE_QUINT startDelay = ALPHA_ENTER_DELAY duration = ALPHA_ENTER_DURATION } val yAnimator = ObjectAnimator.ofFloat(view, "translationY", 0.0f).apply { interpolator = Interpolators.DECELERATE_QUINT startDelay = Y_TRANSLATION_ENTER_DURATION duration = Y_TRANSLATION_ENTER_DURATION } return AnimatorSet().apply { playTogether(alphaAnimator, yAnimator) } } /** * Properly handle animations originating from dialogs. Activity transitions require * transitioning between two activities, so expose this method for dialogs to animate * on exit. */ @JvmStatic fun exitAnimation(view: View, onEnd: Runnable? = null): Animator { Log.d(ControlsUiController.TAG, "Exit animation for $view") val alphaAnimator = ObjectAnimator.ofFloat(view, "transitionAlpha", 0.0f).apply { interpolator = Interpolators.ACCELERATE duration = ALPHA_EXIT_DURATION } view.translationY = 0.0f val yAnimator = ObjectAnimator.ofFloat(view, "translationY", -translationY).apply { interpolator = Interpolators.ACCELERATE duration = Y_TRANSLATION_EXIT_DURATION } return AnimatorSet().apply { playTogether(alphaAnimator, yAnimator) onEnd?.let { addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { it.run() } }) } } } fun enterWindowTransition(@IdRes id: Int) = WindowTransition({ view: View -> enterAnimation(view) }).apply { addTarget(id) } fun exitWindowTransition(@IdRes id: Int) = WindowTransition({ view: View -> exitAnimation(view) }).apply { addTarget(id) } } /** * In order to animate, at least one property must be marked on each view that should move. * Setting "item" is just a flag to indicate that it should move by the animator. */ class WindowTransition( val animator: (view: View) -> Animator ) : Transition() { override fun captureStartValues(tv: TransitionValues) { tv.values["item"] = 0.0f } override fun captureEndValues(tv: TransitionValues) { tv.values["item"] = 1.0f } override fun createAnimator( sceneRoot: ViewGroup, startValues: TransitionValues?, endValues: TransitionValues? ): Animator? = animator(startValues!!.view) } packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt +33 −10 Original line number Original line Diff line number Diff line Loading @@ -16,11 +16,12 @@ package com.android.systemui.controls.management package com.android.systemui.controls.management import android.app.Activity import android.app.ActivityOptions import android.content.ComponentName import android.content.ComponentName import android.content.Intent import android.content.Intent import android.os.Bundle import android.os.Bundle import android.view.View import android.view.View import android.view.ViewGroup import android.view.ViewStub import android.view.ViewStub import android.widget.Button import android.widget.Button import android.widget.TextView import android.widget.TextView Loading @@ -32,6 +33,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.controls.controller.ControlsControllerImpl import com.android.systemui.controls.controller.ControlsControllerImpl import com.android.systemui.controls.controller.StructureInfo import com.android.systemui.controls.controller.StructureInfo import com.android.systemui.settings.CurrentUserTracker import com.android.systemui.settings.CurrentUserTracker import com.android.systemui.util.LifecycleActivity import javax.inject.Inject import javax.inject.Inject /** /** Loading @@ -40,7 +42,7 @@ import javax.inject.Inject class ControlsEditingActivity @Inject constructor( class ControlsEditingActivity @Inject constructor( private val controller: ControlsControllerImpl, private val controller: ControlsControllerImpl, broadcastDispatcher: BroadcastDispatcher broadcastDispatcher: BroadcastDispatcher ) : Activity() { ) : LifecycleActivity() { companion object { companion object { private const val TAG = "ControlsEditingActivity" private const val TAG = "ControlsEditingActivity" Loading Loading @@ -92,6 +94,15 @@ class ControlsEditingActivity @Inject constructor( private fun bindViews() { private fun bindViews() { setContentView(R.layout.controls_management) setContentView(R.layout.controls_management) getLifecycle().addObserver( ControlsAnimations.observerForAnimations( requireViewById<ViewGroup>(R.id.controls_management_root), window, intent ) ) requireViewById<ViewStub>(R.id.stub).apply { requireViewById<ViewStub>(R.id.stub).apply { layoutResource = R.layout.controls_management_editing layoutResource = R.layout.controls_management_editing inflate() inflate() Loading @@ -113,8 +124,8 @@ class ControlsEditingActivity @Inject constructor( putExtras(this@ControlsEditingActivity.intent) putExtras(this@ControlsEditingActivity.intent) putExtra(ControlsFavoritingActivity.EXTRA_SINGLE_STRUCTURE, true) putExtra(ControlsFavoritingActivity.EXTRA_SINGLE_STRUCTURE, true) } } startActivity(intent) startActivity(intent, ActivityOptions finish() .makeSceneTransitionAnimation(this@ControlsEditingActivity).toBundle()) } } } } Loading Loading @@ -151,22 +162,34 @@ class ControlsEditingActivity @Inject constructor( val controls = controller.getFavoritesForStructure(component, structure) val controls = controller.getFavoritesForStructure(component, structure) model = FavoritesModel(component, controls, favoritesModelCallback) model = FavoritesModel(component, controls, favoritesModelCallback) val elevation = resources.getFloat(R.dimen.control_card_elevation) val elevation = resources.getFloat(R.dimen.control_card_elevation) val adapter = ControlAdapter(elevation) val recyclerView = requireViewById<RecyclerView>(R.id.list) val recycler = requireViewById<RecyclerView>(R.id.list) recyclerView.alpha = 0.0f val adapter = ControlAdapter(elevation).apply { registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { var hasAnimated = false override fun onChanged() { if (!hasAnimated) { hasAnimated = true ControlsAnimations.enterAnimation(recyclerView).start() } } }) } val margin = resources val margin = resources .getDimensionPixelSize(R.dimen.controls_card_margin) .getDimensionPixelSize(R.dimen.controls_card_margin) val itemDecorator = MarginItemDecorator(margin, margin) val itemDecorator = MarginItemDecorator(margin, margin) recycler.apply { recyclerView.apply { this.adapter = adapter this.adapter = adapter layoutManager = GridLayoutManager(recycler.context, 2).apply { layoutManager = GridLayoutManager(recyclerView.context, 2).apply { spanSizeLookup = adapter.spanSizeLookup spanSizeLookup = adapter.spanSizeLookup } } addItemDecoration(itemDecorator) addItemDecoration(itemDecorator) } } adapter.changeModel(model) adapter.changeModel(model) model.attachAdapter(adapter) model.attachAdapter(adapter) ItemTouchHelper(model.itemTouchHelperCallback).attachToRecyclerView(recycler) ItemTouchHelper(model.itemTouchHelperCallback).attachToRecyclerView(recyclerView) } } override fun onDestroy() { override fun onDestroy() { Loading Loading
packages/SystemUI/res/layout/controls_management.xml +1 −0 Original line number Original line Diff line number Diff line Loading @@ -17,6 +17,7 @@ <LinearLayout <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/controls_management_root" android:orientation="vertical" android:orientation="vertical" android:layout_width="match_parent" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_height="match_parent" Loading
packages/SystemUI/res/values/dimens.xml +3 −0 Original line number Original line Diff line number Diff line Loading @@ -1014,6 +1014,9 @@ <!-- Margins at the left and right of the power menu and home controls widgets. --> <!-- Margins at the left and right of the power menu and home controls widgets. --> <dimen name="global_actions_side_margin">16dp</dimen> <dimen name="global_actions_side_margin">16dp</dimen> <!-- Amount to shift the layout when exiting/entering for controls activities --> <dimen name="global_actions_controls_y_translation">20dp</dimen> <!-- The maximum offset in either direction that elements are moved horizontally to prevent <!-- The maximum offset in either direction that elements are moved horizontally to prevent burn-in on AOD. --> burn-in on AOD. --> <dimen name="burn_in_prevention_offset_x">8dp</dimen> <dimen name="burn_in_prevention_offset_x">8dp</dimen> Loading
packages/SystemUI/res/values/styles.xml +6 −1 Original line number Original line Diff line number Diff line Loading @@ -663,8 +663,13 @@ <!-- Controls styles --> <!-- Controls styles --> <style name="Theme.ControlsManagement" parent="@android:style/Theme.DeviceDefault.NoActionBar"> <style name="Theme.ControlsManagement" parent="@android:style/Theme.DeviceDefault.NoActionBar"> <item name="android:windowActivityTransitions">true</item> <item name="android:windowContentTransitions">false</item> <item name="android:windowIsTranslucent">false</item> <item name="android:windowIsTranslucent">false</item> <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item> <item name="android:windowBackground">@android:color/black</item> <item name="android:colorBackground">@android:color/black</item> <item name="android:windowAnimationStyle">@null</item> <item name="android:statusBarColor">@*android:color/transparent</item> </style> </style> <style name="TextAppearance.Control"> <style name="TextAppearance.Control"> Loading
packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt 0 → 100644 +180 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.controls.management import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.AnimatorSet import android.animation.ObjectAnimator import android.annotation.IdRes import android.content.Intent import android.transition.Transition import android.transition.TransitionValues import android.util.Log import android.view.View import android.view.ViewGroup import android.view.Window import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent import com.android.systemui.Interpolators import com.android.systemui.R import com.android.systemui.controls.ui.ControlsUiController object ControlsAnimations { private const val ALPHA_EXIT_DURATION = 167L private const val ALPHA_ENTER_DELAY = ALPHA_EXIT_DURATION private const val ALPHA_ENTER_DURATION = 350L - ALPHA_ENTER_DELAY private const val Y_TRANSLATION_EXIT_DURATION = 183L private const val Y_TRANSLATION_ENTER_DELAY = Y_TRANSLATION_EXIT_DURATION - ALPHA_ENTER_DELAY private const val Y_TRANSLATION_ENTER_DURATION = 400L - Y_TRANSLATION_EXIT_DURATION private var translationY: Float = -1f /** * Setup an activity to handle enter/exit animations. [view] should be the root of the content. * Fade and translate together. */ fun observerForAnimations(view: ViewGroup, window: Window, intent: Intent): LifecycleObserver { return object : LifecycleObserver { var showAnimation = intent.getBooleanExtra(ControlsUiController.EXTRA_ANIMATE, false) init { // Must flag the parent group to move it all together, and set the initial // transitionAlpha to 0.0f. This property is reserved for fade animations. view.setTransitionGroup(true) view.transitionAlpha = 0.0f if (translationY == -1f) { translationY = view.context.resources.getDimensionPixelSize( R.dimen.global_actions_controls_y_translation).toFloat() } } @OnLifecycleEvent(Lifecycle.Event.ON_START) fun setup() { with(window) { allowEnterTransitionOverlap = true enterTransition = enterWindowTransition(view.getId()) exitTransition = exitWindowTransition(view.getId()) } } @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) fun enterAnimation() { if (showAnimation) { ControlsAnimations.enterAnimation(view).start() showAnimation = false } } } } fun enterAnimation(view: View): Animator { Log.d(ControlsUiController.TAG, "Enter animation for $view") view.transitionAlpha = 0.0f view.alpha = 1.0f view.translationY = translationY val alphaAnimator = ObjectAnimator.ofFloat(view, "transitionAlpha", 0.0f, 1.0f).apply { interpolator = Interpolators.DECELERATE_QUINT startDelay = ALPHA_ENTER_DELAY duration = ALPHA_ENTER_DURATION } val yAnimator = ObjectAnimator.ofFloat(view, "translationY", 0.0f).apply { interpolator = Interpolators.DECELERATE_QUINT startDelay = Y_TRANSLATION_ENTER_DURATION duration = Y_TRANSLATION_ENTER_DURATION } return AnimatorSet().apply { playTogether(alphaAnimator, yAnimator) } } /** * Properly handle animations originating from dialogs. Activity transitions require * transitioning between two activities, so expose this method for dialogs to animate * on exit. */ @JvmStatic fun exitAnimation(view: View, onEnd: Runnable? = null): Animator { Log.d(ControlsUiController.TAG, "Exit animation for $view") val alphaAnimator = ObjectAnimator.ofFloat(view, "transitionAlpha", 0.0f).apply { interpolator = Interpolators.ACCELERATE duration = ALPHA_EXIT_DURATION } view.translationY = 0.0f val yAnimator = ObjectAnimator.ofFloat(view, "translationY", -translationY).apply { interpolator = Interpolators.ACCELERATE duration = Y_TRANSLATION_EXIT_DURATION } return AnimatorSet().apply { playTogether(alphaAnimator, yAnimator) onEnd?.let { addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { it.run() } }) } } } fun enterWindowTransition(@IdRes id: Int) = WindowTransition({ view: View -> enterAnimation(view) }).apply { addTarget(id) } fun exitWindowTransition(@IdRes id: Int) = WindowTransition({ view: View -> exitAnimation(view) }).apply { addTarget(id) } } /** * In order to animate, at least one property must be marked on each view that should move. * Setting "item" is just a flag to indicate that it should move by the animator. */ class WindowTransition( val animator: (view: View) -> Animator ) : Transition() { override fun captureStartValues(tv: TransitionValues) { tv.values["item"] = 0.0f } override fun captureEndValues(tv: TransitionValues) { tv.values["item"] = 1.0f } override fun createAnimator( sceneRoot: ViewGroup, startValues: TransitionValues?, endValues: TransitionValues? ): Animator? = animator(startValues!!.view) }
packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt +33 −10 Original line number Original line Diff line number Diff line Loading @@ -16,11 +16,12 @@ package com.android.systemui.controls.management package com.android.systemui.controls.management import android.app.Activity import android.app.ActivityOptions import android.content.ComponentName import android.content.ComponentName import android.content.Intent import android.content.Intent import android.os.Bundle import android.os.Bundle import android.view.View import android.view.View import android.view.ViewGroup import android.view.ViewStub import android.view.ViewStub import android.widget.Button import android.widget.Button import android.widget.TextView import android.widget.TextView Loading @@ -32,6 +33,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.controls.controller.ControlsControllerImpl import com.android.systemui.controls.controller.ControlsControllerImpl import com.android.systemui.controls.controller.StructureInfo import com.android.systemui.controls.controller.StructureInfo import com.android.systemui.settings.CurrentUserTracker import com.android.systemui.settings.CurrentUserTracker import com.android.systemui.util.LifecycleActivity import javax.inject.Inject import javax.inject.Inject /** /** Loading @@ -40,7 +42,7 @@ import javax.inject.Inject class ControlsEditingActivity @Inject constructor( class ControlsEditingActivity @Inject constructor( private val controller: ControlsControllerImpl, private val controller: ControlsControllerImpl, broadcastDispatcher: BroadcastDispatcher broadcastDispatcher: BroadcastDispatcher ) : Activity() { ) : LifecycleActivity() { companion object { companion object { private const val TAG = "ControlsEditingActivity" private const val TAG = "ControlsEditingActivity" Loading Loading @@ -92,6 +94,15 @@ class ControlsEditingActivity @Inject constructor( private fun bindViews() { private fun bindViews() { setContentView(R.layout.controls_management) setContentView(R.layout.controls_management) getLifecycle().addObserver( ControlsAnimations.observerForAnimations( requireViewById<ViewGroup>(R.id.controls_management_root), window, intent ) ) requireViewById<ViewStub>(R.id.stub).apply { requireViewById<ViewStub>(R.id.stub).apply { layoutResource = R.layout.controls_management_editing layoutResource = R.layout.controls_management_editing inflate() inflate() Loading @@ -113,8 +124,8 @@ class ControlsEditingActivity @Inject constructor( putExtras(this@ControlsEditingActivity.intent) putExtras(this@ControlsEditingActivity.intent) putExtra(ControlsFavoritingActivity.EXTRA_SINGLE_STRUCTURE, true) putExtra(ControlsFavoritingActivity.EXTRA_SINGLE_STRUCTURE, true) } } startActivity(intent) startActivity(intent, ActivityOptions finish() .makeSceneTransitionAnimation(this@ControlsEditingActivity).toBundle()) } } } } Loading Loading @@ -151,22 +162,34 @@ class ControlsEditingActivity @Inject constructor( val controls = controller.getFavoritesForStructure(component, structure) val controls = controller.getFavoritesForStructure(component, structure) model = FavoritesModel(component, controls, favoritesModelCallback) model = FavoritesModel(component, controls, favoritesModelCallback) val elevation = resources.getFloat(R.dimen.control_card_elevation) val elevation = resources.getFloat(R.dimen.control_card_elevation) val adapter = ControlAdapter(elevation) val recyclerView = requireViewById<RecyclerView>(R.id.list) val recycler = requireViewById<RecyclerView>(R.id.list) recyclerView.alpha = 0.0f val adapter = ControlAdapter(elevation).apply { registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { var hasAnimated = false override fun onChanged() { if (!hasAnimated) { hasAnimated = true ControlsAnimations.enterAnimation(recyclerView).start() } } }) } val margin = resources val margin = resources .getDimensionPixelSize(R.dimen.controls_card_margin) .getDimensionPixelSize(R.dimen.controls_card_margin) val itemDecorator = MarginItemDecorator(margin, margin) val itemDecorator = MarginItemDecorator(margin, margin) recycler.apply { recyclerView.apply { this.adapter = adapter this.adapter = adapter layoutManager = GridLayoutManager(recycler.context, 2).apply { layoutManager = GridLayoutManager(recyclerView.context, 2).apply { spanSizeLookup = adapter.spanSizeLookup spanSizeLookup = adapter.spanSizeLookup } } addItemDecoration(itemDecorator) addItemDecoration(itemDecorator) } } adapter.changeModel(model) adapter.changeModel(model) model.attachAdapter(adapter) model.attachAdapter(adapter) ItemTouchHelper(model.itemTouchHelperCallback).attachToRecyclerView(recycler) ItemTouchHelper(model.itemTouchHelperCallback).attachToRecyclerView(recyclerView) } } override fun onDestroy() { override fun onDestroy() { Loading