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