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 static android.view.ViewGroup.FOCUS_AFTER_DESCENDANTS;
20 
21 import android.graphics.Color;
22 import android.graphics.drawable.Drawable;
23 import android.view.View;
24 import android.view.ViewGroup;
25 
26 import androidx.annotation.NonNull;
27 import androidx.annotation.Nullable;
28 
29 import com.android.car.ui.utils.DirectManipulationHelper;
30 
31 /**
32  * Keeps track of the state of "direct manipulation" Rotary mode for this application window by
33  * tracking a reference to the {@link View} from which the user first enters into "direct
34  * manipulation" mode.
35  *
36  * <p>See {@link DirectManipulationHandler} for a definition of "direct manipulation".
37  */
38 public class DirectManipulationState {
39 
40     /** Background color of a view when it's in direct manipulation mode. */
41     private static final int BACKGROUND_COLOR_IN_DIRECT_MANIPULATION_MODE = Color.BLUE;
42 
43     /** Indicates that the descendant focusability has not been set. */
44     private static final int UNKNOWN_DESCENDANT_FOCUSABILITY = -1;
45 
46     /** The view that is in direct manipulation mode, or null if none. */
47     @Nullable
48     private View mViewInDirectManipulationMode;
49     /** The original background of the view in direct manipulation mode. */
50     @Nullable
51     private Drawable mOriginalBackground;
52     /** The original descendant focusability value of the view in direct manipulation mode. */
53     private int mOriginalDescendantFocusability = UNKNOWN_DESCENDANT_FOCUSABILITY;
54 
55     /**
56      * Returns true if Direct Manipulation mode is active, false otherwise.
57      */
isActive()58     public boolean isActive() {
59         return mViewInDirectManipulationMode != null;
60     }
61 
62     /**
63      * Enables Direct Manipulation mode, and keeps track of {@code view} as the starting point
64      * of this transition.
65      * <p>
66      * We generally want to give some kind of visual indication that this change has happened. In
67      * this example we change the background color of {@code view}.
68      *
69      * @param view the {@link View} from which we entered into Direct Manipulation mode
70      */
enable(@onNull View view)71     public void enable(@NonNull View view) {
72         mViewInDirectManipulationMode = view;
73         mOriginalBackground = view.getBackground();
74         if (mViewInDirectManipulationMode instanceof ViewGroup) {
75             ViewGroup viewGroup = (ViewGroup) mViewInDirectManipulationMode;
76             mOriginalDescendantFocusability = viewGroup.getDescendantFocusability();
77             viewGroup.setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
78         }
79         view.setBackgroundColor(BACKGROUND_COLOR_IN_DIRECT_MANIPULATION_MODE);
80         DirectManipulationHelper.enableDirectManipulationMode(view, /* enable= */ true);
81     }
82 
83     /**
84      * Disables Direct Manipulation mode and restores any visual indicators for the {@link View}
85      * from which we entered into Direct Manipulation mode.
86      */
disable()87     public void disable() {
88         mViewInDirectManipulationMode.setBackground(mOriginalBackground);
89         DirectManipulationHelper.enableDirectManipulationMode(
90                 mViewInDirectManipulationMode, /* enable= */ false);
91         // For ViewGroup objects, restore descendant focusability to the previous value.
92         if (mViewInDirectManipulationMode instanceof ViewGroup
93                 && mOriginalDescendantFocusability != UNKNOWN_DESCENDANT_FOCUSABILITY) {
94             ViewGroup viewGroup = (ViewGroup) mViewInDirectManipulationMode;
95             viewGroup.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
96         }
97 
98         mViewInDirectManipulationMode = null;
99         mOriginalBackground = null;
100         mOriginalDescendantFocusability = UNKNOWN_DESCENDANT_FOCUSABILITY;
101     }
102 }
103