package com.xypower.mpapp; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.os.FileObserver; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.Messenger; import android.text.method.ScrollingMovementMethod; import android.util.Log; import android.view.MenuItem; import com.xypower.common.MicroPhotoContext; import com.xypower.mpapp.databinding.ActivityLogBinding; import com.xypower.mpapp.utils.RandomReader; import java.io.File; public class LogActivity extends AppCompatActivity { public static final String TAG = "MPLOG"; public static final int MSG_WHAT_LOG_OBSERVER = MicroPhotoService.MSG_WHAT_MAX + 10; public static final int MAX_LOG_LINES = 480; public static final int MIN_LOG_LINES = 120; private ActivityLogBinding binding; private Handler mHandler = null; private LogFileObserver mLogFileObserver = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityLogBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); getSupportActionBar().setDisplayHomeAsUpEnabled(true); binding.logs.setText(""); binding.logs.setMovementMethod(ScrollingMovementMethod.getInstance()); binding.logs.setScrollbarFadingEnabled(false); mHandler = new Handler(Looper.myLooper()) { @Override public void handleMessage(@NonNull Message msg) { switch (msg.what) { case MSG_WHAT_LOG_OBSERVER: { byte[] bytes = (byte[])msg.obj; int bytesRead = msg.arg1; String log = null; try { log = new String(bytes, 0, bytesRead, "UTF-8"); } catch (Exception e) { e.printStackTrace(); } if (log != null) { binding.logs.append(log); int offset = binding.logs.getLineCount() * binding.logs.getLineHeight(); if (offset > binding.logs.getHeight()) { binding.logs.scrollTo(0, offset - binding.logs.getHeight() + binding.logs.getLineHeight()); } } } break; } } }; } @Override protected void onResume() { // call the superclass method first super.onResume(); try { String logFilePath = MicroPhotoContext.buildAppDir(this.getApplicationContext()); logFilePath += "logs"; File file = new File(logFilePath); if (!file.exists()) { file.mkdirs(); } logFilePath += "/log.txt"; file = new File(logFilePath); if (!file.exists()) { file.createNewFile(); } mLogFileObserver = new LogFileObserver(logFilePath); mLogFileObserver.startWatching(); Log.i(TAG, "Log Observer Started"); int lines = binding.logs.getLineCount(); if (lines > MAX_LOG_LINES) { int excessLineNumber = lines - MIN_LOG_LINES; int eolIndex = -1; CharSequence charSequence = binding.logs.getText(); for (int i = 0; i < excessLineNumber; i++) { do { eolIndex++; } while (eolIndex < charSequence.length() && charSequence.charAt(eolIndex) != '\n'); } if (eolIndex < charSequence.length()) { binding.logs.getEditableText().delete(0, eolIndex + 1); } } } catch (Exception e) { e.printStackTrace(); } } @Override protected void onPause() { // call the superclass method first super.onPause(); try { if (mLogFileObserver != null) { mLogFileObserver.stopWatching(); mLogFileObserver = null; Log.i(TAG, "Log Observer Stopped"); } } catch (Exception e) { e.printStackTrace(); } } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: // todo: goto back activity from here finish(); return true; default: return super.onOptionsItemSelected(item); } } private class LogFileObserver extends FileObserver { private long mOffset = 0; private String mPath = null; public LogFileObserver(String path) { super(path, FileObserver.MODIFY | FileObserver.CREATE); mPath = path; File file = new File(path); if (file.exists()) { mOffset = file.length(); } } @Override public void onEvent(int event, String s) { int e = event & FileObserver.ALL_EVENTS; if (e == FileObserver.MODIFY) { File file = new File(mPath); long newOffset = file.length(); if (newOffset > mOffset) { RandomReader reader = new RandomReader(mPath, mOffset); byte[] bytes = new byte[(int)(newOffset - mOffset)]; int bytesRead = reader.read(bytes); mOffset += bytesRead; Message msg = Message.obtain(); msg.what = MSG_WHAT_LOG_OBSERVER; msg.obj = bytes; msg.arg1 = bytesRead; mHandler.sendMessage(msg); } } else if (e == FileObserver.CREATE) { mOffset = 0; } } } }