Java tutorial
/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.mozstumbler.client.subactivities; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v4.content.LocalBroadcastManager; import android.support.v4.view.MenuItemCompat; import android.support.v7.app.ActionBarActivity; import android.text.Html; import android.util.AttributeSet; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.ScrollView; import android.widget.TextView; import org.mozilla.mozstumbler.R; import org.mozilla.mozstumbler.service.AppGlobals; import org.mozilla.mozstumbler.service.core.logging.ClientLog; import org.mozilla.mozstumbler.svclocator.services.log.LoggerUtil; import java.lang.ref.WeakReference; import java.util.LinkedList; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentLinkedQueue; public class LogActivity extends ActionBarActivity { private static final int MAX_SIZE = 200; static ConsoleView sConsoleView; private static String LOG_TAG = LoggerUtil.makeLogTag(LogActivity.class); private static LinkedList<String> buffer = new LinkedList<String>(); private static int sLongLinesCounter; private static LogMessageReceiver sInstance; ConsoleView mConsoleView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_log); mConsoleView = (ConsoleView) findViewById(R.id.scrollview); } @Override protected void onResume() { super.onResume(); sConsoleView = mConsoleView; for (String s : buffer) { mConsoleView.println(s); } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.log_menu, menu); MenuItem m = menu.findItem(R.id.scroll_to_end); MenuItemCompat.setShowAsAction(m, MenuItemCompat.SHOW_AS_ACTION_ALWAYS); m = menu.findItem(R.id.scroll_to_start); MenuItemCompat.setShowAsAction(m, MenuItemCompat.SHOW_AS_ACTION_ALWAYS); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.scroll_to_start: this.mConsoleView.fullScroll(View.FOCUS_UP); return true; case R.id.scroll_to_end: this.mConsoleView.fullScroll(View.FOCUS_DOWN); return true; default: return super.onOptionsItemSelected(item); } } public static class LogMessageReceiver extends BroadcastReceiver { Timer mFlushMessagesTimer = new Timer(); AddToBufferOnMain mMainThreadHandler; LogMessageReceiver(Context context) { LocalBroadcastManager.getInstance(context).registerReceiver(this, new IntentFilter(AppGlobals.ACTION_GUI_LOG_MESSAGE)); final int kMillis = 1000 * 3; mFlushMessagesTimer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { mMainThreadHandler.obtainMessage().sendToTarget(); } }, kMillis, kMillis); } public static void createGlobalInstance(Context context) { sInstance = new LogMessageReceiver(context); sInstance.mMainThreadHandler = new AddToBufferOnMain(new WeakReference<LogMessageReceiver>(sInstance)); AppGlobals.guiLogMessageBuffer = new ConcurrentLinkedQueue<String>(); } void addMessageToBuffer(String s) { if (s == null) { return; } if (buffer.size() > MAX_SIZE) { buffer.removeFirst(); } // size of log is: 1000 * 30 + 200 * 470 = 30 kb + 94 kb, should be a very safe size final int kMaxCharsOfLongerLines = 1000; final int kMaxCharsOfTruncatedLine = 200; final int kLongLinesAllowedBeforeTruncate = 30; if (s.length() > kMaxCharsOfTruncatedLine) { sLongLinesCounter++; if (sLongLinesCounter == kLongLinesAllowedBeforeTruncate) { String msg = "LOG VIEWER REACHED " + kLongLinesAllowedBeforeTruncate + " LONG MESSAGES. TRUNCATING MESSAGES."; buffer.add(msg); if (sConsoleView != null) { sConsoleView.println(msg); } } } final boolean isTruncating = sLongLinesCounter >= kLongLinesAllowedBeforeTruncate; final int maxChars = isTruncating ? kMaxCharsOfTruncatedLine : kMaxCharsOfLongerLines; if (s.length() > maxChars && !s.startsWith(AppGlobals.NO_TRUNCATE_FLAG)) { // 1/3 of max length, ellipse, then last 2/3 of max length s = s.substring(0, maxChars / 3) + " ... " + s.substring(s.length() - 1 - maxChars * 2 / 3); } String prev = (buffer.size() > 0) ? buffer.getLast() : null; if (prev != null && prev.length() > 10 && s.length() > 10) { if (prev.substring(10).equals(s.substring(10))) { ClientLog.d(LOG_TAG, "Message is repeated: " + s); return; } } buffer.add(s); if (sConsoleView != null) { sConsoleView.println(s); } } @Override public void onReceive(Context c, Intent intent) { String s = intent.getStringExtra(AppGlobals.ACTION_GUI_LOG_MESSAGE_EXTRA); addMessageToBuffer(s); } // Ensure that the message buffer used by the GUI is accessed only on the main thread static class AddToBufferOnMain extends Handler { WeakReference<LogMessageReceiver> mParentClass; public AddToBufferOnMain(WeakReference<LogMessageReceiver> parent) { mParentClass = parent; } public void handleMessage(Message m) { String msg = null; do { msg = AppGlobals.guiLogMessageBuffer.poll(); if (mParentClass.get() != null) { mParentClass.get().addMessageToBuffer(msg); } } while (msg != null); } } } @Override protected void onPause() { super.onPause(); mConsoleView.clear(); sConsoleView = null; } public static class ConsoleView extends ScrollView { private static final String LOG_TAG = LoggerUtil.makeLogTag(ConsoleView.class); public TextView tv; boolean enable_scroll = true; public ConsoleView(Context context) { super(context); init(context); } public ConsoleView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public ConsoleView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } void init(Context context) { tv = new TextView(context); addView(tv); tv.setTextSize(13.0f); tv.setClickable(false); enableScroll(true); } public void enableScroll(boolean v) { this.enable_scroll = v; } public void print(String str) { tv.append(Html.fromHtml(str + "<br />")); if (enable_scroll) { scrollTo(0, tv.getBottom()); } } public void println(String str) { print(str + "\n"); } public void clear() { tv.setText(""); this.scrollTo(0, 0); } @Override protected void onScrollChanged(int x, int y, int oldx, int oldy) { super.onScrollChanged(x, y, oldx, oldy); int diff = tv.getHeight() - (y + getHeight()); boolean isAtBottom = diff <= 0; enableScroll(isAtBottom); } } }