package net.i2p.android.router.log; import android.app.Activity; import android.content.Context; import android.os.Build; import android.os.Bundle; import android.support.v4.app.ListFragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import net.i2p.I2PAppContext; import net.i2p.android.router.R; import java.util.ArrayList; import java.util.List; public class LogFragment extends ListFragment implements LoaderManager.LoaderCallbacks<List<String>> { public static final String LOG_LEVEL = "log_level"; public static final String LOG_LEVEL_ERROR = "ERROR"; /** * The serialization (saved instance state) Bundle key representing the * activated item position. Only used on tablets. */ private static final String STATE_ACTIVATED_POSITION = "activated_position"; private static final int LEVEL_ERROR = 1; private static final int LEVEL_ALL = 2; OnEntrySelectedListener mEntrySelectedCallback; private final List<String> mLogEntries = new ArrayList<>(); private LogAdapter mAdapter; private TextView mHeaderView; private String mLogLevel; /** * The current activated item position. Only used on tablets. */ private int mActivatedPosition = ListView.INVALID_POSITION; private boolean mActivateOnItemClick = false; private MenuItem mCopyLogs; // Container Activity must implement this interface public interface OnEntrySelectedListener { void onEntrySelected(String entry); } public static LogFragment newInstance(String level) { LogFragment f = new LogFragment(); Bundle args = new Bundle(); args.putString(LOG_LEVEL, level); f.setArguments(args); return f; } @Override public void onAttach(Activity activity) { super.onAttach(activity); // This makes sure that the container activity has implemented // the callback interface. If not, it throws an exception try { mEntrySelectedCallback = (OnEntrySelectedListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnEntrySelectedListener"); } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // Restore the previously serialized activated item position. if (savedInstanceState != null && savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) { setActivatedPosition(savedInstanceState .getInt(STATE_ACTIVATED_POSITION)); } // When setting CHOICE_MODE_SINGLE, ListView will automatically // give items the 'activated' state when touched. getListView().setChoiceMode( mActivateOnItemClick ? ListView.CHOICE_MODE_SINGLE : ListView.CHOICE_MODE_NONE); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mAdapter = new LogAdapter(getActivity()); mLogLevel = getArguments().getString(LOG_LEVEL); // set the header mHeaderView = (TextView) getActivity().getLayoutInflater().inflate(R.layout.logs_header, null); getListView().addHeaderView(mHeaderView, "", false); setListAdapter(mAdapter); I2PAppContext ctx = I2PAppContext.getCurrentContext(); if (ctx != null) { setEmptyText(getString(LOG_LEVEL_ERROR.equals(mLogLevel) ? R.string.no_error_messages : R.string.no_messages)); setListShown(false); getLoaderManager().initLoader(LOG_LEVEL_ERROR.equals(mLogLevel) ? LEVEL_ERROR : LEVEL_ALL, null, this); } else setEmptyText(getResources().getString( R.string.router_not_running)); } @Override public void onListItemClick(ListView parent, View view, int pos, long id) { super.onListItemClick(parent, view, pos, id); String entry = mAdapter.getItem(pos - 1); mEntrySelectedCallback.onEntrySelected(entry); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); if (mActivatedPosition != ListView.INVALID_POSITION) { // Serialize and persist the activated item position. outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition); } } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.fragment_log_actions, menu); mCopyLogs = menu.findItem(R.id.action_copy_logs); } @Override public void onPrepareOptionsMenu(Menu menu) { mCopyLogs.setVisible(I2PAppContext.getCurrentContext() != null); } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle presses on the action bar items switch (item.getItemId()) { case R.id.action_copy_logs: String logText = ""; synchronized (mLogEntries) { for (String logEntry : mLogEntries) { logText += logEntry; } } boolean isError = LOG_LEVEL_ERROR.equals(mLogLevel); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { android.text.ClipboardManager clipboard = (android.text.ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE); clipboard.setText(logText); } else { android.content.ClipboardManager clipboard = (android.content.ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE); android.content.ClipData clip = android.content.ClipData.newPlainText( isError ? getString(R.string.i2p_android_error_logs) : getString(R.string.i2p_android_logs), logText); clipboard.setPrimaryClip(clip); } int textId; if (isError) textId = R.string.error_logs_copied_to_clipboard; else textId = R.string.logs_copied_to_clipboard; Toast.makeText(getActivity(), textId, Toast.LENGTH_SHORT).show(); return true; default: return super.onOptionsItemSelected(item); } } /** * Turns on activate-on-click mode. When this mode is on, list items will be * given the 'activated' state when touched. */ public void setActivateOnItemClick(boolean activateOnItemClick) { mActivateOnItemClick = activateOnItemClick; } private void setActivatedPosition(int position) { if (position == ListView.INVALID_POSITION) { getListView().setItemChecked(mActivatedPosition, false); } else { getListView().setItemChecked(position, true); } mActivatedPosition = position; } private static String getHeader(Context ctx, int sz, boolean errorsOnly) { if (sz > 0) return ctx.getResources().getQuantityString(errorsOnly ? R.plurals.log_error_messages : R.plurals.log_messages, sz, sz); else return ctx.getString(errorsOnly ? R.string.no_error_messages : R.string.no_messages); } // LoaderManager.LoaderCallbacks<List<String>> public Loader<List<String>> onCreateLoader(int id, Bundle args) { return new LogLoader(getActivity(), I2PAppContext.getCurrentContext(), mLogLevel); } public void onLoadFinished(Loader<List<String>> loader, List<String> data) { if (loader.getId() == (LOG_LEVEL_ERROR.equals(mLogLevel) ? LEVEL_ERROR : LEVEL_ALL)) { synchronized (mLogEntries) { mLogEntries.clear(); mLogEntries.addAll(data); } mAdapter.setData(data); String header = getHeader(getActivity(), data.size(), (LOG_LEVEL_ERROR.equals(mLogLevel))); mHeaderView.setText(header); if (isResumed()) { setListShown(true); } else { setListShownNoAnimation(true); } } } public void onLoaderReset(Loader<List<String>> loader) { if (loader.getId() == (LOG_LEVEL_ERROR.equals(mLogLevel) ? LEVEL_ERROR : LEVEL_ALL)) { mAdapter.setData(null); } } }