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 android.content.Context;
20 import android.util.AttributeSet;
21 import android.util.Log;
22 import android.view.MotionEvent;
23 import android.view.View;
24 import android.widget.FrameLayout;
25 
26 import java.util.ArrayList;
27 
28 public class PanelBar extends FrameLayout {
29     public static final boolean DEBUG = false;
30     public static final String TAG = PanelBar.class.getSimpleName();
LOG(String fmt, Object... args)31     public static final void LOG(String fmt, Object... args) {
32         if (!DEBUG) return;
33         Log.v(TAG, String.format(fmt, args));
34     }
35 
36     public static final int STATE_CLOSED = 0;
37     public static final int STATE_OPENING = 1;
38     public static final int STATE_OPEN = 2;
39 
40     PanelHolder mPanelHolder;
41     ArrayList<PanelView> mPanels = new ArrayList<PanelView>();
42     PanelView mTouchingPanel;
43     private int mState = STATE_CLOSED;
44     private boolean mTracking;
45 
46     float mPanelExpandedFractionSum;
47 
go(int state)48     public void go(int state) {
49         if (DEBUG) LOG("go state: %d -> %d", mState, state);
50         mState = state;
51     }
52 
PanelBar(Context context, AttributeSet attrs)53     public PanelBar(Context context, AttributeSet attrs) {
54         super(context, attrs);
55     }
56 
57     @Override
onFinishInflate()58     protected void onFinishInflate() {
59         super.onFinishInflate();
60     }
61 
addPanel(PanelView pv)62     public void addPanel(PanelView pv) {
63         mPanels.add(pv);
64         pv.setBar(this);
65     }
66 
setPanelHolder(PanelHolder ph)67     public void setPanelHolder(PanelHolder ph) {
68         if (ph == null) {
69             Log.e(TAG, "setPanelHolder: null PanelHolder", new Throwable());
70             return;
71         }
72         ph.setBar(this);
73         mPanelHolder = ph;
74         final int N = ph.getChildCount();
75         for (int i=0; i<N; i++) {
76             final View v = ph.getChildAt(i);
77             if (v != null && v instanceof PanelView) {
78                 addPanel((PanelView) v);
79             }
80         }
81     }
82 
getBarHeight()83     public float getBarHeight() {
84         return getMeasuredHeight();
85     }
86 
selectPanelForTouch(MotionEvent touch)87     public PanelView selectPanelForTouch(MotionEvent touch) {
88         final int N = mPanels.size();
89         return mPanels.get((int)(N * touch.getX() / getMeasuredWidth()));
90     }
91 
panelsEnabled()92     public boolean panelsEnabled() {
93         return true;
94     }
95 
96     @Override
onTouchEvent(MotionEvent event)97     public boolean onTouchEvent(MotionEvent event) {
98         // Allow subclasses to implement enable/disable semantics
99         if (!panelsEnabled()) {
100             if (event.getAction() == MotionEvent.ACTION_DOWN) {
101                 Log.v(TAG, String.format("onTouch: all panels disabled, ignoring touch at (%d,%d)",
102                         (int) event.getX(), (int) event.getY()));
103             }
104             return false;
105         }
106 
107         // figure out which panel needs to be talked to here
108         if (event.getAction() == MotionEvent.ACTION_DOWN) {
109             final PanelView panel = selectPanelForTouch(event);
110             if (panel == null) {
111                 // panel is not there, so we'll eat the gesture
112                 Log.v(TAG, String.format("onTouch: no panel for touch at (%d,%d)",
113                         (int) event.getX(), (int) event.getY()));
114                 mTouchingPanel = null;
115                 return true;
116             }
117             boolean enabled = panel.isEnabled();
118             if (DEBUG) LOG("PanelBar.onTouch: state=%d ACTION_DOWN: panel %s %s", mState, panel,
119                     (enabled ? "" : " (disabled)"));
120             if (!enabled) {
121                 // panel is disabled, so we'll eat the gesture
122                 Log.v(TAG, String.format(
123                         "onTouch: panel (%s) is disabled, ignoring touch at (%d,%d)",
124                         panel, (int) event.getX(), (int) event.getY()));
125                 mTouchingPanel = null;
126                 return true;
127             }
128             startOpeningPanel(panel);
129         }
130         final boolean result = mTouchingPanel != null
131                 ? mTouchingPanel.onTouchEvent(event)
132                 : true;
133         return result;
134     }
135 
136     // called from PanelView when self-expanding, too
startOpeningPanel(PanelView panel)137     public void startOpeningPanel(PanelView panel) {
138         if (DEBUG) LOG("startOpeningPanel: " + panel);
139         mTouchingPanel = panel;
140         mPanelHolder.setSelectedPanel(mTouchingPanel);
141         for (PanelView pv : mPanels) {
142             if (pv != panel) {
143                 pv.collapse(false /* delayed */);
144             }
145         }
146     }
147 
148     /**
149      * @param panel the panel which changed its expansion state
150      * @param frac the fraction from the expansion in [0, 1]
151      * @param expanded whether the panel is currently expanded; this is independent from the
152      *                 fraction as the panel also might be expanded if the fraction is 0
153      */
panelExpansionChanged(PanelView panel, float frac, boolean expanded)154     public void panelExpansionChanged(PanelView panel, float frac, boolean expanded) {
155         boolean fullyClosed = true;
156         PanelView fullyOpenedPanel = null;
157         if (DEBUG) LOG("panelExpansionChanged: start state=%d panel=%s", mState, panel.getName());
158         mPanelExpandedFractionSum = 0f;
159         for (PanelView pv : mPanels) {
160             boolean visible = pv.getExpandedHeight() > 0;
161             pv.setVisibility(visible ? View.VISIBLE : View.GONE);
162             // adjust any other panels that may be partially visible
163             if (expanded) {
164                 if (mState == STATE_CLOSED) {
165                     go(STATE_OPENING);
166                     onPanelPeeked();
167                 }
168                 fullyClosed = false;
169                 final float thisFrac = pv.getExpandedFraction();
170                 mPanelExpandedFractionSum += (visible ? thisFrac : 0);
171                 if (DEBUG) LOG("panelExpansionChanged:  -> %s: f=%.1f", pv.getName(), thisFrac);
172                 if (panel == pv) {
173                     if (thisFrac == 1f) fullyOpenedPanel = panel;
174                 }
175             }
176         }
177         mPanelExpandedFractionSum /= mPanels.size();
178         if (fullyOpenedPanel != null && !mTracking) {
179             go(STATE_OPEN);
180             onPanelFullyOpened(fullyOpenedPanel);
181         } else if (fullyClosed && !mTracking && mState != STATE_CLOSED) {
182             go(STATE_CLOSED);
183             onAllPanelsCollapsed();
184         }
185 
186         if (DEBUG) LOG("panelExpansionChanged: end state=%d [%s%s ]", mState,
187                 (fullyOpenedPanel!=null)?" fullyOpened":"", fullyClosed?" fullyClosed":"");
188     }
189 
collapseAllPanels(boolean animate)190     public void collapseAllPanels(boolean animate) {
191         boolean waiting = false;
192         for (PanelView pv : mPanels) {
193             if (animate && !pv.isFullyCollapsed()) {
194                 pv.collapse(true /* delayed */);
195                 waiting = true;
196             } else {
197                 pv.resetViews();
198                 pv.setExpandedFraction(0); // just in case
199                 pv.setVisibility(View.GONE);
200                 pv.cancelPeek();
201             }
202         }
203         if (DEBUG) LOG("collapseAllPanels: animate=%s waiting=%s", animate, waiting);
204         if (!waiting && mState != STATE_CLOSED) {
205             // it's possible that nothing animated, so we replicate the termination
206             // conditions of panelExpansionChanged here
207             go(STATE_CLOSED);
208             onAllPanelsCollapsed();
209         }
210     }
211 
onPanelPeeked()212     public void onPanelPeeked() {
213         if (DEBUG) LOG("onPanelPeeked");
214     }
215 
onAllPanelsCollapsed()216     public void onAllPanelsCollapsed() {
217         if (DEBUG) LOG("onAllPanelsCollapsed");
218     }
219 
onPanelFullyOpened(PanelView openPanel)220     public void onPanelFullyOpened(PanelView openPanel) {
221         if (DEBUG) LOG("onPanelFullyOpened");
222     }
223 
onTrackingStarted(PanelView panel)224     public void onTrackingStarted(PanelView panel) {
225         mTracking = true;
226         if (DEBUG && panel != mTouchingPanel) {
227             LOG("shouldn't happen: onTrackingStarted(%s) != mTouchingPanel(%s)",
228                     panel, mTouchingPanel);
229         }
230     }
231 
onTrackingStopped(PanelView panel, boolean expand)232     public void onTrackingStopped(PanelView panel, boolean expand) {
233         mTracking = false;
234     }
235 
onExpandingFinished()236     public void onExpandingFinished() {
237 
238     }
239 
onClosingFinished()240     public void onClosingFinished() {
241 
242     }
243 }
244