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