/* (C) 2012 Pragmatic Software
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 com.googlecode.networklog;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import java.lang.Thread;
import java.lang.Runnable;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.concurrent.FutureTask;
public class ClearLog
{
FixedSpinnerAlertDialog dialog = null;
int spinnerInit = 0;
ProgressDialog progressDialog = null;
int progress = 0;
int progress_max = 0;
public FutureTask showProgressDialog(final Context context) {
FutureTask futureTask = new FutureTask(new Runnable() {
public void run() {
progressDialog = new ProgressDialog(context);
if(progress_max == 0) {
progressDialog.setIndeterminate(true);
} else {
progressDialog.setIndeterminate(false);
}
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setCancelable(false);
progressDialog.setTitle("");
progressDialog.setMessage(context.getResources().getString(R.string.clear_progress_message));
progressDialog.setMax(progress_max);
progressDialog.setProgress(progress);
progressDialog.show();
}
}, null);
NetworkLog.handler.post(futureTask);
return futureTask;
}
public void clearLogFileEntriesOlderThan(final Context context, final long timerange) {
LogfileLoader loader = new LogfileLoader();
long start = System.currentTimeMillis();
try {
loader.openLogfile(NetworkLog.settings.getLogFile());
long length = loader.getLength();
long starting_pos = loader.seekToTimestampPosition(System.currentTimeMillis() - timerange);
File logfile = new File(NetworkLog.settings.getLogFile());
File file = new File(logfile.getParent(), logfile.getName() + ".clear");
Log.d("NetworkLog", "Opened " + logfile + " and " + file + " for clearing");
Log.d("NetworkLog", "starting pos: " + starting_pos);
BufferedOutputStream fileWriter = new BufferedOutputStream(new FileOutputStream(file));
if(starting_pos != -1) {
progress_max = (int)(length - starting_pos);
progress = 0;
progressDialog.setIndeterminate(false);
progressDialog.setMax(progress_max);
long processed_so_far = 0;
long progress_increment_size = (long)((length - starting_pos) * 0.01);
long next_progress_increment = progress_increment_size;
while(true) {
if(loader.readChunk() == false) {
// end of file
MyLog.d("[clearlogfile] Reached end of file");
break;
}
processed_so_far = loader.getReadSoFar();
if(processed_so_far >= next_progress_increment) {
next_progress_increment += progress_increment_size;
progress = (int)processed_so_far;
if(progressDialog != null && progressDialog.isShowing()) {
progressDialog.setProgress(progress);
}
}
fileWriter.write(loader.getBuffer(), 0, loader.getBufferLength());
}
loader.closeLogfile();
}
fileWriter.close();
if(logfile.delete()) {
if(!file.renameTo(logfile)) {
Log.w("NetworkLog", "Failed to rename " + file + " to " + logfile);
}
} else {
Log.w("NetworkLog", "Failed to delete " + logfile);
}
} catch (Exception e) {
Log.w("NetworkLog", "clearLogFileEntriesOlderThan", e);
} finally {
long elapsed = System.currentTimeMillis() - start;
Log.d("NetworkLog", "Clear logfile history elapsed: " + elapsed);
NetworkLog.handler.post(new Runnable() {
public void run() {
NetworkLog.updateStatusText();
}
});
}
}
public void clearLogEntriesOlderThan(final Context context, final long timerange, final boolean clearLogfile) {
new Thread(new Runnable() {
public void run() {
Log.d("NetworkLog", "Clearing entries older than " + timerange + "; logfile: " + clearLogfile);
progress_max = 0;
progress = 0;
FutureTask showDialog = showProgressDialog(context);
try {
showDialog.get(); // wait for task to complete
} catch (Exception e) {
// do nothing
}
NetworkLog.logFragment.clearLogEntriesOlderThan(timerange);
NetworkLog.appFragment.rebuildLogEntries();
if(clearLogfile) {
boolean serviceRunning = false;
if(NetworkLog.isServiceRunning(context, NetworkLogService.class.getName())) {
serviceRunning = true;
Log.d("NetworkLog", "Stopping logging to clear log");
NetworkLogService.instance.stopLogging();
}
Log.d("NetworkLog", "Clearing logfile...");
clearLogFileEntriesOlderThan(context, timerange);
if(serviceRunning) {
Log.d("NetworkLog", "Resuming logging");
NetworkLogService.instance.startLogging();
}
}
NetworkLog.handler.post(new Runnable() {
public void run() {
if(progressDialog != null) {
progressDialog.dismiss();
progressDialog = null;
}
}
});
Log.d("NetworkLog", "Done clearing log entries.");
}
}).start();
}
public void showClearLogDialog(final Context context) {
LinearLayout view = new LinearLayout(context);
view.setOrientation(LinearLayout.VERTICAL);
LinearLayout layout = new LinearLayout(context);
layout.setOrientation(LinearLayout.HORIZONTAL);
TextView tv = new TextView(context);
tv.setText(context.getResources().getString(R.string.clear_dialog_prompt));
layout.addView(tv);
final String[] timerangeValues = context.getResources().getStringArray(R.array.clearlog_timerange_values);
final Spinner spinner = new Spinner(context);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
context, R.array.clearlog_timerange_entries, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
spinner.setPrompt(context.getResources().getString(R.string.clear_dialog_prompt));
spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
if(spinnerInit > 0) {
// Don't process selection events if spinner is initializing
spinnerInit--;
return;
}
NetworkLog.settings.setClearLogTimerange(timerangeValues[pos]);
}
@Override
public void onNothingSelected(AdapterView parent) {
// do nothing
}
});
// Initialize spinner
String timerange = NetworkLog.settings.getClearLogTimerange();
int length = timerangeValues.length;
for(int i = 0; i < length; i++) {
if(timerange.equals(timerangeValues[i])) {
spinnerInit++;
spinner.setSelection(i);
break;
}
}
layout.addView(spinner);
view.addView(layout);
final CheckBox checkbox = new CheckBox(context);
Resources res = context.getResources();
checkbox.setChecked(false);
checkbox.setText(res.getString(R.string.clear_dialog_delete_from_logfile));
view.addView(checkbox);
dialog = new FixedSpinnerAlertDialog(context);
dialog.setTitle(res.getString(R.string.clear_dialog_title));
dialog.setCancelable(true);
dialog.setView(view);
dialog.setLayout(view); /* workaround to dismiss Spinner pop-up dialog when changing orientation */
dialog.setButton(DialogInterface.BUTTON_POSITIVE, res.getString(R.string.clear_dialog_button_positive), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.dismiss();
clearLogEntriesOlderThan(context, Long.parseLong(timerangeValues[spinner.getSelectedItemPosition()]), checkbox.isChecked());
}
});
dialog.setButton(DialogInterface.BUTTON_NEGATIVE, res.getString(R.string.cancel), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
dialog.cancel();
}
});
dialog.show();
}
/* workaround to dismiss Spinner pop-up dialog when changing orientation */
class FixedSpinnerAlertDialog extends AlertDialog {
LinearLayout layout;
public FixedSpinnerAlertDialog(final Context context) {
super(context);
}
public FixedSpinnerAlertDialog(final Context context, final int theme) {
super(context, theme);
}
public FixedSpinnerAlertDialog(final Context context, final boolean cancelable, final OnCancelListener cancelListener) {
super(context, cancelable, cancelListener);
}
public void setLayout(LinearLayout layout) {
this.layout = layout;
}
@Override
public void dismiss() {
// Workaround for Spinner's dialog leaking current window during rotation
// See issue #4936 : http://code.google.com/p/android/issues/detail?id=4936
layout.removeAllViewsInLayout();
super.dismiss();
}
}
}