/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.voicedialer;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.speech.srec.WaveHeader;
import android.text.format.DateFormat;
import android.util.Log;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* This class logs the inputs and results of a recognition session to
* the files listed below, which reside in
* /data/data/com.android.voicedialer/app_logdir.
* The files have the date encoded in the name so that they will sort in
* time order. The newest RecognizerLogger.MAX_FILES are kept,
* and the rest deleted to limit space used in the file system.
*
* - datename.wav - what the microphone heard.
*
- datename.log - contact list, results, errors, etc.
*
*/
public class RecognizerLogger {
private static final String TAG = "RecognizerLogger";
private static final String LOGDIR = "logdir";
private static final String ENABLED = "enabled";
private static final int MAX_FILES = 20;
private final String mDatedPath;
private final BufferedWriter mWriter;
/**
* Determine if logging is enabled. If the
* @param context needed to reference the logging directory.
* @return true if logging is enabled, determined by the 'enabled' file.
*/
public static boolean isEnabled(Context context) {
File dir = context.getDir(LOGDIR, 0);
File enabled = new File(dir, ENABLED);
return enabled.exists();
}
/**
* Enable logging.
* @param context needed to reference the logging directory.
*/
public static void enable(Context context) {
try {
File dir = context.getDir(LOGDIR, 0);
File enabled = new File(dir, ENABLED);
enabled.createNewFile();
}
catch (IOException e) {
Log.e(TAG, "enableLogging " + e);
}
}
/**
* Disable logging.
* @param context needed to reference the logging directory.
*/
public static void disable(Context context) {
try {
File dir = context.getDir(LOGDIR, 0);
File enabled = new File(dir, ENABLED);
enabled.delete();
}
catch (SecurityException e) {
Log.e(TAG, "disableLogging " + e);
}
}
/**
* Constructor
* @param dataDir directory to contain the log files.
*/
public RecognizerLogger(Context context) throws IOException {
if (false) Log.d(TAG, "RecognizerLogger");
// generate new root filename
File dir = context.getDir(LOGDIR, 0);
mDatedPath = dir.toString() + File.separator + "log_" +
DateFormat.format("yyyy_MM_dd_kk_mm_ss",
System.currentTimeMillis());
// delete oldest files
deleteOldest(".wav");
deleteOldest(".log");
// generate new text output log file
mWriter = new BufferedWriter(new FileWriter(mDatedPath + ".log"), 8192);
mWriter.write(Build.FINGERPRINT);
mWriter.newLine();
}
/**
* Write a line into the text log file.
*/
public void logLine(String msg) {
try {
mWriter.write(msg);
mWriter.newLine();
}
catch (IOException e) {
Log.e(TAG, "logLine exception: " + e);
}
}
/**
* Write a header for the NBest lines into the text log file.
*/
public void logNbestHeader() {
logLine("Nbest *****************");
}
/**
* Write the list of contacts into the text log file.
* @param contacts
*/
public void logContacts(List contacts) {
logLine("Contacts *****************");
for (VoiceContact vc : contacts) logLine(vc.toString());
try {
mWriter.flush();
}
catch (IOException e) {
Log.e(TAG, "logContacts exception: " + e);
}
}
/**
* Write a list of Intents into the text log file.
* @param intents
*/
public void logIntents(ArrayList intents) {
logLine("Intents *********************");
StringBuffer sb = new StringBuffer();
for (Intent intent : intents) {
logLine(intent.toString() + " " + RecognizerEngine.SENTENCE_EXTRA + "=" +
intent.getStringExtra(RecognizerEngine.SENTENCE_EXTRA));
}
try {
mWriter.flush();
}
catch (IOException e) {
Log.e(TAG, "logIntents exception: " + e);
}
}
/**
* Close the text log file.
* @throws IOException
*/
public void close() throws IOException {
mWriter.close();
}
/**
* Delete oldest files with a given suffix, if more than MAX_FILES.
* @param suffix delete oldest files with this suffix.
*/
private void deleteOldest(final String suffix) {
FileFilter ff = new FileFilter() {
public boolean accept(File f) {
String name = f.getName();
return name.startsWith("log_") && name.endsWith(suffix);
}
};
File[] files = (new File(mDatedPath)).getParentFile().listFiles(ff);
Arrays.sort(files);
for (int i = 0; i < files.length - MAX_FILES; i++) {
files[i].delete();
}
}
/**
* InputStream wrapper which will log the contents to a WAV file.
* @param inputStream
* @param sampleRate
* @return
*/
public InputStream logInputStream(final InputStream inputStream, final int sampleRate) {
final ByteArrayOutputStream baos = new ByteArrayOutputStream(sampleRate * 2 * 20);
return new InputStream() {
public int available() throws IOException {
return inputStream.available();
}
public int read(byte[] b, int offset, int length) throws IOException {
int rtn = inputStream.read(b, offset, length);
if (rtn > 0) baos.write(b, offset, rtn);
return rtn;
}
public int read(byte[] b) throws IOException {
int rtn = inputStream.read(b);
if (rtn > 0) baos.write(b, 0, rtn);
return rtn;
}
public int read() throws IOException {
int rtn = inputStream.read();
if (rtn > 0) baos.write(rtn);
return rtn;
}
public long skip(long n) throws IOException {
throw new UnsupportedOperationException();
}
public void close() throws IOException {
try {
OutputStream out = new FileOutputStream(mDatedPath + ".wav");
try {
byte[] pcm = baos.toByteArray();
WaveHeader hdr = new WaveHeader(WaveHeader.FORMAT_PCM,
(short)1, sampleRate, (short)16, pcm.length);
hdr.write(out);
out.write(pcm);
}
finally {
out.close();
}
}
finally {
inputStream.close();
baos.close();
}
}
};
}
}