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     PanelView 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 
setPanel(PanelView pv)88     public void setPanel(PanelView pv) {
89         mPanel = pv;
90         pv.setBar(this);
91     }
92 
setBouncerShowing(boolean showing)93     public void setBouncerShowing(boolean showing) {
94         mBouncerShowing = showing;
95         int important = showing ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
96                 : IMPORTANT_FOR_ACCESSIBILITY_AUTO;
97 
98         setImportantForAccessibility(important);
99         updateVisibility();
100 
101         if (mPanel != null) mPanel.setImportantForAccessibility(important);
102     }
103 
getExpansionFraction()104     public float getExpansionFraction() {
105         return mPanelFraction;
106     }
107 
isExpanded()108     public boolean isExpanded() {
109         return mExpanded;
110     }
111 
updateVisibility()112     protected void updateVisibility() {
113         mPanel.setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE);
114     }
115 
shouldPanelBeVisible()116     protected boolean shouldPanelBeVisible() {
117         return mExpanded || mBouncerShowing;
118     }
119 
panelEnabled()120     public boolean panelEnabled() {
121         return true;
122     }
123 
124     @Override
onTouchEvent(MotionEvent event)125     public boolean onTouchEvent(MotionEvent event) {
126         // Allow subclasses to implement enable/disable semantics
127         if (!panelEnabled()) {
128             if (event.getAction() == MotionEvent.ACTION_DOWN) {
129                 Log.v(TAG, String.format("onTouch: all panels disabled, ignoring touch at (%d,%d)",
130                         (int) event.getX(), (int) event.getY()));
131             }
132             return false;
133         }
134 
135         if (event.getAction() == MotionEvent.ACTION_DOWN) {
136             final PanelView panel = mPanel;
137             if (panel == null) {
138                 // panel is not there, so we'll eat the gesture
139                 Log.v(TAG, String.format("onTouch: no panel for touch at (%d,%d)",
140                         (int) event.getX(), (int) event.getY()));
141                 return true;
142             }
143             boolean enabled = panel.isEnabled();
144             if (DEBUG) LOG("PanelBar.onTouch: state=%d ACTION_DOWN: panel %s %s", mState, panel,
145                     (enabled ? "" : " (disabled)"));
146             if (!enabled) {
147                 // panel is disabled, so we'll eat the gesture
148                 Log.v(TAG, String.format(
149                         "onTouch: panel (%s) is disabled, ignoring touch at (%d,%d)",
150                         panel, (int) event.getX(), (int) event.getY()));
151                 return true;
152             }
153         }
154         return mPanel == null || mPanel.onTouchEvent(event);
155     }
156 
panelScrimMinFractionChanged(float minFraction)157     public abstract void panelScrimMinFractionChanged(float minFraction);
158 
159     /**
160      * @param frac the fraction from the expansion in [0, 1]
161      * @param expanded whether the panel is currently expanded; this is independent from the
162      *                 fraction as the panel also might be expanded if the fraction is 0
163      */
panelExpansionChanged(float frac, boolean expanded)164     public void panelExpansionChanged(float frac, boolean expanded) {
165         if (isNaN(frac)) {
166             throw new IllegalArgumentException("frac cannot be NaN");
167         }
168         boolean fullyClosed = true;
169         boolean fullyOpened = false;
170         if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
171         PanelView pv = mPanel;
172         mExpanded = expanded;
173         mPanelFraction = frac;
174         updateVisibility();
175         // adjust any other panels that may be partially visible
176         if (expanded) {
177             if (mState == STATE_CLOSED) {
178                 go(STATE_OPENING);
179                 onPanelPeeked();
180             }
181             fullyClosed = false;
182             final float thisFrac = pv.getExpandedFraction();
183             if (SPEW) LOG("panelExpansionChanged:  -> %s: f=%.1f", pv.getName(), thisFrac);
184             fullyOpened = thisFrac >= 1f;
185         }
186         if (fullyOpened && !mTracking) {
187             go(STATE_OPEN);
188             onPanelFullyOpened();
189         } else if (fullyClosed && !mTracking && mState != STATE_CLOSED) {
190             go(STATE_CLOSED);
191             onPanelCollapsed();
192         }
193 
194         if (SPEW) LOG("panelExpansionChanged: end state=%d [%s%s ]", mState,
195                 fullyOpened?" fullyOpened":"", fullyClosed?" fullyClosed":"");
196     }
197 
collapsePanel(boolean animate, boolean delayed, float speedUpFactor)198     public void collapsePanel(boolean animate, boolean delayed, float speedUpFactor) {
199         boolean waiting = false;
200         PanelView pv = mPanel;
201         if (animate && !pv.isFullyCollapsed()) {
202             pv.collapse(delayed, speedUpFactor);
203             waiting = true;
204         } else {
205             pv.resetViews(false /* animate */);
206             pv.setExpandedFraction(0); // just in case
207             pv.cancelPeek();
208         }
209         if (DEBUG) LOG("collapsePanel: animate=%s waiting=%s", animate, waiting);
210         if (!waiting && mState != STATE_CLOSED) {
211             // it's possible that nothing animated, so we replicate the termination
212             // conditions of panelExpansionChanged here
213             go(STATE_CLOSED);
214             onPanelCollapsed();
215         }
216     }
217 
onPanelPeeked()218     public void onPanelPeeked() {
219         if (DEBUG) LOG("onPanelPeeked");
220     }
221 
isClosed()222     public boolean isClosed() {
223         return mState == STATE_CLOSED;
224     }
225 
onPanelCollapsed()226     public void onPanelCollapsed() {
227         if (DEBUG) LOG("onPanelCollapsed");
228     }
229 
onPanelFullyOpened()230     public void onPanelFullyOpened() {
231         if (DEBUG) LOG("onPanelFullyOpened");
232     }
233 
onTrackingStarted()234     public void onTrackingStarted() {
235         mTracking = true;
236     }
237 
onTrackingStopped(boolean expand)238     public void onTrackingStopped(boolean expand) {
239         mTracking = false;
240     }
241 
onExpandingFinished()242     public void onExpandingFinished() {
243         if (DEBUG) LOG("onExpandingFinished");
244     }
245 
onClosingFinished()246     public void onClosingFinished() {
247 
248     }
249 }
250