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