1/* 2 * Copyright (C) 2009 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.browser; 18 19import android.content.Context; 20import android.database.DataSetObserver; 21import android.graphics.Color; 22import android.util.AttributeSet; 23import android.view.LayoutInflater; 24import android.view.View; 25import android.view.ViewGroup; 26import android.view.View.OnClickListener; 27import android.webkit.ConsoleMessage; 28import android.webkit.WebView; 29import android.widget.Button; 30import android.widget.EditText; 31import android.widget.LinearLayout; 32import android.widget.ListView; 33import android.widget.TextView; 34import android.widget.TwoLineListItem; 35 36import java.util.Vector; 37 38/* package */ class ErrorConsoleView extends LinearLayout { 39 40 /** 41 * Define some constants to describe the visibility of the error console. 42 */ 43 public static final int SHOW_MINIMIZED = 0; 44 public static final int SHOW_MAXIMIZED = 1; 45 public static final int SHOW_NONE = 2; 46 47 private TextView mConsoleHeader; 48 private ErrorConsoleListView mErrorList; 49 private LinearLayout mEvalJsViewGroup; 50 private EditText mEvalEditText; 51 private Button mEvalButton; 52 private WebView mWebView; 53 private int mCurrentShowState = SHOW_NONE; 54 55 private boolean mSetupComplete = false; 56 57 // Before we've been asked to display the console, cache any messages that should 58 // be added to the console. Then when we do display the console, add them to the view 59 // then. 60 private Vector<ConsoleMessage> mErrorMessageCache; 61 62 public ErrorConsoleView(Context context) { 63 super(context); 64 } 65 66 public ErrorConsoleView(Context context, AttributeSet attributes) { 67 super(context, attributes); 68 } 69 70 private void commonSetupIfNeeded() { 71 if (mSetupComplete) { 72 return; 73 } 74 75 LayoutInflater inflater = (LayoutInflater) getContext().getSystemService( 76 Context.LAYOUT_INFLATER_SERVICE); 77 inflater.inflate(R.layout.error_console, this); 78 79 // Get references to each ui element. 80 mConsoleHeader = (TextView) findViewById(R.id.error_console_header_id); 81 mErrorList = (ErrorConsoleListView) findViewById(R.id.error_console_list_id); 82 mEvalJsViewGroup = (LinearLayout) findViewById(R.id.error_console_eval_view_group_id); 83 mEvalEditText = (EditText) findViewById(R.id.error_console_eval_text_id); 84 mEvalButton = (Button) findViewById(R.id.error_console_eval_button_id); 85 86 mEvalButton.setOnClickListener(new OnClickListener() { 87 public void onClick(View v) { 88 // Send the javascript to be evaluated to webkit as a javascript: url 89 // TODO: Can we expose access to webkit's JS interpreter here and evaluate it that 90 // way? Note that this is called on the UI thread so we will need to post a message 91 // to the WebCore thread to implement this. 92 if (mWebView != null) { 93 mWebView.loadUrl("javascript:" + mEvalEditText.getText()); 94 } 95 96 mEvalEditText.setText(""); 97 } 98 }); 99 100 // Make clicking on the console title bar min/maximse it. 101 mConsoleHeader.setOnClickListener(new OnClickListener() { 102 public void onClick(View v) { 103 if (mCurrentShowState == SHOW_MINIMIZED) { 104 showConsole(SHOW_MAXIMIZED); 105 } else { 106 showConsole(SHOW_MINIMIZED); 107 } 108 } 109 }); 110 111 // Add any cached messages to the list now that we've assembled the view. 112 if (mErrorMessageCache != null) { 113 for (ConsoleMessage msg : mErrorMessageCache) { 114 mErrorList.addErrorMessage(msg); 115 } 116 mErrorMessageCache.clear(); 117 } 118 119 mSetupComplete = true; 120 } 121 122 /** 123 * Adds a message to the set of messages the console uses. 124 */ 125 public void addErrorMessage(ConsoleMessage consoleMessage) { 126 if (mSetupComplete) { 127 mErrorList.addErrorMessage(consoleMessage); 128 } else { 129 if (mErrorMessageCache == null) { 130 mErrorMessageCache = new Vector<ConsoleMessage>(); 131 } 132 mErrorMessageCache.add(consoleMessage); 133 } 134 } 135 136 /** 137 * Removes all error messages from the console. 138 */ 139 public void clearErrorMessages() { 140 if (mSetupComplete) { 141 mErrorList.clearErrorMessages(); 142 } else if (mErrorMessageCache != null) { 143 mErrorMessageCache.clear(); 144 } 145 } 146 147 /** 148 * Returns the current number of errors displayed in the console. 149 */ 150 public int numberOfErrors() { 151 if (mSetupComplete) { 152 return mErrorList.getCount(); 153 } else { 154 return (mErrorMessageCache == null) ? 0 : mErrorMessageCache.size(); 155 } 156 } 157 158 /** 159 * Sets the webview that this console is associated with. Currently this is used so 160 * we can call into webkit to evaluate JS expressions in the console. 161 */ 162 public void setWebView(WebView webview) { 163 mWebView = webview; 164 } 165 166 /** 167 * Sets the visibility state of the console. 168 */ 169 public void showConsole(int show_state) { 170 commonSetupIfNeeded(); 171 switch (show_state) { 172 case SHOW_MINIMIZED: 173 mConsoleHeader.setVisibility(View.VISIBLE); 174 mConsoleHeader.setText(R.string.error_console_header_text_minimized); 175 mErrorList.setVisibility(View.GONE); 176 mEvalJsViewGroup.setVisibility(View.GONE); 177 break; 178 179 case SHOW_MAXIMIZED: 180 mConsoleHeader.setVisibility(View.VISIBLE); 181 mConsoleHeader.setText(R.string.error_console_header_text_maximized); 182 mErrorList.setVisibility(View.VISIBLE); 183 mEvalJsViewGroup.setVisibility(View.VISIBLE); 184 break; 185 186 case SHOW_NONE: 187 mConsoleHeader.setVisibility(View.GONE); 188 mErrorList.setVisibility(View.GONE); 189 mEvalJsViewGroup.setVisibility(View.GONE); 190 break; 191 } 192 mCurrentShowState = show_state; 193 } 194 195 /** 196 * Returns the current visibility state of the console. 197 */ 198 public int getShowState() { 199 if (mSetupComplete) { 200 return mCurrentShowState; 201 } else { 202 return SHOW_NONE; 203 } 204 } 205 206 /** 207 * This class extends ListView to implement the View that will actually display the set of 208 * errors encountered on the current page. 209 */ 210 private static class ErrorConsoleListView extends ListView { 211 // An adapter for this View that contains a list of error messages. 212 private ErrorConsoleMessageList mConsoleMessages; 213 214 public ErrorConsoleListView(Context context, AttributeSet attributes) { 215 super(context, attributes); 216 mConsoleMessages = new ErrorConsoleMessageList(context); 217 setAdapter(mConsoleMessages); 218 } 219 220 public void addErrorMessage(ConsoleMessage consoleMessage) { 221 mConsoleMessages.add(consoleMessage); 222 setSelection(mConsoleMessages.getCount()); 223 } 224 225 public void clearErrorMessages() { 226 mConsoleMessages.clear(); 227 } 228 229 /** 230 * This class is an adapter for ErrorConsoleListView that contains the error console 231 * message data. 232 */ 233 private static class ErrorConsoleMessageList extends android.widget.BaseAdapter 234 implements android.widget.ListAdapter { 235 236 private Vector<ConsoleMessage> mMessages; 237 private LayoutInflater mInflater; 238 239 public ErrorConsoleMessageList(Context context) { 240 mMessages = new Vector<ConsoleMessage>(); 241 mInflater = (LayoutInflater)context.getSystemService( 242 Context.LAYOUT_INFLATER_SERVICE); 243 } 244 245 /** 246 * Add a new message to the list and update the View. 247 */ 248 public void add(ConsoleMessage consoleMessage) { 249 mMessages.add(consoleMessage); 250 notifyDataSetChanged(); 251 } 252 253 /** 254 * Remove all messages from the list and update the view. 255 */ 256 public void clear() { 257 mMessages.clear(); 258 notifyDataSetChanged(); 259 } 260 261 @Override 262 public boolean areAllItemsEnabled() { 263 return false; 264 } 265 266 @Override 267 public boolean isEnabled(int position) { 268 return false; 269 } 270 271 public long getItemId(int position) { 272 return position; 273 } 274 275 public Object getItem(int position) { 276 return mMessages.get(position); 277 } 278 279 public int getCount() { 280 return mMessages.size(); 281 } 282 283 @Override 284 public boolean hasStableIds() { 285 return true; 286 } 287 288 /** 289 * Constructs a TwoLineListItem for the error at position. 290 */ 291 public View getView(int position, View convertView, ViewGroup parent) { 292 View view; 293 ConsoleMessage error = mMessages.get(position); 294 295 if (error == null) { 296 return null; 297 } 298 299 if (convertView == null) { 300 view = mInflater.inflate(android.R.layout.two_line_list_item, parent, false); 301 } else { 302 view = convertView; 303 } 304 305 TextView headline = (TextView) view.findViewById(android.R.id.text1); 306 TextView subText = (TextView) view.findViewById(android.R.id.text2); 307 headline.setText(error.sourceId() + ":" + error.lineNumber()); 308 subText.setText(error.message()); 309 switch (error.messageLevel()) { 310 case ERROR: 311 subText.setTextColor(Color.RED); 312 break; 313 case WARNING: 314 // Orange 315 subText.setTextColor(Color.rgb(255,192,0)); 316 break; 317 case TIP: 318 subText.setTextColor(Color.BLUE); 319 break; 320 default: 321 subText.setTextColor(Color.LTGRAY); 322 break; 323 } 324 return view; 325 } 326 327 } 328 } 329} 330