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.car.rotaryplayground;
18 
19 import android.graphics.Color;
20 import android.view.View;
21 import android.view.ViewGroup;
22 
23 import androidx.annotation.NonNull;
24 import androidx.annotation.Nullable;
25 
26 import com.android.car.ui.utils.DirectManipulationHelper;
27 
28 /**
29  * Keeps track of the state of "direct manipulation" Rotary mode for this application window by
30  * tracking a reference to the {@link View} from which the user first enters into "direct
31  * manipulation" mode.
32  *
33  * <p>See {@link DirectManipulationHandler} for a definition of "direct manipulation".
34  */
35 public class DirectManipulationState {
36 
37 
38     /** Background color of a view when it's in direct manipulation mode. */
39     private static final int BACKGROUND_COLOR_IN_DIRECT_MANIPULATION_MODE = Color.BLUE;
40 
41     /** Background color of a view when it's not in direct manipulation mode. */
42     private static final int BACKGROUND_COLOR_NOT_IN_DIRECT_MANIPULATION_MODE = Color.TRANSPARENT;
43 
44     /** The view that is in direct manipulation mode, or null if none. */
45     @Nullable private View mViewInDirectManipulationMode;
46 
setStartingView(@ullable View view)47     private void setStartingView(@Nullable View view) {
48         mViewInDirectManipulationMode = view;
49     }
50 
51     /**
52      * Returns true if Direct Manipulation mode is active, false otherwise.
53      */
isActive()54     public boolean isActive() {
55         return mViewInDirectManipulationMode != null;
56     }
57 
58     /**
59      * Enables Direct Manipulation mode, and keeps track of {@code view} as the starting point
60      * of this transition.
61      * <p>
62      * We generally want to give some kind of visual indication that this change has happened. In
63      * this example we change the background color of {@code view}.
64      *
65      * @param view - the {@link View} from which we entered into Direct Manipulation mode.
66      */
enable(@onNull View view)67     public void enable(@NonNull View view) {
68         /*
69          * A more robust approach would be to fetch the current background color from
70          * the view object and store it back onto the View itself using the {@link
71          * View#setTag(int, java.lang.Object)} API. This could then be fetched back
72          * and used to restore the background color without needing to keep a constant
73          * reference to the color here which could fall out of sync with the xml files.
74          */
75         view.setBackgroundColor(BACKGROUND_COLOR_IN_DIRECT_MANIPULATION_MODE);
76         DirectManipulationHelper.enableDirectManipulationMode(view, /* enable= */ true);
77         setStartingView(view);
78     }
79 
80     /**
81      * Disables Direct Manipulation mode and restores any visual indicators for the {@link View}
82      * from which we entered into Direct Manipulation mode.
83      */
disable()84     public void disable() {
85         mViewInDirectManipulationMode.setBackgroundColor(
86                 BACKGROUND_COLOR_NOT_IN_DIRECT_MANIPULATION_MODE);
87         DirectManipulationHelper.enableDirectManipulationMode(
88                 mViewInDirectManipulationMode, /* enable= */ false);
89         // For ViewGroup objects, restore descendant focusability to FOCUS_BLOCK_DESCENDANTS so
90         // during non-Direct Manipulation mode, aka, general rotary navigation, we don't go
91         // through the individual inner UI elements.
92         if (mViewInDirectManipulationMode instanceof ViewGroup) {
93             ViewGroup viewGroup = (ViewGroup) mViewInDirectManipulationMode;
94             viewGroup.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
95         }
96         setStartingView(null);
97     }
98 }
99