Java tutorial
/* Copyright (c) 2010-2016 Darshan-Josiah Barber This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ package com.stylovid.fastbattery; import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.database.Cursor; import android.database.CursorWrapper; import android.os.Bundle; import android.os.Environment; import android.support.v4.app.ActivityCompat; import android.support.v4.app.DialogFragment; import android.support.v4.app.ListFragment; import android.support.v4.content.ContextCompat; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.CursorAdapter; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; public class LogViewFragment extends ListFragment { private static PersistentFragment pfrag; public LogDatabase logs; private Col col; private Cursor completeCursor; private Cursor filteredCursor; private Cursor timeDeltaCursor; private LayoutInflater mInflater; private LogAdapter mAdapter; private TextView header_text; private Boolean convertF; private boolean reversed; private boolean noDB; private static final String LOG_TAG = "BatteryBot"; private static final String[] CSV_ORDER = { LogDatabase.KEY_TIME, LogDatabase.KEY_STATUS_CODE, LogDatabase.KEY_CHARGE, LogDatabase.KEY_TEMPERATURE, LogDatabase.KEY_VOLTAGE }; private static final String P_WRITE_STORAGE = android.Manifest.permission.WRITE_EXTERNAL_STORAGE; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.onCreateView(inflater, container, savedInstanceState); mInflater = inflater; View logs_header = View.inflate(getActivity(), R.layout.logs_header, null); header_text = (TextView) logs_header.findViewById(R.id.header_text); ListView lv = (ListView) view.findViewById(android.R.id.list); lv.addHeaderView(logs_header, null, false); lv.setFastScrollEnabled(true); if (noDB) return view; setHeaderText(); setListAdapter(mAdapter); return view; } @Override public void onAttach(android.app.Activity a) { super.onAttach(a); pfrag = PersistentFragment.getInstance(getFragmentManager()); logs = new LogDatabase(getActivity().getApplicationContext()); completeCursor = logs.getAllLogs(false); if (completeCursor == null) { noDB = true; return; } timeDeltaCursor = new TimeDeltaCursor(completeCursor); filteredCursor = new FilteredCursor(timeDeltaCursor); mAdapter = new LogAdapter(a, filteredCursor); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); convertF = pfrag.settings.getBoolean(SettingsActivity.KEY_CONVERT_F, pfrag.res.getBoolean(R.bool.default_convert_to_fahrenheit)); col = new Col(); if (!pfrag.sp_main.getBoolean("log_filters_migrated_to_sp_main", false)) migrateFiltersToSpMain(); } private void migrateFiltersToSpMain() { SharedPreferences.Editor spm_editor = pfrag.sp_main.edit(); for (int i = 0; i < pfrag.str.log_filter_pref_keys.length; i++) { spm_editor.putBoolean(pfrag.str.log_filter_pref_keys[i], pfrag.settings.getBoolean(pfrag.str.log_filter_pref_keys[i], true)); } Str.apply(spm_editor.putBoolean("log_filters_migrated_to_sp_main", true)); } @Override public void onDestroy() { super.onDestroy(); if (completeCursor != null) completeCursor.close(); logs.close(); } @Override public void onResume() { super.onResume(); pfrag.setLVF(this); convertF = pfrag.settings.getBoolean(SettingsActivity.KEY_CONVERT_F, pfrag.res.getBoolean(R.bool.default_convert_to_fahrenheit)); } @Override public void onPause() { super.onPause(); pfrag.setLVF(null); } public static class ConfirmClearLogsDialogFragment extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { return new AlertDialog.Builder(getActivity()).setTitle(pfrag.res.getString(R.string.confirm_clear_logs)) .setPositiveButton(pfrag.res.getString(R.string.yes), new DialogInterface.OnClickListener() { public void onClick(DialogInterface di, int id) { LogViewFragment lvf = (LogViewFragment) getTargetFragment(); lvf.logs.clearAllLogs(); lvf.reloadList(false); di.cancel(); } }) .setNegativeButton(pfrag.res.getString(R.string.cancel), new DialogInterface.OnClickListener() { public void onClick(DialogInterface di, int id) { di.cancel(); } }).create(); } } public static class ConfigureLogFilterDialogFragment extends DialogFragment { final boolean[] checked_items = new boolean[pfrag.str.log_filter_pref_keys.length]; @Override public Dialog onCreateDialog(Bundle savedInstanceState) { for (int i = 0; i < checked_items.length; i++) { checked_items[i] = pfrag.sp_main.getBoolean(pfrag.str.log_filter_pref_keys[i], true); } return new AlertDialog.Builder(getActivity()) .setTitle(pfrag.res.getString(R.string.configure_log_filter)) .setMultiChoiceItems(R.array.log_filters, checked_items, new DialogInterface.OnMultiChoiceClickListener() { @Override public void onClick(DialogInterface di, int id, boolean isChecked) { checked_items[id] = isChecked; LogViewFragment lvf = (LogViewFragment) getTargetFragment(); lvf.setFilters(checked_items); } }) .setPositiveButton(pfrag.res.getString(R.string.okay), new DialogInterface.OnClickListener() { public void onClick(DialogInterface di, int id) { di.cancel(); } }).create(); } } private void setFilters(boolean[] checked_items) { SharedPreferences.Editor spm_editor = pfrag.sp_main.edit(); for (int i = 0; i < checked_items.length; i++) { spm_editor.putBoolean(pfrag.str.log_filter_pref_keys[i], checked_items[i]); } Str.apply(spm_editor); reloadList(false); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.logs, menu); } @Override public void onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); // According to Play Developer Console, filteredCursor can be null here, even though it shouldn't be... // I guess onPrepareOptionsMenu() can be called before onCreate()? if (filteredCursor == null) { menu.findItem(R.id.menu_clear).setEnabled(false); menu.findItem(R.id.menu_export).setEnabled(false); menu.findItem(R.id.menu_reverse).setEnabled(false); return; } switch (filteredCursor.getCount()) { case 0: menu.findItem(R.id.menu_clear).setEnabled(false); menu.findItem(R.id.menu_export).setEnabled(false); menu.findItem(R.id.menu_reverse).setEnabled(false); break; case 1: menu.findItem(R.id.menu_clear).setEnabled(true); menu.findItem(R.id.menu_export).setEnabled(true); menu.findItem(R.id.menu_reverse).setEnabled(false); break; default: menu.findItem(R.id.menu_clear).setEnabled(true); menu.findItem(R.id.menu_export).setEnabled(true); menu.findItem(R.id.menu_reverse).setEnabled(true); } } @Override public boolean onOptionsItemSelected(MenuItem item) { DialogFragment df; switch (item.getItemId()) { case R.id.menu_clear: df = new ConfirmClearLogsDialogFragment(); df.setTargetFragment(this, 0); df.show(getFragmentManager(), "TODO: What is this string for?"); return true; case R.id.menu_log_filter: df = new ConfigureLogFilterDialogFragment(); df.setTargetFragment(this, 0); df.show(getFragmentManager(), "TODO: What is this string for?2"); return true; case R.id.menu_export: exportCSV(); return true; case R.id.menu_reverse: reversed = (reversed) ? false : true; reloadList(true); return true; default: return super.onOptionsItemSelected(item); } } public void reloadList(Boolean newQuery) { if (newQuery) { completeCursor.close(); completeCursor = logs.getAllLogs(reversed); timeDeltaCursor = new TimeDeltaCursor(completeCursor); filteredCursor = new FilteredCursor(timeDeltaCursor); mAdapter.changeCursor(filteredCursor); } else { filteredCursor.requery(); mAdapter.notifyDataSetChanged(); } setHeaderText(); } private void setHeaderText() { int count = filteredCursor.getCount(); if (count == 0) header_text.setText(pfrag.str.logs_empty); else header_text.setText(pfrag.str.n_log_items(count)); } public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { switch (requestCode) { case BatteryInfoActivity.PR_LVF_WRITE_STORAGE: { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) exportCSV(); else Toast.makeText(getActivity(), pfrag.str.no_storage_permission, Toast.LENGTH_SHORT).show(); return; } } } public void batteryInfoUpdated() { reloadList(false); } private void exportCSV() { if (ContextCompat.checkSelfPermission(getActivity(), P_WRITE_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(getActivity(), new String[] { P_WRITE_STORAGE }, BatteryInfoActivity.PR_LVF_WRITE_STORAGE); return; } String state = Environment.getExternalStorageState(); if (state != null && state.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) { Toast.makeText(getActivity(), pfrag.str.read_only_storage, Toast.LENGTH_SHORT).show(); return; } else if (state == null || !state.equals(Environment.MEDIA_MOUNTED)) { Toast.makeText(getActivity(), pfrag.str.inaccessible_w_reason + state, Toast.LENGTH_SHORT).show(); return; } Date d = new Date(); String csvFileName = "FastBattery-Logs-" + d.getTime() + ".csv"; File root = Environment.getExternalStorageDirectory(); File csvFile = new File(root, csvFileName); String[] csvFields = { pfrag.str.date, pfrag.str.time, pfrag.str.status, pfrag.str.charge, pfrag.str.temperature, pfrag.str.temperature_f, pfrag.str.voltage }; try { if (!csvFile.createNewFile() || !csvFile.canWrite()) { Toast.makeText(getActivity(), pfrag.str.inaccessible_storage, Toast.LENGTH_SHORT).show(); return; } BufferedWriter buf = new BufferedWriter(new FileWriter(csvFile)); int cols = csvFields.length; int i; for (i = 0; i < cols; i++) { buf.write(csvFields[i]); if (i != cols - 1) buf.write(","); } buf.write("\r\n"); int statusCode; int[] statusCodes; int status, plugged, status_age; String s; for (completeCursor.moveToFirst(); !completeCursor.isAfterLast(); completeCursor.moveToNext()) { cols = CSV_ORDER.length; for (i = 0; i < cols; i++) { if (CSV_ORDER[i].equals(LogDatabase.KEY_TIME)) { d.setTime(completeCursor.getLong(mAdapter.timeIndex)); buf.write(mAdapter.dateFormat.format(d) + "," + mAdapter.timeFormat.format(d) + ","); } else if (CSV_ORDER[i].equals(LogDatabase.KEY_STATUS_CODE)) { statusCode = completeCursor.getInt(mAdapter.statusCodeIndex); statusCodes = LogDatabase.decodeStatus(statusCode); status = statusCodes[0]; plugged = statusCodes[1]; status_age = statusCodes[2]; if (status == LogDatabase.STATUS_BOOT_COMPLETED) s = pfrag.str.status_boot_completed; else if (status_age == LogDatabase.STATUS_OLD) s = pfrag.str.log_statuses_old[status]; else s = pfrag.str.log_statuses[status]; if (plugged > 0) s += " " + pfrag.str.pluggeds[plugged]; buf.write(s + ","); } else if (CSV_ORDER[i].equals(LogDatabase.KEY_CHARGE)) { buf.write(String.valueOf(completeCursor.getInt(mAdapter.chargeIndex)) + ","); } else if (CSV_ORDER[i].equals(LogDatabase.KEY_TEMPERATURE)) { int temperature = completeCursor.getInt(mAdapter.temperatureIndex); buf.write(String.valueOf(temperature / 10.0) + ","); buf.write(String.valueOf(java.lang.Math.round(temperature * 9 / 5.0) / 10.0 + 32.0) + ","); } else if (CSV_ORDER[i].equals(LogDatabase.KEY_VOLTAGE)) { buf.write(String.valueOf(completeCursor.getInt(mAdapter.voltageIndex) / 1000.0)); } } buf.write("\r\n"); } buf.close(); } catch (Exception e) { Toast.makeText(getActivity(), pfrag.str.inaccessible_storage, Toast.LENGTH_SHORT).show(); return; } Toast.makeText(getActivity(), pfrag.str.file_written, Toast.LENGTH_SHORT).show(); } // Based on http://stackoverflow.com/a/7343721/1427098 private class FilteredCursor extends CursorWrapper { private Cursor wrappedCursor; private ArrayList<Integer> shownIDs; private int len; private int pos; public FilteredCursor(Cursor cursor) { super(cursor); shownIDs = new ArrayList<Integer>(); wrappedCursor = cursor; refilter(); } public void refilter() { if (wrappedCursor.isClosed()) return; shownIDs.clear(); int wrappedCursorPos = wrappedCursor.getPosition(); int statusCodeIndex = wrappedCursor.getColumnIndexOrThrow(LogDatabase.KEY_STATUS_CODE); boolean show_plugged_in = pfrag.sp_main.getBoolean("plugged_in", true); boolean show_unplugged = pfrag.sp_main.getBoolean("unplugged", true); boolean show_charging = pfrag.sp_main.getBoolean("charging", true); boolean show_discharging = pfrag.sp_main.getBoolean("discharging", true); boolean show_fully_charged = pfrag.sp_main.getBoolean("fully_charged", true); boolean show_boot = pfrag.sp_main.getBoolean("boot_completed", true); boolean show_unknown = pfrag.sp_main.getBoolean("unknown", true); for (wrappedCursor.moveToFirst(); !wrappedCursor.isAfterLast(); wrappedCursor.moveToNext()) { int statusCode = wrappedCursor.getInt(statusCodeIndex); int[] statusCodes = LogDatabase.decodeStatus(statusCode); int status = statusCodes[0]; int plugged = statusCodes[1]; int status_age = statusCodes[2]; if (status == BatteryInfo.STATUS_FULLY_CHARGED && show_fully_charged) { shownIDs.add(wrappedCursor.getPosition()); } else if (status == LogDatabase.STATUS_BOOT_COMPLETED && show_boot) { shownIDs.add(wrappedCursor.getPosition()); } else if ((status == BatteryInfo.STATUS_UNKNOWN || status == BatteryInfo.STATUS_DISCHARGING || status == BatteryInfo.STATUS_NOT_CHARGING || status > BatteryInfo.STATUS_MAX) && show_unknown) { shownIDs.add(wrappedCursor.getPosition()); } else if (status_age == LogDatabase.STATUS_OLD) { if ((status == BatteryInfo.STATUS_UNPLUGGED && show_discharging) || (status == BatteryInfo.STATUS_CHARGING && show_charging)) shownIDs.add(wrappedCursor.getPosition()); } else if (status_age == LogDatabase.STATUS_NEW) { if ((status == BatteryInfo.STATUS_UNPLUGGED && show_unplugged) || (status == BatteryInfo.STATUS_CHARGING && show_plugged_in)) shownIDs.add(wrappedCursor.getPosition()); } } wrappedCursor.moveToPosition(wrappedCursorPos); len = shownIDs.size(); pos = -1; } @Override public boolean requery() { boolean ret = super.requery(); refilter(); return ret; } @Override public int getCount() { return len; } @Override public boolean moveToPosition(int newPos) { boolean moved = super.moveToPosition(shownIDs.get(newPos)); if (moved) pos = newPos; return moved; } @Override public final boolean move(int offset) { return moveToPosition(pos + offset); } @Override public final boolean moveToFirst() { return moveToPosition(0); } @Override public final boolean moveToLast() { return moveToPosition(len - 1); } @Override public final boolean moveToNext() { return moveToPosition(pos + 1); } @Override public final boolean moveToPrevious() { return moveToPosition(pos - 1); } @Override public final boolean isFirst() { return len != 0 && pos == 0; } @Override public final boolean isLast() { return len != 0 && pos == len - 1; } @Override public final boolean isBeforeFirst() { return len == 0 || pos == -1; } @Override public final boolean isAfterLast() { return len == 0 || pos == len; } @Override public int getPosition() { return pos; } } private class TimeDeltaCursor extends CursorWrapper { public static final String KEY_TIME_DELTA = "time_delta"; private Cursor wrappedCursor; private int deltaColumnIndex; private String deltaColumnName = KEY_TIME_DELTA; private int statusCodeIndex, timeIndex; private long last_plugged, last_unplugged; private ArrayList<Long> deltas; public TimeDeltaCursor(Cursor cursor) { super(cursor); deltas = new ArrayList<Long>(); wrappedCursor = cursor; deltaColumnIndex = super.getColumnCount(); gen_deltas(); } private void gen_delta() { long time = wrappedCursor.getLong(timeIndex); int statusCode = wrappedCursor.getInt(statusCodeIndex); int[] statusCodes = LogDatabase.decodeStatus(statusCode); int status = statusCodes[0]; int plugged = statusCodes[1]; int status_age = statusCodes[2]; if (status == BatteryInfo.STATUS_FULLY_CHARGED) { if (last_plugged > 0) deltas.add(time - last_plugged); else deltas.add(-1l); } else if (status_age == LogDatabase.STATUS_NEW && status == BatteryInfo.STATUS_UNPLUGGED) { if (last_plugged > 0) deltas.add(time - last_plugged); else deltas.add(-1l); last_unplugged = time; } else if (status_age == LogDatabase.STATUS_NEW && status == BatteryInfo.STATUS_CHARGING) { if (last_unplugged > 0) deltas.add(time - last_unplugged); else deltas.add(-1l); last_plugged = time; } else { deltas.add(-1l); } } private boolean wrappedIsChronological() { if (wrappedCursor.getCount() < 2) return true; boolean chrono = true; int pos = wrappedCursor.getPosition(); wrappedCursor.moveToFirst(); long time1 = wrappedCursor.getLong(timeIndex); wrappedCursor.moveToNext(); long time2 = wrappedCursor.getLong(timeIndex); while (time2 == time1 && !wrappedCursor.isAfterLast()) { wrappedCursor.moveToNext(); time2 = wrappedCursor.getLong(timeIndex); } if (time2 < time1) chrono = false; wrappedCursor.moveToPosition(pos); return chrono; } public void gen_deltas() { if (wrappedCursor.isClosed()) return; deltas.clear(); last_plugged = -1; last_unplugged = -1; int wrappedCursorPos = wrappedCursor.getPosition(); statusCodeIndex = wrappedCursor.getColumnIndexOrThrow(LogDatabase.KEY_STATUS_CODE); timeIndex = wrappedCursor.getColumnIndexOrThrow(LogDatabase.KEY_TIME); if (wrappedIsChronological()) for (wrappedCursor.moveToFirst(); !wrappedCursor.isAfterLast(); wrappedCursor.moveToNext()) gen_delta(); else for (wrappedCursor.moveToLast(); !wrappedCursor.isBeforeFirst(); wrappedCursor.moveToPrevious()) gen_delta(); wrappedCursor.moveToPosition(wrappedCursorPos); } @Override public boolean requery() { boolean ret = super.requery(); gen_deltas(); return ret; } @Override public int getColumnCount() { return deltaColumnIndex + 1; } @Override public int getColumnIndex(String columnName) { if (deltaColumnName.equals(columnName)) return deltaColumnIndex; else return super.getColumnIndex(columnName); } @Override public int getColumnIndexOrThrow(String columnName) throws java.lang.IllegalArgumentException { if (deltaColumnName.equals(columnName)) return deltaColumnIndex; else return super.getColumnIndexOrThrow(columnName); } @Override public String getColumnName(int columnIndex) { if (columnIndex == deltaColumnIndex) return deltaColumnName; else return super.getColumnName(columnIndex); } @Override public String[] getColumnNames() { String[] a = super.getColumnNames(); String[] b = new String[a.length + 1]; for (int i = 0; i < a.length; i++) b[i] = a[i]; b[a.length] = deltaColumnName; return b; } @Override public long getLong(int columnIndex) { if (columnIndex == deltaColumnIndex) { int pos = getPosition(); if (!wrappedIsChronological()) pos = getCount() - 1 - pos; return deltas.get(pos); } else return super.getLong(columnIndex); } // TODO: This was introduced in API 11. It seems to be safely ignored by APIs less than 11, other than // taking a while for the verifier to no-op it. Is it worth overriding to add only to // API 11+ devices? I think it only slows things down on the first run, and not noticeably on most devices. /* @Override public int getType(int columnIndex) { if (columnIndex == deltaColumnIndex) return Cursor.FIELD_TYPE_INTEGER; else return super.getType(columnIndex); } */ @Override public boolean isNull(int columnIndex) { if (columnIndex == deltaColumnIndex) return false; else return super.isNull(columnIndex); } } private static class LogItemViewHolder { public TextView status_tv; public TextView percent_tv; public TextView time_tv; public TextView temp_volt_tv; public TextView time_diff_tv; } private class LogAdapter extends CursorAdapter { public int statusCodeIndex, chargeIndex, timeIndex, temperatureIndex, voltageIndex, timeDeltaIndex; public DateFormat dateFormat, timeFormat; private Date d = new Date(); public LogAdapter(Context context, Cursor cursor) { super(context, cursor); dateFormat = android.text.format.DateFormat.getDateFormat(context); timeFormat = android.text.format.DateFormat.getTimeFormat(context); statusCodeIndex = cursor.getColumnIndexOrThrow(LogDatabase.KEY_STATUS_CODE); chargeIndex = cursor.getColumnIndexOrThrow(LogDatabase.KEY_CHARGE); timeIndex = cursor.getColumnIndexOrThrow(LogDatabase.KEY_TIME); temperatureIndex = cursor.getColumnIndexOrThrow(LogDatabase.KEY_TEMPERATURE); voltageIndex = cursor.getColumnIndexOrThrow(LogDatabase.KEY_VOLTAGE); timeDeltaIndex = cursor.getColumnIndexOrThrow(TimeDeltaCursor.KEY_TIME_DELTA); } public View newView(Context context, Cursor cursor, ViewGroup parent) { View v = mInflater.inflate(R.layout.log_item, parent, false); LogItemViewHolder vh = new LogItemViewHolder(); vh.status_tv = (TextView) v.findViewById(R.id.status); vh.percent_tv = (TextView) v.findViewById(R.id.percent); vh.time_tv = (TextView) v.findViewById(R.id.time); vh.temp_volt_tv = (TextView) v.findViewById(R.id.temp_volt); vh.time_diff_tv = (TextView) v.findViewById(R.id.time_diff); v.setTag(vh); return v; } public void bindView(View view, Context context, Cursor cursor) { LogItemViewHolder vh = (LogItemViewHolder) view.getTag(); TextView status_tv = vh.status_tv; TextView percent_tv = vh.percent_tv; TextView time_tv = vh.time_tv; TextView temp_volt_tv = vh.temp_volt_tv; TextView time_diff_tv = vh.time_diff_tv; int statusCode = cursor.getInt(statusCodeIndex); int[] statusCodes = LogDatabase.decodeStatus(statusCode); int status = statusCodes[0]; int plugged = statusCodes[1]; int status_age = statusCodes[2]; String s; if (status == LogDatabase.STATUS_BOOT_COMPLETED) percent_tv.setVisibility(View.GONE); else percent_tv.setVisibility(View.VISIBLE); if (status == LogDatabase.STATUS_BOOT_COMPLETED) { status_tv.setTextColor(col.boot); s = pfrag.str.status_boot_completed; time_diff_tv.setVisibility(View.GONE); } else if (status_age == LogDatabase.STATUS_OLD) { status_tv.setTextColor(col.old_status); percent_tv.setTextColor(col.old_status); s = pfrag.str.log_statuses_old[status]; time_diff_tv.setVisibility(View.GONE); } else { switch (status) { case 0: status_tv.setTextColor(col.unplugged); percent_tv.setTextColor(col.unplugged); break; case 2: status_tv.setTextColor(col.plugged); percent_tv.setTextColor(col.plugged); break; case 5: status_tv.setTextColor(col.charged); percent_tv.setTextColor(col.charged); break; default: status_tv.setTextColor(col.unknown); percent_tv.setTextColor(col.unknown); } s = pfrag.str.log_statuses[status]; long delta; switch (status) { case 0: case 5: delta = cursor.getLong(timeDeltaIndex); if (delta < 0) { time_diff_tv.setVisibility(View.GONE); break; } time_diff_tv.setText(String.format(pfrag.res.getString(R.string.after_n_hours_plugged_in), delta / 1000.0 / 60.0 / 60.0)); time_diff_tv.setVisibility(View.VISIBLE); break; case 2: delta = cursor.getLong(timeDeltaIndex); if (delta < 0) { time_diff_tv.setVisibility(View.GONE); break; } time_diff_tv.setText(String.format(pfrag.res.getString(R.string.after_n_hours_unplugged), delta / 1000.0 / 60.0 / 60.0)); time_diff_tv.setVisibility(View.VISIBLE); break; default: time_diff_tv.setVisibility(View.GONE); } } if (plugged > 0) s += " " + pfrag.str.pluggeds[plugged]; status_tv.setText(s); percent_tv.setText("" + cursor.getInt(chargeIndex) + "%"); d.setTime(cursor.getLong(timeIndex)); time_tv.setText(dateFormat.format(d) + " " + timeFormat.format(d)); int temperature = cursor.getInt(temperatureIndex); if (temperature != 0) temp_volt_tv.setText("" + pfrag.str.formatTemp(temperature, convertF)); else temp_volt_tv.setText(""); /* TextViews are reused */ int voltage = cursor.getInt(voltageIndex); if (voltage != 0) temp_volt_tv.setText( ((String) temp_volt_tv.getText().toString()) + " / " + pfrag.str.formatVoltage(voltage)); } } private class Col { public int old_status; public int charged; public int plugged; public int unplugged; public int unknown; public int boot; public Col() { old_status = pfrag.res.getColor(R.color.log_old_status); charged = pfrag.res.getColor(R.color.log_charged); plugged = pfrag.res.getColor(R.color.log_plugged); unplugged = pfrag.res.getColor(R.color.log_unplugged); unknown = pfrag.res.getColor(R.color.log_unknown); boot = pfrag.res.getColor(R.color.log_boot_completed); } } }