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