Merge "Controls UI - Motion for activities" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
65b3b5e90e
@@ -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"
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -0,0 +1,180 @@
|
|||||||
|
/*
|
||||||
|
* 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)
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -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"
|
||||||
@@ -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()
|
||||||
@@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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() {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
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.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
@@ -41,6 +41,7 @@ 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.dagger.qualifiers.Main
|
import com.android.systemui.dagger.qualifiers.Main
|
||||||
import com.android.systemui.settings.CurrentUserTracker
|
import com.android.systemui.settings.CurrentUserTracker
|
||||||
|
import com.android.systemui.util.LifecycleActivity
|
||||||
import java.text.Collator
|
import java.text.Collator
|
||||||
import java.util.concurrent.Executor
|
import java.util.concurrent.Executor
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
@@ -51,7 +52,7 @@ class ControlsFavoritingActivity @Inject constructor(
|
|||||||
private val controller: ControlsControllerImpl,
|
private val controller: ControlsControllerImpl,
|
||||||
private val listingController: ControlsListingController,
|
private val listingController: ControlsListingController,
|
||||||
broadcastDispatcher: BroadcastDispatcher
|
broadcastDispatcher: BroadcastDispatcher
|
||||||
) : Activity() {
|
) : LifecycleActivity() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "ControlsFavoritingActivity"
|
private const val TAG = "ControlsFavoritingActivity"
|
||||||
@@ -115,6 +116,7 @@ class ControlsFavoritingActivity @Inject constructor(
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
val collator = Collator.getInstance(resources.configuration.locales[0])
|
val collator = Collator.getInstance(resources.configuration.locales[0])
|
||||||
comparator = compareBy(collator) { it.structureName }
|
comparator = compareBy(collator) { it.structureName }
|
||||||
appName = intent.getCharSequenceExtra(EXTRA_APP)
|
appName = intent.getCharSequenceExtra(EXTRA_APP)
|
||||||
@@ -174,12 +176,17 @@ class ControlsFavoritingActivity @Inject constructor(
|
|||||||
pageIndicator.setLocation(0f)
|
pageIndicator.setLocation(0f)
|
||||||
pageIndicator.visibility =
|
pageIndicator.visibility =
|
||||||
if (listOfStructures.size > 1) View.VISIBLE else View.GONE
|
if (listOfStructures.size > 1) View.VISIBLE else View.GONE
|
||||||
|
|
||||||
|
ControlsAnimations.enterAnimation(pageIndicator).start()
|
||||||
|
ControlsAnimations.enterAnimation(structurePager).start()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpPager() {
|
private fun setUpPager() {
|
||||||
|
structurePager.alpha = 0.0f
|
||||||
|
pageIndicator.alpha = 0.0f
|
||||||
structurePager.apply {
|
structurePager.apply {
|
||||||
adapter = StructureAdapter(emptyList())
|
adapter = StructureAdapter(emptyList())
|
||||||
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||||
@@ -203,6 +210,15 @@ class ControlsFavoritingActivity @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_favorites
|
layoutResource = R.layout.controls_management_favorites
|
||||||
inflate()
|
inflate()
|
||||||
@@ -278,7 +294,8 @@ class ControlsFavoritingActivity @Inject constructor(
|
|||||||
val i = Intent()
|
val i = Intent()
|
||||||
i.setComponent(
|
i.setComponent(
|
||||||
ComponentName(context, ControlsProviderSelectorActivity::class.java))
|
ComponentName(context, ControlsProviderSelectorActivity::class.java))
|
||||||
context.startActivity(i)
|
startActivity(i, ActivityOptions
|
||||||
|
.makeSceneTransitionAnimation(this@ControlsFavoritingActivity).toBundle())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,15 +16,18 @@
|
|||||||
|
|
||||||
package com.android.systemui.controls.management
|
package com.android.systemui.controls.management
|
||||||
|
|
||||||
|
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.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
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
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver
|
||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
import com.android.systemui.broadcast.BroadcastDispatcher
|
import com.android.systemui.broadcast.BroadcastDispatcher
|
||||||
import com.android.systemui.controls.controller.ControlsController
|
import com.android.systemui.controls.controller.ControlsController
|
||||||
@@ -66,12 +69,23 @@ class ControlsProviderSelectorActivity @Inject constructor(
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
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_apps
|
layoutResource = R.layout.controls_management_apps
|
||||||
inflate()
|
inflate()
|
||||||
}
|
}
|
||||||
|
|
||||||
recyclerView = requireViewById(R.id.list)
|
recyclerView = requireViewById(R.id.list)
|
||||||
|
recyclerView.alpha = 0.0f
|
||||||
|
recyclerView.layoutManager = LinearLayoutManager(applicationContext)
|
||||||
recyclerView.adapter = AppAdapter(
|
recyclerView.adapter = AppAdapter(
|
||||||
backExecutor,
|
backExecutor,
|
||||||
executor,
|
executor,
|
||||||
@@ -80,11 +94,21 @@ class ControlsProviderSelectorActivity @Inject constructor(
|
|||||||
LayoutInflater.from(this),
|
LayoutInflater.from(this),
|
||||||
::launchFavoritingActivity,
|
::launchFavoritingActivity,
|
||||||
FavoritesRenderer(resources, controlsController::countFavoritesForComponent),
|
FavoritesRenderer(resources, controlsController::countFavoritesForComponent),
|
||||||
resources)
|
resources).apply {
|
||||||
recyclerView.layoutManager = LinearLayoutManager(applicationContext)
|
registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||||
|
var hasAnimated = false
|
||||||
|
override fun onChanged() {
|
||||||
|
if (!hasAnimated) {
|
||||||
|
hasAnimated = true
|
||||||
|
ControlsAnimations.enterAnimation(recyclerView).start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
requireViewById<TextView>(R.id.title).text =
|
requireViewById<TextView>(R.id.title).apply {
|
||||||
resources.getText(R.string.controls_providers_title)
|
text = resources.getText(R.string.controls_providers_title)
|
||||||
|
}
|
||||||
|
|
||||||
requireViewById<Button>(R.id.done).setOnClickListener {
|
requireViewById<Button>(R.id.done).setOnClickListener {
|
||||||
this@ControlsProviderSelectorActivity.finishAffinity()
|
this@ControlsProviderSelectorActivity.finishAffinity()
|
||||||
@@ -98,7 +122,7 @@ class ControlsProviderSelectorActivity @Inject constructor(
|
|||||||
* @param component a component name for a [ControlsProviderService]
|
* @param component a component name for a [ControlsProviderService]
|
||||||
*/
|
*/
|
||||||
fun launchFavoritingActivity(component: ComponentName?) {
|
fun launchFavoritingActivity(component: ComponentName?) {
|
||||||
backExecutor.execute {
|
executor.execute {
|
||||||
component?.let {
|
component?.let {
|
||||||
val intent = Intent(applicationContext, ControlsFavoritingActivity::class.java)
|
val intent = Intent(applicationContext, ControlsFavoritingActivity::class.java)
|
||||||
.apply {
|
.apply {
|
||||||
@@ -107,7 +131,7 @@ class ControlsProviderSelectorActivity @Inject constructor(
|
|||||||
putExtra(Intent.EXTRA_COMPONENT_NAME, it)
|
putExtra(Intent.EXTRA_COMPONENT_NAME, it)
|
||||||
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
|
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||||
}
|
}
|
||||||
startActivity(intent)
|
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,9 +26,10 @@ interface ControlsUiController {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
public const val TAG = "ControlsUiController"
|
public const val TAG = "ControlsUiController"
|
||||||
|
public const val EXTRA_ANIMATE = "extra_animate"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun show(parent: ViewGroup)
|
fun show(parent: ViewGroup, dismissGlobalActions: Runnable)
|
||||||
fun hide()
|
fun hide()
|
||||||
fun onRefreshState(componentName: ComponentName, controls: List<Control>)
|
fun onRefreshState(componentName: ComponentName, controls: List<Control>)
|
||||||
fun onActionResponse(
|
fun onActionResponse(
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ class ControlsUiControllerImpl @Inject constructor (
|
|||||||
private const val PREF_STRUCTURE = "controls_structure"
|
private const val PREF_STRUCTURE = "controls_structure"
|
||||||
|
|
||||||
private const val USE_PANELS = "systemui.controls_use_panel"
|
private const val USE_PANELS = "systemui.controls_use_panel"
|
||||||
private const val FADE_IN_MILLIS = 225L
|
private const val FADE_IN_MILLIS = 200L
|
||||||
|
|
||||||
private val EMPTY_COMPONENT = ComponentName("", "")
|
private val EMPTY_COMPONENT = ComponentName("", "")
|
||||||
private val EMPTY_STRUCTURE = StructureInfo(
|
private val EMPTY_STRUCTURE = StructureInfo(
|
||||||
@@ -104,6 +104,7 @@ class ControlsUiControllerImpl @Inject constructor (
|
|||||||
private var popup: ListPopupWindow? = null
|
private var popup: ListPopupWindow? = null
|
||||||
private var activeDialog: Dialog? = null
|
private var activeDialog: Dialog? = null
|
||||||
private var hidden = true
|
private var hidden = true
|
||||||
|
private lateinit var dismissGlobalActions: Runnable
|
||||||
|
|
||||||
override val available: Boolean
|
override val available: Boolean
|
||||||
get() = controlsController.get().available
|
get() = controlsController.get().available
|
||||||
@@ -134,9 +135,10 @@ class ControlsUiControllerImpl @Inject constructor (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun show(parent: ViewGroup) {
|
override fun show(parent: ViewGroup, dismissGlobalActions: Runnable) {
|
||||||
Log.d(ControlsUiController.TAG, "show()")
|
Log.d(ControlsUiController.TAG, "show()")
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
|
this.dismissGlobalActions = dismissGlobalActions
|
||||||
hidden = false
|
hidden = false
|
||||||
|
|
||||||
allStructures = controlsController.get().getFavorites()
|
allStructures = controlsController.get().getFavorites()
|
||||||
@@ -169,7 +171,7 @@ class ControlsUiControllerImpl @Inject constructor (
|
|||||||
fadeAnim.setDuration(FADE_IN_MILLIS)
|
fadeAnim.setDuration(FADE_IN_MILLIS)
|
||||||
fadeAnim.addListener(object : AnimatorListenerAdapter() {
|
fadeAnim.addListener(object : AnimatorListenerAdapter() {
|
||||||
override fun onAnimationEnd(animation: Animator) {
|
override fun onAnimationEnd(animation: Animator) {
|
||||||
show(parent)
|
show(parent, dismissGlobalActions)
|
||||||
val showAnim = ObjectAnimator.ofFloat(parent, "alpha", 0.0f, 1.0f)
|
val showAnim = ObjectAnimator.ofFloat(parent, "alpha", 0.0f, 1.0f)
|
||||||
showAnim.setInterpolator(DecelerateInterpolator(1.0f))
|
showAnim.setInterpolator(DecelerateInterpolator(1.0f))
|
||||||
showAnim.setDuration(FADE_IN_MILLIS)
|
showAnim.setDuration(FADE_IN_MILLIS)
|
||||||
@@ -256,9 +258,10 @@ class ControlsUiControllerImpl @Inject constructor (
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun startActivity(context: Context, intent: Intent) {
|
private fun startActivity(context: Context, intent: Intent) {
|
||||||
val closeDialog = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
|
// Force animations when transitioning from a dialog to an activity
|
||||||
context.sendBroadcast(closeDialog)
|
intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
|
dismissGlobalActions.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showControlsView(items: List<SelectionItem>) {
|
private fun showControlsView(items: List<SelectionItem>) {
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
|
|||||||
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
import com.android.systemui.colorextraction.SysuiColorExtractor;
|
||||||
import com.android.systemui.controls.ControlsServiceInfo;
|
import com.android.systemui.controls.ControlsServiceInfo;
|
||||||
import com.android.systemui.controls.controller.ControlsController;
|
import com.android.systemui.controls.controller.ControlsController;
|
||||||
|
import com.android.systemui.controls.management.ControlsAnimations;
|
||||||
import com.android.systemui.controls.management.ControlsListingController;
|
import com.android.systemui.controls.management.ControlsListingController;
|
||||||
import com.android.systemui.controls.ui.ControlsUiController;
|
import com.android.systemui.controls.ui.ControlsUiController;
|
||||||
import com.android.systemui.dagger.qualifiers.Background;
|
import com.android.systemui.dagger.qualifiers.Background;
|
||||||
@@ -1815,7 +1816,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
|||||||
case MESSAGE_DISMISS:
|
case MESSAGE_DISMISS:
|
||||||
if (mDialog != null) {
|
if (mDialog != null) {
|
||||||
if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
|
if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) {
|
||||||
mDialog.dismissImmediately();
|
mDialog.completeDismiss();
|
||||||
} else {
|
} else {
|
||||||
mDialog.dismiss();
|
mDialog.dismiss();
|
||||||
}
|
}
|
||||||
@@ -2200,50 +2201,55 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
|||||||
return WindowInsets.CONSUMED;
|
return WindowInsets.CONSUMED;
|
||||||
});
|
});
|
||||||
if (mControlsUiController != null) {
|
if (mControlsUiController != null) {
|
||||||
mControlsUiController.show(mControlsView);
|
mControlsUiController.show(mControlsView, this::dismissForControlsActivity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dismiss() {
|
public void dismiss() {
|
||||||
|
dismissWithAnimation(() -> {
|
||||||
|
mGlobalActionsLayout.setTranslationX(0);
|
||||||
|
mGlobalActionsLayout.setTranslationY(0);
|
||||||
|
mGlobalActionsLayout.setAlpha(1);
|
||||||
|
mGlobalActionsLayout.animate()
|
||||||
|
.alpha(0)
|
||||||
|
.translationX(mGlobalActionsLayout.getAnimationOffsetX())
|
||||||
|
.translationY(mGlobalActionsLayout.getAnimationOffsetY())
|
||||||
|
.setDuration(550)
|
||||||
|
.withEndAction(this::completeDismiss)
|
||||||
|
.setInterpolator(new LogAccelerateInterpolator())
|
||||||
|
.setUpdateListener(animation -> {
|
||||||
|
float animatedValue = 1f - animation.getAnimatedFraction();
|
||||||
|
int alpha = (int) (animatedValue * mScrimAlpha * 255);
|
||||||
|
mBackgroundDrawable.setAlpha(alpha);
|
||||||
|
mDepthController.updateGlobalDialogVisibility(animatedValue,
|
||||||
|
mGlobalActionsLayout);
|
||||||
|
})
|
||||||
|
.start();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dismissForControlsActivity() {
|
||||||
|
dismissWithAnimation(() -> {
|
||||||
|
ViewGroup root = (ViewGroup) mGlobalActionsLayout.getRootView();
|
||||||
|
ControlsAnimations.exitAnimation(root, this::completeDismiss).start();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void dismissWithAnimation(Runnable animation) {
|
||||||
if (!mShowing) {
|
if (!mShowing) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mShowing = false;
|
mShowing = false;
|
||||||
if (mControlsUiController != null) mControlsUiController.hide();
|
animation.run();
|
||||||
mGlobalActionsLayout.setTranslationX(0);
|
|
||||||
mGlobalActionsLayout.setTranslationY(0);
|
|
||||||
mGlobalActionsLayout.setAlpha(1);
|
|
||||||
mGlobalActionsLayout.animate()
|
|
||||||
.alpha(0)
|
|
||||||
.translationX(mGlobalActionsLayout.getAnimationOffsetX())
|
|
||||||
.translationY(mGlobalActionsLayout.getAnimationOffsetY())
|
|
||||||
.setDuration(550)
|
|
||||||
.withEndAction(this::completeDismiss)
|
|
||||||
.setInterpolator(new LogAccelerateInterpolator())
|
|
||||||
.setUpdateListener(animation -> {
|
|
||||||
float animatedValue = 1f - animation.getAnimatedFraction();
|
|
||||||
int alpha = (int) (animatedValue * mScrimAlpha * 255);
|
|
||||||
mBackgroundDrawable.setAlpha(alpha);
|
|
||||||
mDepthController.updateGlobalDialogVisibility(animatedValue,
|
|
||||||
mGlobalActionsLayout);
|
|
||||||
})
|
|
||||||
.start();
|
|
||||||
dismissPanel();
|
|
||||||
dismissOverflow();
|
|
||||||
resetOrientation();
|
|
||||||
}
|
|
||||||
|
|
||||||
void dismissImmediately() {
|
|
||||||
mShowing = false;
|
|
||||||
if (mControlsUiController != null) mControlsUiController.hide();
|
|
||||||
dismissPanel();
|
|
||||||
dismissOverflow();
|
|
||||||
resetOrientation();
|
|
||||||
completeDismiss();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void completeDismiss() {
|
private void completeDismiss() {
|
||||||
|
mShowing = false;
|
||||||
|
resetOrientation();
|
||||||
|
dismissPanel();
|
||||||
|
dismissOverflow();
|
||||||
|
if (mControlsUiController != null) mControlsUiController.hide();
|
||||||
mNotificationShadeWindowController.setForceHasTopUi(mHadTopUi);
|
mNotificationShadeWindowController.setForceHasTopUi(mHadTopUi);
|
||||||
mDepthController.updateGlobalDialogVisibility(0, null /* view */);
|
mDepthController.updateGlobalDialogVisibility(0, null /* view */);
|
||||||
super.dismiss();
|
super.dismiss();
|
||||||
@@ -2304,7 +2310,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
|||||||
initializeLayout();
|
initializeLayout();
|
||||||
mGlobalActionsLayout.updateList();
|
mGlobalActionsLayout.updateList();
|
||||||
if (mControlsUiController != null) {
|
if (mControlsUiController != null) {
|
||||||
mControlsUiController.show(mControlsView);
|
mControlsUiController.show(mControlsView, this::dismissForControlsActivity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2343,7 +2349,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
|
|||||||
&& mControlsUiController.getAvailable()
|
&& mControlsUiController.getAvailable()
|
||||||
&& !mControlsServiceInfos.isEmpty();
|
&& !mControlsServiceInfos.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove legacy layout XML and classes.
|
// TODO: Remove legacy layout XML and classes.
|
||||||
protected boolean shouldUseControlsLayout() {
|
protected boolean shouldUseControlsLayout() {
|
||||||
// always use new controls layout
|
// always use new controls layout
|
||||||
|
|||||||
Reference in New Issue
Block a user