1 /*
2  * Copyright (C) 2012 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.statusbar.phone;
18 
19 import static java.lang.Float.isNaN;
20 
21 import android.content.Context;
22 import android.os.Bundle;
23 import android.os.Parcelable;
24 import android.util.AttributeSet;
25 import android.util.Log;
26 import android.view.MotionEvent;
27 import android.widget.FrameLayout;
28 
29 public abstract class PanelBar extends FrameLayout {
30     public static final boolean DEBUG = false;
31     public static final String TAG = PanelBar.class.getSimpleName();
32     private static final boolean SPEW = false;
33     private static final String PANEL_BAR_SUPER_PARCELABLE = "panel_bar_super_parcelable";
34     private static final String STATE = "state";
35     private boolean mBouncerShowing;
36     private boolean mExpanded;
37     protected float mPanelFraction;
38 
LOG(String fmt, Object... args)39     public static final void LOG(String fmt, Object... args) {
40         if (!DEBUG) return;
41         Log.v(TAG, String.format(fmt, args));
42     }
43 
44     public static final int STATE_CLOSED = 0;
45     public static final int STATE_OPENING = 1;
46     public static final int STATE_OPEN = 2;
47 
48     PanelViewController mPanel;
49     private int mState = STATE_CLOSED;
50     private boolean mTracking;
51 
go(int state)52     public void go(int state) {
53         if (DEBUG) LOG("go state: %d -> %d", mState, state);
54         mState = state;
55     }
56 
57     @Override
onSaveInstanceState()58     protected Parcelable onSaveInstanceState() {
59         Bundle bundle = new Bundle();
60         bundle.putParcelable(PANEL_BAR_SUPER_PARCELABLE, super.onSaveInstanceState());
61         bundle.putInt(STATE, mState);
62         return bundle;
63     }
64 
65     @Override
onRestoreInstanceState(Parcelable state)66     protected void onRestoreInstanceState(Parcelable state) {
67         if (state == null || !(state instanceof Bundle)) {
68             super.onRestoreInstanceState(state);
69             return;
70         }
71 
72         Bundle bundle = (Bundle) state;
73         super.onRestoreInstanceState(bundle.getParcelable(PANEL_BAR_SUPER_PARCELABLE));
74         if (((Bundle) state).containsKey(STATE)) {
75             go(bundle.getInt(STATE, STATE_CLOSED));
76         }
77     }
78 
PanelBar(Context context, AttributeSet attrs)79     public PanelBar(Context context, AttributeSet attrs) {
80         super(context, attrs);
81     }
82 
83     @Override
onFinishInflate()84     protected void onFinishInflate() {
85         super.onFinishInflate();
86     }
87 
88     /** Set the PanelViewController */
setPanel(PanelViewController pv)89     public void setPanel(PanelViewController pv) {
90         mPanel = pv;
91         pv.setBar(this);
92     }
93 
setBouncerShowing(boolean showing)94     public void setBouncerShowing(boolean showing) {
95         mBouncerShowing = showing;
96         int important = showing ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
97                 : IMPORTANT_FOR_ACCESSIBILITY_AUTO;
98 
99         setImportantForAccessibility(important);
100         updateVisibility();
101 
102         if (mPanel != null) mPanel.getView().setImportantForAccessibility(important);
103     }
104 
getExpansionFraction()105     public float getExpansionFraction() {
106         return mPanelFraction;
107     }
108 
isExpanded()109     public boolean isExpanded() {
110         return mExpanded;
111     }
112 
updateVisibility()113     protected void updateVisibility() {
114         mPanel.getView().setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE);
115     }
116 
shouldPanelBeVisible()117     protected boolean shouldPanelBeVisible() {
118         return mExpanded || mBouncerShowing;
119     }
120 
panelEnabled()121     public boolean panelEnabled() {
122         return true;
123     }
124 
125     @Override
onTouchEvent(MotionEvent event)126     public boolean onTouchEvent(MotionEvent event) {
127         // Allow subclasses to implement enable/disable semantics
128         if (!panelEnabled()) {
129             if (event.getAction() == MotionEvent.ACTION_DOWN) {
130                 Log.v(TAG, String.format("onTouch: all panels disabled, ignoring touch at (%d,%d)",
131                         (int) event.getX(), (int) event.getY()));
132             }
133             return false;
134         }
135 
136         if (event.getAction() == MotionEvent.ACTION_DOWN) {
137             final PanelViewController panel = mPanel;
138             if (panel == null) {
139                 // panel is not there, so we'll eat the gesture
140                 Log.v(TAG, String.format("onTouch: no panel for touch at (%d,%d)",
141                         (int) event.getX(), (int) event.getY()));
142                 return true;
143             }
144             boolean enabled = panel.isEnabled();
145             if (DEBUG) LOG("PanelBar.onTouch: state=%d ACTION_DOWN: panel %s %s", mState, panel,
146                     (enabled ? "" : " (disabled)"));
147             if (!enabled) {
148                 // panel is disabled, so we'll eat the gesture
149                 Log.v(TAG, String.format(
150                         "onTouch: panel (%s) is disabled, ignoring touch at (%d,%d)",
151                         panel, (int) event.getX(), (int) event.getY()));
152                 return true;
153             }
154         }
155         return mPanel == null || mPanel.getView().dispatchTouchEvent(event);
156     }
157 
panelScrimMinFractionChanged(float minFraction)158     public abstract void panelScrimMinFractionChanged(float minFraction);
159 
160     /**
161      * @param frac the fraction from the expansion in [0, 1]
162      * @param expanded whether the panel is currently expanded; this is independent from the
163      *                 fraction as the panel also might be expanded if the fraction is 0
164      */
panelExpansionChanged(float frac, boolean expanded)165     public void panelExpansionChanged(float frac, boolean expanded) {
166         if (isNaN(frac)) {
167             throw new IllegalArgumentException("frac cannot be NaN");
168         }
169         boolean fullyClosed = true;
170         boolean fullyOpened = false;
171         if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
172         PanelViewController pv = mPanel;
173         mExpanded = expanded;
174         mPanelFraction = frac;
175         updateVisibility();
176         // adjust any other panels that may be partially visible
177         if (expanded) {
178             if (mState == STATE_CLOSED) {
179                 go(STATE_OPENING);
180                 onPanelPeeked();
181             }
182             fullyClosed = false;
183             final float thisFrac = pv.getExpandedFraction();
184             if (SPEW) LOG("panelExpansionChanged:  -> %s: f=%.1f", pv.getName(), thisFrac);
185             fullyOpened = thisFrac >= 1f;
186         }
187         if (fullyOpened && !mTracking) {
188             go(STATE_OPEN);
189             onPanelFullyOpened();
190         } else if (fullyClosed && !mTracking && mState != STATE_CLOSED) {
191             go(STATE_CLOSED);
192             onPanelCollapsed();
193         }
194 
195         if (SPEW) LOG("panelExpansionChanged: end state=%d [%s%s ]", mState,
196                 fullyOpened?" fullyOpened":"", fullyClosed?" fullyClosed":"");
197     }
198 
collapsePanel(boolean animate, boolean delayed, float speedUpFactor)199     public void collapsePanel(boolean animate, boolean delayed, float speedUpFactor) {
200         boolean waiting = false;
201         PanelViewController pv = mPanel;
202         if (animate && !pv.isFullyCollapsed()) {
203             pv.collapse(delayed, speedUpFactor);
204             waiting = true;
205         } else {
206             pv.resetViews(false /* animate */);
207             pv.setExpandedFraction(0); // just in case
208             pv.cancelPeek();
209         }
210         if (DEBUG) LOG("collapsePanel: animate=%s waiting=%s", animate, waiting);
211         if (!waiting && mState != STATE_CLOSED) {
212             // it's possible that nothing animated, so we replicate the termination
213             // conditions of panelExpansionChanged here
214             go(STATE_CLOSED);
215             onPanelCollapsed();
216         }
217     }
218 
onPanelPeeked()219     public void onPanelPeeked() {
220         if (DEBUG) LOG("onPanelPeeked");
221     }
222 
isClosed()223     public boolean isClosed() {
224         return mState == STATE_CLOSED;
225     }
226 
onPanelCollapsed()227     public void onPanelCollapsed() {
228         if (DEBUG) LOG("onPanelCollapsed");
229     }
230 
onPanelFullyOpened()231     public void onPanelFullyOpened() {
232         if (DEBUG) LOG("onPanelFullyOpened");
233     }
234 
onTrackingStarted()235     public void onTrackingStarted() {
236         mTracking = true;
237     }
238 
onTrackingStopped(boolean expand)239     public void onTrackingStopped(boolean expand) {
240         mTracking = false;
241     }
242 
onExpandingFinished()243     public void onExpandingFinished() {
244         if (DEBUG) LOG("onExpandingFinished");
245     }
246 
onClosingFinished()247     public void onClosingFinished() {
248 
249     }
250 }
251