• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 
17 package com.googlecode.android_scripting.activity;
18 
19 import android.app.ListActivity;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.os.Bundle;
23 import android.text.ClipboardManager;
24 import android.util.TypedValue;
25 import android.view.Menu;
26 import android.view.MenuItem;
27 import android.view.View;
28 import android.view.ViewGroup;
29 import android.widget.BaseAdapter;
30 import android.widget.TextView;
31 import android.widget.Toast;
32 
33 import com.googlecode.android_scripting.ActivityFlinger;
34 import com.googlecode.android_scripting.Log;
35 import com.googlecode.android_scripting.Process;
36 import com.googlecode.android_scripting.R;
37 
38 import java.io.BufferedReader;
39 import java.io.File;
40 import java.io.IOException;
41 import java.io.InputStreamReader;
42 import java.util.LinkedList;
43 import java.util.List;
44 
45 public class LogcatViewer extends ListActivity {
46 
47   private List<String> mLogcatMessages;
48   private int mOldLastPosition;
49   private LogcatViewerAdapter mAdapter;
50   private Process mLogcatProcess;
51 
52   private static enum MenuId {
53     HELP, PREFERENCES, JUMP_TO_BOTTOM, SHARE, COPY;
getId()54     public int getId() {
55       return ordinal() + Menu.FIRST;
56     }
57   }
58 
59   private class LogcatWatcher implements Runnable {
60     @Override
run()61     public void run() {
62       mLogcatProcess = new Process();
63       mLogcatProcess.setBinary(new File("/system/bin/logcat"));
64       mLogcatProcess.start(null);
65       try {
66         BufferedReader br = new BufferedReader(new InputStreamReader(mLogcatProcess.getIn()));
67         while (true) {
68           final String line = br.readLine();
69           if (line == null) {
70             break;
71           }
72           runOnUiThread(new Runnable() {
73             @Override
74             public void run() {
75               mLogcatMessages.add(line);
76               mAdapter.notifyDataSetInvalidated();
77               // This logic performs what transcriptMode="normal" should do. Since that doesn't seem
78               // to work, we do it this way.
79               int lastVisiblePosition = getListView().getLastVisiblePosition();
80               int lastPosition = mLogcatMessages.size() - 1;
81               if (lastVisiblePosition == mOldLastPosition || lastVisiblePosition == -1) {
82                 getListView().setSelection(lastPosition);
83               }
84               mOldLastPosition = lastPosition;
85             }
86           });
87         }
88       } catch (IOException e) {
89         Log.e("Failed to read from logcat process.", e);
90       } finally {
91         mLogcatProcess.kill();
92       }
93     }
94   }
95 
96   @Override
onCreate(Bundle savedInstanceState)97   protected void onCreate(Bundle savedInstanceState) {
98     super.onCreate(savedInstanceState);
99     CustomizeWindow.requestCustomTitle(this, "Logcat", R.layout.logcat_viewer);
100     mLogcatMessages = new LinkedList<String>();
101     mOldLastPosition = 0;
102     mAdapter = new LogcatViewerAdapter();
103     setListAdapter(mAdapter);
104     ActivityFlinger.attachView(getListView(), this);
105     ActivityFlinger.attachView(getWindow().getDecorView(), this);
106   }
107 
108   @Override
onCreateOptionsMenu(Menu menu)109   public boolean onCreateOptionsMenu(Menu menu) {
110     menu.add(Menu.NONE, MenuId.PREFERENCES.getId(), Menu.NONE, "Preferences").setIcon(
111         android.R.drawable.ic_menu_preferences);
112     menu.add(Menu.NONE, MenuId.JUMP_TO_BOTTOM.getId(), Menu.NONE, "Jump to Bottom").setIcon(
113         android.R.drawable.ic_menu_revert);
114     menu.add(Menu.NONE, MenuId.SHARE.getId(), Menu.NONE, "Share").setIcon(
115         android.R.drawable.ic_menu_share);
116     menu.add(Menu.NONE, MenuId.COPY.getId(), Menu.NONE, "Copy").setIcon(
117         android.R.drawable.ic_menu_edit);
118     return super.onCreateOptionsMenu(menu);
119   }
120 
getAsString()121   private String getAsString() {
122     StringBuilder builder = new StringBuilder();
123     for (String message : mLogcatMessages) {
124       builder.append(message + "\n");
125     }
126     return builder.toString();
127   }
128 
129   @Override
onOptionsItemSelected(MenuItem item)130   public boolean onOptionsItemSelected(MenuItem item) {
131     int itemId = item.getItemId();
132     if (itemId == MenuId.JUMP_TO_BOTTOM.getId()) {
133       getListView().setSelection(mLogcatMessages.size() - 1);
134     } else if (itemId == MenuId.PREFERENCES.getId()) {
135       startActivity(new Intent(this, Preferences.class));
136     } else if (itemId == MenuId.SHARE.getId()) {
137       Intent intent = new Intent(Intent.ACTION_SEND);
138       intent.putExtra(Intent.EXTRA_TEXT, getAsString().toString());
139       intent.putExtra(Intent.EXTRA_SUBJECT, "Logcat Dump");
140       intent.setType("text/plain");
141       startActivity(Intent.createChooser(intent, "Send Logcat to:"));
142     } else if (itemId == MenuId.COPY.getId()) {
143       ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
144       clipboard.setText(getAsString());
145       Toast.makeText(this, "Copied to clipboard", Toast.LENGTH_SHORT).show();
146     }
147     return super.onOptionsItemSelected(item);
148   }
149 
150   @Override
onStart()151   protected void onStart() {
152     mLogcatMessages.clear();
153     Thread logcatWatcher = new Thread(new LogcatWatcher());
154     logcatWatcher.setPriority(Thread.NORM_PRIORITY - 1);
155     logcatWatcher.start();
156     mAdapter.notifyDataSetInvalidated();
157     super.onStart();
158   }
159 
160   @Override
onPause()161   protected void onPause() {
162     super.onPause();
163     mLogcatProcess.kill();
164   }
165 
166   private class LogcatViewerAdapter extends BaseAdapter {
167 
168     @Override
getCount()169     public int getCount() {
170       return mLogcatMessages.size();
171     }
172 
173     @Override
getItem(int position)174     public Object getItem(int position) {
175       return mLogcatMessages.get(position);
176     }
177 
178     @Override
getItemId(int position)179     public long getItemId(int position) {
180       return position;
181     }
182 
183     @Override
getView(final int position, View convertView, ViewGroup parent)184     public View getView(final int position, View convertView, ViewGroup parent) {
185       TextView view = new TextView(LogcatViewer.this);
186       view.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
187       view.setText(mLogcatMessages.get(position));
188       return view;
189     }
190   }
191 }
192