summaryrefslogtreecommitdiffstats
path: root/src/com/android/browser/ErrorConsoleView.java
blob: ca5fed466a1b444d56c70389f2ff01ec9ea65d46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.browser;

import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.webkit.ConsoleMessage;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.TwoLineListItem;

import java.util.Vector;

/* package */ class ErrorConsoleView extends LinearLayout {

    /**
     * Define some constants to describe the visibility of the error console.
     */
    public static final int SHOW_MINIMIZED = 0;
    public static final int SHOW_MAXIMIZED = 1;
    public static final int SHOW_NONE      = 2;

    private TextView mConsoleHeader;
    private ErrorConsoleListView mErrorList;
    private LinearLayout mEvalJsViewGroup;
    private EditText mEvalEditText;
    private Button mEvalButton;
    private WebView mWebView;
    private int mCurrentShowState = SHOW_NONE;

    private boolean mSetupComplete = false;

    // Before we've been asked to display the console, cache any messages that should
    // be added to the console. Then when we do display the console, add them to the view
    // then.
    private Vector<ConsoleMessage> mErrorMessageCache;

    public ErrorConsoleView(Context context) {
        super(context);
    }

    public ErrorConsoleView(Context context, AttributeSet attributes) {
        super(context, attributes);
    }

    private void commonSetupIfNeeded() {
        if (mSetupComplete) {
            return;
        }

        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
                Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.error_console, this);

        // Get references to each ui element.
        mConsoleHeader = (TextView) findViewById(R.id.error_console_header_id);
        mErrorList = (ErrorConsoleListView) findViewById(R.id.error_console_list_id);
        mEvalJsViewGroup = (LinearLayout) findViewById(R.id.error_console_eval_view_group_id);
        mEvalEditText = (EditText) findViewById(R.id.error_console_eval_text_id);
        mEvalButton = (Button) findViewById(R.id.error_console_eval_button_id);

        mEvalButton.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                // Send the javascript to be evaluated to webkit as a javascript: url
                // TODO: Can we expose access to webkit's JS interpreter here and evaluate it that
                // way? Note that this is called on the UI thread so we will need to post a message
                // to the WebCore thread to implement this.
                if (mWebView != null) {
                    mWebView.loadUrl("javascript:" + mEvalEditText.getText());
                }

                mEvalEditText.setText("");
            }
        });

        // Make clicking on the console title bar min/maximse it.
        mConsoleHeader.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                if (mCurrentShowState == SHOW_MINIMIZED) {
                    showConsole(SHOW_MAXIMIZED);
                } else {
                    showConsole(SHOW_MINIMIZED);
                }
            }
        });

        // Add any cached messages to the list now that we've assembled the view.
        if (mErrorMessageCache != null) {
            for (ConsoleMessage msg : mErrorMessageCache) {
                mErrorList.addErrorMessage(msg);
            }
            mErrorMessageCache.clear();
        }

        mSetupComplete = true;
    }

    /**
     * Adds a message to the set of messages the console uses.
     */
    public void addErrorMessage(ConsoleMessage consoleMessage) {
        if (mSetupComplete) {
            mErrorList.addErrorMessage(consoleMessage);
        } else {
            if (mErrorMessageCache == null) {
                mErrorMessageCache = new Vector<ConsoleMessage>();
            }
            mErrorMessageCache.add(consoleMessage);
        }
    }

    /**
     * Removes all error messages from the console.
     */
    public void clearErrorMessages() {
        if (mSetupComplete) {
            mErrorList.clearErrorMessages();
        } else if (mErrorMessageCache != null) {
            mErrorMessageCache.clear();
        }
    }

    /**
     * Returns the current number of errors displayed in the console.
     */
    public int numberOfErrors() {
        if (mSetupComplete) {
            return mErrorList.getCount();
        } else {
            return (mErrorMessageCache == null) ? 0 : mErrorMessageCache.size();
        }
    }

    /**
     * Sets the webview that this console is associated with. Currently this is used so
     * we can call into webkit to evaluate JS expressions in the console.
     */
    public void setWebView(WebView webview) {
        mWebView = webview;
    }

    /**
     * Sets the visibility state of the console.
     */
    public void showConsole(int show_state) {
        commonSetupIfNeeded();
        switch (show_state) {
            case SHOW_MINIMIZED:
                mConsoleHeader.setVisibility(View.VISIBLE);
                mConsoleHeader.setText(R.string.error_console_header_text_minimized);
                mErrorList.setVisibility(View.GONE);
                mEvalJsViewGroup.setVisibility(View.GONE);
                break;

            case SHOW_MAXIMIZED:
                mConsoleHeader.setVisibility(View.VISIBLE);
                mConsoleHeader.setText(R.string.error_console_header_text_maximized);
                mErrorList.setVisibility(View.VISIBLE);
                mEvalJsViewGroup.setVisibility(View.VISIBLE);
                break;

            case SHOW_NONE:
                mConsoleHeader.setVisibility(View.GONE);
                mErrorList.setVisibility(View.GONE);
                mEvalJsViewGroup.setVisibility(View.GONE);
                break;
        }
        mCurrentShowState = show_state;
    }

    /**
     * Returns the current visibility state of the console.
     */
    public int getShowState() {
        if (mSetupComplete) {
            return mCurrentShowState;
        } else {
            return SHOW_NONE;
        }
    }

    /**
     * This class extends ListView to implement the View that will actually display the set of
     * errors encountered on the current page.
     */
    private static class ErrorConsoleListView extends ListView {
        // An adapter for this View that contains a list of error messages.
        private ErrorConsoleMessageList mConsoleMessages;

        public ErrorConsoleListView(Context context, AttributeSet attributes) {
            super(context, attributes);
            mConsoleMessages = new ErrorConsoleMessageList(context);
            setAdapter(mConsoleMessages);
        }

        public void addErrorMessage(ConsoleMessage consoleMessage) {
            mConsoleMessages.add(consoleMessage);
            setSelection(mConsoleMessages.getCount());
        }

        public void clearErrorMessages() {
            mConsoleMessages.clear();
        }

        /**
         * This class is an adapter for ErrorConsoleListView that contains the error console
         * message data.
         */
        private static class ErrorConsoleMessageList extends android.widget.BaseAdapter
                implements android.widget.ListAdapter {

            private Vector<ConsoleMessage> mMessages;
            private LayoutInflater mInflater;

            public ErrorConsoleMessageList(Context context) {
                mMessages = new Vector<ConsoleMessage>();
                mInflater = (LayoutInflater)context.getSystemService(
                        Context.LAYOUT_INFLATER_SERVICE);
            }

            /**
             * Add a new message to the list and update the View.
             */
            public void add(ConsoleMessage consoleMessage) {
                mMessages.add(consoleMessage);
                notifyDataSetChanged();
            }

            /**
             * Remove all messages from the list and update the view.
             */
            public void clear() {
                mMessages.clear();
                notifyDataSetChanged();
            }

            @Override
            public boolean areAllItemsEnabled() {
                return false;
            }

            @Override
            public boolean isEnabled(int position) {
                return false;
            }

            public long getItemId(int position) {
                return position;
            }

            public Object getItem(int position) {
                return mMessages.get(position);
            }

            public int getCount() {
                return mMessages.size();
            }

            @Override
            public boolean hasStableIds() {
                return true;
            }

            /**
             * Constructs a TwoLineListItem for the error at position.
             */
            public View getView(int position, View convertView, ViewGroup parent) {
                View view;
                ConsoleMessage error = mMessages.get(position);

                if (error == null) {
                    return null;
                }

                if (convertView == null) {
                    view = mInflater.inflate(android.R.layout.two_line_list_item, parent, false);
                } else {
                    view = convertView;
                }

                TextView headline = (TextView) view.findViewById(android.R.id.text1);
                TextView subText = (TextView) view.findViewById(android.R.id.text2);
                headline.setText(error.sourceId() + ":" + error.lineNumber());
                subText.setText(error.message());
                switch (error.messageLevel()) {
                    case ERROR:
                        subText.setTextColor(Color.RED);
                        break;
                    case WARNING:
                        // Orange
                        subText.setTextColor(Color.rgb(255,192,0));
                        break;
                    case TIP:
                        subText.setTextColor(Color.BLUE);
                        break;
                    default:
                        subText.setTextColor(Color.LTGRAY);
                        break;
                }
                return view;
            }

        }
    }
}