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