1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.controls.management
18 
19 import android.content.ComponentName
20 import android.content.Intent
21 import android.os.Bundle
22 import android.view.View
23 import android.view.ViewGroup
24 import android.view.ViewStub
25 import android.widget.Button
26 import android.widget.TextView
27 import androidx.recyclerview.widget.GridLayoutManager
28 import androidx.recyclerview.widget.ItemTouchHelper
29 import androidx.recyclerview.widget.RecyclerView
30 import com.android.systemui.R
31 import com.android.systemui.broadcast.BroadcastDispatcher
32 import com.android.systemui.controls.controller.ControlsControllerImpl
33 import com.android.systemui.controls.controller.StructureInfo
34 import com.android.systemui.globalactions.GlobalActionsComponent
35 import com.android.systemui.settings.CurrentUserTracker
36 import com.android.systemui.util.LifecycleActivity
37 import javax.inject.Inject
38 
39 /**
40  * Activity for rearranging and removing controls for a given structure
41  */
42 class ControlsEditingActivity @Inject constructor(
43     private val controller: ControlsControllerImpl,
44     broadcastDispatcher: BroadcastDispatcher,
45     private val globalActionsComponent: GlobalActionsComponent
46 ) : LifecycleActivity() {
47 
48     companion object {
49         private const val TAG = "ControlsEditingActivity"
50         private const val EXTRA_STRUCTURE = ControlsFavoritingActivity.EXTRA_STRUCTURE
51         private val SUBTITLE_ID = R.string.controls_favorite_rearrange
52         private val EMPTY_TEXT_ID = R.string.controls_favorite_removed
53     }
54 
55     private lateinit var component: ComponentName
56     private lateinit var structure: CharSequence
57     private lateinit var model: FavoritesModel
58     private lateinit var subtitle: TextView
59     private lateinit var saveButton: View
60 
61     private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
62         private val startingUser = controller.currentUserId
63 
onUserSwitchednull64         override fun onUserSwitched(newUserId: Int) {
65             if (newUserId != startingUser) {
66                 stopTracking()
67                 finish()
68             }
69         }
70     }
71 
onCreatenull72     override fun onCreate(savedInstanceState: Bundle?) {
73         super.onCreate(savedInstanceState)
74 
75         intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)?.let {
76             component = it
77         } ?: run(this::finish)
78 
79         intent.getCharSequenceExtra(EXTRA_STRUCTURE)?.let {
80             structure = it
81         } ?: run(this::finish)
82 
83         bindViews()
84 
85         bindButtons()
86     }
87 
onStartnull88     override fun onStart() {
89         super.onStart()
90         setUpList()
91 
92         currentUserTracker.startTracking()
93     }
94 
onStopnull95     override fun onStop() {
96         super.onStop()
97         currentUserTracker.stopTracking()
98     }
99 
onBackPressednull100     override fun onBackPressed() {
101         globalActionsComponent.handleShowGlobalActionsMenu()
102         animateExitAndFinish()
103     }
104 
animateExitAndFinishnull105     private fun animateExitAndFinish() {
106         val rootView = requireViewById<ViewGroup>(R.id.controls_management_root)
107         ControlsAnimations.exitAnimation(
108                 rootView,
109                 object : Runnable {
110                     override fun run() {
111                         finish()
112                     }
113                 }
114         ).start()
115     }
116 
bindViewsnull117     private fun bindViews() {
118         setContentView(R.layout.controls_management)
119 
120         getLifecycle().addObserver(
121             ControlsAnimations.observerForAnimations(
122                 requireViewById<ViewGroup>(R.id.controls_management_root),
123                 window,
124                 intent
125             )
126         )
127 
128         requireViewById<ViewStub>(R.id.stub).apply {
129             layoutResource = R.layout.controls_management_editing
130             inflate()
131         }
132         requireViewById<TextView>(R.id.title).text = structure
133         setTitle(structure)
134         subtitle = requireViewById<TextView>(R.id.subtitle).apply {
135             setText(SUBTITLE_ID)
136         }
137     }
138 
bindButtonsnull139     private fun bindButtons() {
140         val rootView = requireViewById<ViewGroup>(R.id.controls_management_root)
141         saveButton = requireViewById<Button>(R.id.done).apply {
142             isEnabled = false
143             setText(R.string.save)
144             setOnClickListener {
145                 saveFavorites()
146                 animateExitAndFinish()
147                 globalActionsComponent.handleShowGlobalActionsMenu()
148             }
149         }
150     }
151 
saveFavoritesnull152     private fun saveFavorites() {
153         controller.replaceFavoritesForStructure(
154                 StructureInfo(component, structure, model.favorites))
155     }
156 
157     private val favoritesModelCallback = object : FavoritesModel.FavoritesModelCallback {
onNoneChangednull158         override fun onNoneChanged(showNoFavorites: Boolean) {
159             if (showNoFavorites) {
160                 subtitle.setText(EMPTY_TEXT_ID)
161             } else {
162                 subtitle.setText(SUBTITLE_ID)
163             }
164         }
165 
onFirstChangenull166         override fun onFirstChange() {
167             saveButton.isEnabled = true
168         }
169     }
170 
setUpListnull171     private fun setUpList() {
172         val controls = controller.getFavoritesForStructure(component, structure)
173         model = FavoritesModel(component, controls, favoritesModelCallback)
174         val elevation = resources.getFloat(R.dimen.control_card_elevation)
175         val recyclerView = requireViewById<RecyclerView>(R.id.list)
176         recyclerView.alpha = 0.0f
177         val adapter = ControlAdapter(elevation).apply {
178             registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
179                 var hasAnimated = false
180                 override fun onChanged() {
181                     if (!hasAnimated) {
182                         hasAnimated = true
183                         ControlsAnimations.enterAnimation(recyclerView).start()
184                     }
185                 }
186             })
187         }
188 
189         val margin = resources
190                 .getDimensionPixelSize(R.dimen.controls_card_margin)
191         val itemDecorator = MarginItemDecorator(margin, margin)
192 
193         recyclerView.apply {
194             this.adapter = adapter
195             layoutManager = object : GridLayoutManager(recyclerView.context, 2) {
196 
197                 // This will remove from the announcement the row corresponding to the divider,
198                 // as it's not something that should be announced.
199                 override fun getRowCountForAccessibility(
200                     recycler: RecyclerView.Recycler,
201                     state: RecyclerView.State
202                 ): Int {
203                     val initial = super.getRowCountForAccessibility(recycler, state)
204                     return if (initial > 0) initial - 1 else initial
205                 }
206             }.apply {
207                 spanSizeLookup = adapter.spanSizeLookup
208             }
209             addItemDecoration(itemDecorator)
210         }
211         adapter.changeModel(model)
212         model.attachAdapter(adapter)
213         ItemTouchHelper(model.itemTouchHelperCallback).attachToRecyclerView(recyclerView)
214     }
215 
onDestroynull216     override fun onDestroy() {
217         currentUserTracker.stopTracking()
218         super.onDestroy()
219     }
220 }
221