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
17package com.android.internal.widget;
18
19import com.android.internal.app.ActionBarImpl;
20
21import android.content.Context;
22import android.content.res.TypedArray;
23import android.graphics.Rect;
24import android.util.AttributeSet;
25import android.util.Log;
26import android.view.View;
27import android.widget.FrameLayout;
28
29/**
30 * Special layout for the containing of an overlay action bar (and its
31 * content) to correctly handle fitting system windows when the content
32 * has request that its layout ignore them.
33 */
34public class ActionBarOverlayLayout extends FrameLayout {
35    private int mActionBarHeight;
36    private ActionBarImpl mActionBar;
37    private int mWindowVisibility = View.VISIBLE;
38    private View mContent;
39    private View mActionBarTop;
40    private ActionBarContainer mContainerView;
41    private ActionBarView mActionView;
42    private View mActionBarBottom;
43    private int mLastSystemUiVisibility;
44    private final Rect mZeroRect = new Rect(0, 0, 0, 0);
45
46    static final int[] mActionBarSizeAttr = new int [] {
47            com.android.internal.R.attr.actionBarSize
48    };
49
50    public ActionBarOverlayLayout(Context context) {
51        super(context);
52        init(context);
53    }
54
55    public ActionBarOverlayLayout(Context context, AttributeSet attrs) {
56        super(context, attrs);
57        init(context);
58    }
59
60    private void init(Context context) {
61        TypedArray ta = getContext().getTheme().obtainStyledAttributes(mActionBarSizeAttr);
62        mActionBarHeight = ta.getDimensionPixelSize(0, 0);
63        ta.recycle();
64    }
65
66    public void setActionBar(ActionBarImpl impl) {
67        mActionBar = impl;
68        if (getWindowToken() != null) {
69            // This is being initialized after being added to a window;
70            // make sure to update all state now.
71            mActionBar.setWindowVisibility(mWindowVisibility);
72            if (mLastSystemUiVisibility != 0) {
73                int newVis = mLastSystemUiVisibility;
74                onWindowSystemUiVisibilityChanged(newVis);
75                requestFitSystemWindows();
76            }
77        }
78    }
79
80    public void setShowingForActionMode(boolean showing) {
81        if (showing) {
82            // Here's a fun hack: if the status bar is currently being hidden,
83            // and the application has asked for stable content insets, then
84            // we will end up with the action mode action bar being shown
85            // without the status bar, but moved below where the status bar
86            // would be.  Not nice.  Trying to have this be positioned
87            // correctly is not easy (basically we need yet *another* content
88            // inset from the window manager to know where to put it), so
89            // instead we will just temporarily force the status bar to be shown.
90            if ((getWindowSystemUiVisibility() & (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
91                    | SYSTEM_UI_FLAG_LAYOUT_STABLE))
92                    == (SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | SYSTEM_UI_FLAG_LAYOUT_STABLE)) {
93                setDisabledSystemUiVisibility(SYSTEM_UI_FLAG_FULLSCREEN);
94            }
95        } else {
96            setDisabledSystemUiVisibility(0);
97        }
98    }
99
100    @Override
101    public void onWindowSystemUiVisibilityChanged(int visible) {
102        super.onWindowSystemUiVisibilityChanged(visible);
103        pullChildren();
104        final int diff = mLastSystemUiVisibility ^ visible;
105        mLastSystemUiVisibility = visible;
106        final boolean barVisible = (visible&SYSTEM_UI_FLAG_FULLSCREEN) == 0;
107        final boolean wasVisible = mActionBar != null ? mActionBar.isSystemShowing() : true;
108        if (mActionBar != null) {
109            if (barVisible) mActionBar.showForSystem();
110            else mActionBar.hideForSystem();
111        }
112        if ((diff&SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
113            if (mActionBar != null) {
114                requestFitSystemWindows();
115            }
116        }
117    }
118
119    @Override
120    protected void onWindowVisibilityChanged(int visibility) {
121        super.onWindowVisibilityChanged(visibility);
122        mWindowVisibility = visibility;
123        if (mActionBar != null) {
124            mActionBar.setWindowVisibility(visibility);
125        }
126    }
127
128    private boolean applyInsets(View view, Rect insets, boolean left, boolean top,
129            boolean bottom, boolean right) {
130        boolean changed = false;
131        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams)view.getLayoutParams();
132        if (left && lp.leftMargin != insets.left) {
133            changed = true;
134            lp.leftMargin = insets.left;
135        }
136        if (top && lp.topMargin != insets.top) {
137            changed = true;
138            lp.topMargin = insets.top;
139        }
140        if (right && lp.rightMargin != insets.right) {
141            changed = true;
142            lp.rightMargin = insets.right;
143        }
144        if (bottom && lp.bottomMargin != insets.bottom) {
145            changed = true;
146            lp.bottomMargin = insets.bottom;
147        }
148        return changed;
149    }
150
151    @Override
152    protected boolean fitSystemWindows(Rect insets) {
153        pullChildren();
154
155        final int vis = getWindowSystemUiVisibility();
156        final boolean stable = (vis & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0;
157
158        // The top and bottom action bars are always within the content area.
159        boolean changed = applyInsets(mActionBarTop, insets, true, true, false, true);
160        if (mActionBarBottom != null) {
161            changed |= applyInsets(mActionBarBottom, insets, true, false, true, true);
162        }
163
164        // If the window has not requested system UI layout flags, we need to
165        // make sure its content is not being covered by system UI...  though it
166        // will still be covered by the action bar since they have requested it to
167        // overlay.
168        if ((vis & SYSTEM_UI_LAYOUT_FLAGS) == 0) {
169            changed |= applyInsets(mContent, insets, true, true, true, true);
170            // The insets are now consumed.
171            insets.set(0, 0, 0, 0);
172        } else {
173            changed |= applyInsets(mContent, mZeroRect, true, true, true, true);
174        }
175
176
177        if (stable || mActionBarTop.getVisibility() == VISIBLE) {
178            // The action bar creates additional insets for its content to use.
179            insets.top += mActionBarHeight;
180        }
181
182        if (mActionBar != null && mActionBar.hasNonEmbeddedTabs()) {
183            View tabs = mContainerView.getTabContainer();
184            if (stable || (tabs != null && tabs.getVisibility() == VISIBLE)) {
185                // If tabs are not embedded, adjust insets to account for them.
186                insets.top += mActionBarHeight;
187            }
188        }
189
190        if (mActionView.isSplitActionBar()) {
191            if (stable || (mActionBarBottom != null
192                    && mActionBarBottom.getVisibility() == VISIBLE)) {
193                // If action bar is split, adjust buttom insets for it.
194                insets.bottom += mActionBarHeight;
195            }
196        }
197
198        if (changed) {
199            requestLayout();
200        }
201
202        return super.fitSystemWindows(insets);
203    }
204
205    void pullChildren() {
206        if (mContent == null) {
207            mContent = findViewById(com.android.internal.R.id.content);
208            mActionBarTop = findViewById(com.android.internal.R.id.top_action_bar);
209            mContainerView = (ActionBarContainer)findViewById(
210                    com.android.internal.R.id.action_bar_container);
211            mActionView = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
212            mActionBarBottom = findViewById(com.android.internal.R.id.split_action_bar);
213        }
214    }
215}
216