1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.chrome.browser.infobar; 6 7 import android.content.Context; 8 import android.view.View; 9 10 import com.google.common.annotations.VisibleForTesting; 11 12 import org.chromium.base.CalledByNative; 13 import org.chromium.chrome.R; 14 15 /** 16 * The base class for all InfoBar classes. 17 * Note that infobars expire by default when a new navigation occurs. 18 * Make sure to use setExpireOnNavigation(false) if you want an infobar to be sticky. 19 */ 20 public abstract class InfoBar implements InfoBarView { 21 private static final String TAG = "InfoBar"; 22 23 /** 24 * Possible labels of all the infobar buttons. 25 * 26 * Make sure this set of values is aligned with the C++ correspondent in 27 * infobar_android.h 28 */ 29 public static final int ACTION_TYPE_NONE = 0; 30 31 // Confirm infobar 32 public static final int ACTION_TYPE_OK = 1; 33 public static final int ACTION_TYPE_CANCEL = 2; 34 35 // Translate infobar 36 public static final int ACTION_TYPE_TRANSLATE = 3; 37 public static final int ACTION_TYPE_TRANSLATE_SHOW_ORIGINAL = 4; 38 39 private final int mIconDrawableId; 40 private final CharSequence mMessage; 41 42 private InfoBarListeners.Dismiss mListener; 43 private ContentWrapperView mContentView; 44 private InfoBarContainer mContainer; 45 private Context mContext; 46 47 private boolean mExpireOnNavigation; 48 private boolean mIsDismissed; 49 private boolean mControlsEnabled; 50 51 // This cannot be private until the swap in place infrastructure is 52 // improved since subclasses need to access a possibly replaced native 53 // pointer. 54 protected long mNativeInfoBarPtr; 55 56 // Used by tests to reference infobars. 57 private final int mId; 58 private static int sIdCounter = 0; generateId()59 private static int generateId() { 60 return sIdCounter++; 61 } 62 63 /** 64 * @param listener Listens to when buttons have been clicked on the InfoBar. 65 * @param iconDrawableId ID of the resource to use for the Icon. If 0, no icon will be shown. 66 * @param message The message to show in the infobar. 67 */ InfoBar(InfoBarListeners.Dismiss listener, int iconDrawableId, CharSequence message)68 public InfoBar(InfoBarListeners.Dismiss listener, int iconDrawableId, CharSequence message) { 69 mListener = listener; 70 mId = generateId(); 71 mIconDrawableId = iconDrawableId; 72 mMessage = message; 73 mExpireOnNavigation = true; 74 } 75 76 /** 77 * @return The message shown in the infobar, useful for accessibility. 78 */ getMessage()79 public CharSequence getMessage() { 80 return mMessage; 81 } 82 83 /** 84 * Stores a pointer to the native-side counterpart of this InfoBar. 85 * @param nativeInfoBarPtr Pointer to the NativeInfoBar. 86 */ setNativeInfoBar(long nativeInfoBarPtr)87 protected void setNativeInfoBar(long nativeInfoBarPtr) { 88 if (nativeInfoBarPtr != 0) { 89 // The native code takes care of expiring infobars on navigations. 90 mExpireOnNavigation = false; 91 mNativeInfoBarPtr = nativeInfoBarPtr; 92 } 93 } 94 95 /** 96 * Change the pointer to the native-side counterpart of this InfoBar. Native-side code is 97 * responsible for managing the cleanup of the pointer. 98 * @param newInfoBarPtr Pointer to the NativeInfoBar. 99 */ replaceNativePointer(long newInfoBarPtr)100 protected void replaceNativePointer(long newInfoBarPtr) { 101 mNativeInfoBarPtr = newInfoBarPtr; 102 } 103 104 /** 105 * Determine if the infobar should be dismissed when a new page starts loading. Calling 106 * setExpireOnNavigation(true/false) causes this method always to return true/false. 107 * This only applies to java-only infobars. C++ infobars will use the same logic 108 * as other platforms so they are not attempted to be dismissed twice. 109 * It should really be removed once all infobars have a C++ counterpart. 110 */ shouldExpire()111 public final boolean shouldExpire() { 112 return mExpireOnNavigation && mNativeInfoBarPtr == 0; 113 } 114 115 // Sets whether the bar should be dismissed when a navigation occurs. setExpireOnNavigation(boolean expireOnNavigation)116 public void setExpireOnNavigation(boolean expireOnNavigation) { 117 mExpireOnNavigation = expireOnNavigation; 118 } 119 120 /** 121 * @return true if this java infobar owns this {@code nativePointer} 122 */ ownsNativeInfoBar(long nativePointer)123 boolean ownsNativeInfoBar(long nativePointer) { 124 return mNativeInfoBarPtr == nativePointer; 125 } 126 127 /** 128 * @return whether or not the InfoBar has been dismissed. 129 */ isDismissed()130 protected boolean isDismissed() { 131 return mIsDismissed; 132 } 133 134 /** 135 * Sets the Context used when creating the InfoBar. 136 */ setContext(Context context)137 protected void setContext(Context context) { 138 mContext = context; 139 } 140 141 /** 142 * @return The Context used to create the InfoBar. This will be null until the InfoBar is added 143 * to the InfoBarContainer, and should never be null afterward. 144 */ getContext()145 protected Context getContext() { 146 return mContext; 147 } 148 149 /** 150 * Creates the View that represents the InfoBar. 151 * @return The View representing the InfoBar. 152 */ createView()153 protected final View createView() { 154 assert mContext != null; 155 156 InfoBarLayout layout = new InfoBarLayout(mContext, this, mIconDrawableId, mMessage); 157 createContent(layout); 158 return layout; 159 } 160 161 /** 162 * Used to close a java only infobar. 163 */ dismissJavaOnlyInfoBar()164 public void dismissJavaOnlyInfoBar() { 165 assert mNativeInfoBarPtr == 0; 166 if (closeInfoBar() && mListener != null) { 167 mListener.onInfoBarDismissed(this); 168 } 169 } 170 171 /** 172 * @return whether the infobar actually needed closing. 173 */ 174 @CalledByNative closeInfoBar()175 public boolean closeInfoBar() { 176 if (!mIsDismissed) { 177 mIsDismissed = true; 178 if (!mContainer.hasBeenDestroyed()) { 179 // If the container was destroyed, it's already been emptied of all its infobars. 180 mContainer.removeInfoBar(this); 181 } 182 return true; 183 } 184 return false; 185 } 186 getContentWrapper(boolean createIfNotFound)187 protected ContentWrapperView getContentWrapper(boolean createIfNotFound) { 188 if (mContentView == null && createIfNotFound) { 189 mContentView = new ContentWrapperView(getContext(), this, createView()); 190 mContentView.setFocusable(false); 191 } 192 return mContentView; 193 } 194 getInfoBarContainer()195 protected InfoBarContainer getInfoBarContainer() { 196 return mContainer; 197 } 198 199 /** 200 * @return The content view for the info bar. 201 */ getContentWrapper()202 public ContentWrapperView getContentWrapper() { 203 return getContentWrapper(true); 204 } 205 setInfoBarContainer(InfoBarContainer container)206 void setInfoBarContainer(InfoBarContainer container) { 207 mContainer = container; 208 } 209 areControlsEnabled()210 public boolean areControlsEnabled() { 211 return mControlsEnabled; 212 } 213 214 @Override setControlsEnabled(boolean state)215 public void setControlsEnabled(boolean state) { 216 mControlsEnabled = state; 217 218 // Disable all buttons on the infobar. 219 if (mContentView != null) { 220 View closeButton = mContentView.findViewById(R.id.infobar_close_button); 221 View primaryButton = mContentView.findViewById(R.id.button_primary); 222 View secondaryButton = mContentView.findViewById(R.id.button_secondary); 223 View tertiaryButton = mContentView.findViewById(R.id.button_tertiary); 224 if (closeButton != null) closeButton.setEnabled(state); 225 if (primaryButton != null) primaryButton.setEnabled(state); 226 if (secondaryButton != null) secondaryButton.setEnabled(state); 227 if (tertiaryButton != null) tertiaryButton.setEnabled(state); 228 } 229 } 230 231 @Override onButtonClicked(boolean isPrimaryButton)232 public void onButtonClicked(boolean isPrimaryButton) { 233 } 234 235 @Override onLinkClicked()236 public void onLinkClicked() { 237 nativeOnLinkClicked(mNativeInfoBarPtr); 238 } 239 240 @Override createContent(InfoBarLayout layout)241 public void createContent(InfoBarLayout layout) { 242 } 243 244 /** 245 * Returns the id of the tab this infobar is showing into. 246 */ getTabId()247 public int getTabId() { 248 return mContainer.getTabId(); 249 } 250 251 @VisibleForTesting getId()252 public int getId() { 253 return mId; 254 } 255 256 @VisibleForTesting setDismissedListener(InfoBarListeners.Dismiss listener)257 public void setDismissedListener(InfoBarListeners.Dismiss listener) { 258 mListener = listener; 259 } 260 nativeOnLinkClicked(long nativeInfoBarAndroid)261 protected native void nativeOnLinkClicked(long nativeInfoBarAndroid); nativeOnButtonClicked( long nativeInfoBarAndroid, int action, String actionValue)262 protected native void nativeOnButtonClicked( 263 long nativeInfoBarAndroid, int action, String actionValue); nativeOnCloseButtonClicked(long nativeInfoBarAndroid)264 protected native void nativeOnCloseButtonClicked(long nativeInfoBarAndroid); 265 } 266