AssetInstaller

プロジェクトのAssetフォルダに格納したzipファイルを適当なフォルダに展開するインストーラー。もしインストール済みであった場合は何もしない。
こちらを参考にしていますが、色々といじってます。
http://outcesticide.hatenablog.com/entries/2012/12/12
上との違いは

- tessdata.zip以外のファイルにも対応(AssetInstaller#executeの引数にzipファイルの名前を拡張子抜きで指定する)
- プログレスダイアログのタイトル、メッセージ表示に対応(setProgressTitle/setProgressMessageでメッセージを指定する)
- プログレスダイアログにDialogFragmentを使うように変更(そのため、AssetInstaller#ctor()の第一引数にはContextではなくFragmentActivityを指定する必要があります)
- インストール先ディレクトリを任意に指定出来る(AssetInstaller#ctor()の引数にディレクトリを指定する)
- 処理が終わったら「FinishedListener#installFinished()」メソッドを呼び出す(もしインストール済みであった場合、何もせずに本メソッドを呼び出す)。
- Assetからの展開→実ディレクトリに配置するときの処理バッファを256バイト→1024バイトに変更

ちなみにAssetInstaller.CANCELLEDは、定義はしていますが実際には使用されません(今後使うかも)

```java:つかいかた
public class MainActivity extends FragmentActivity implements AssetInstaller.FinishedListener {
  protected void onCreate(Bundle savedInstanceState) {
    ・・・
    AssetInstaller installer = new AssetInstaller(this, getFilesDir(), this);
    installer.setProgressTitle("文字解析に必要なファイルをインストールしています。");
    installer.setProgressMessage("%1$s.zipの%2$sを展開中");
    installer.execute("tessdata");
  }
  ・・・
  @Override
  public void installFinished(int state) {
    if (state != AssetInstaller.CANCELLED) {
      /* インストールしたファイルの利用 */
    }
  }
}
```

```java:AssetInstaller.java
/**
 * http://outcesticide.hatenablog.com/entries/2012/12/12
 * の記事を参考にしています。
 */

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.res.AssetFileDescriptor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.util.Log;

public class AssetInstaller extends AsyncTask<String, Object, Boolean> {

    /**
     * インストールがキャンセルされた(現時点のバージョンでは起こりえません)
     */
    public final static int CANCELLED = -1;

    /**
     * すでにインストールされていたため、処理をスキップした
     */
    public final static int INSTALLED = 0;

    /**
     * インストールが正常終了した
     */
    public final static int SUCCESSED = 1;

    /**
     * インストールが完了したときに呼び出されるコールバックインタフェース
     * @author 知英
     */
    public interface FinishedListener {
	/**
	 * インストールが完了したときにコールバックされるメソッド。
	 * もし指定したファイルがインストール済みであった場合、何もせずにこのメソッドを呼び出す。
	 * @param state インストール状態。CANCELLED, INSTALLED, SUCCESSEDのいずれか。
	 */
	void installFinished(int state);
    }

    public final static String TAG = AssetInstaller.class.getSimpleName();

    private FragmentActivity mActivity;

    private File mBaseDir;

    private ArrayList<String> mZippedFiles;

    private int mTotalSize;

    private ProgressDialogFragment mDialogFragment;

    private FinishedListener mFinishedListener;

    private String mDialogTitle;

    private String mDialogMessage;

    public AssetInstaller(FragmentActivity activity, File baseDir,
	    FinishedListener finished) {
	this.mActivity = activity;
	this.mBaseDir = baseDir;
	this.mFinishedListener = finished;
    }

    public void setProgressTitle(String title) {
	this.mDialogTitle = title;
    }

    public void setProgressMessage(String message) {
	this.mDialogMessage = message;
    }

    @Override
    protected Boolean doInBackground(String... params) {
	boolean result = false;
	for (String param : params) {
	    Log.d(TAG, "install " + param + "?");
	    try {
		File dir = new File(mBaseDir, param);
		if (checkToCopyFile(dir, param)) {
		    Log.d(TAG, "install " + param + ".");
		    result |= startCopyToFile(dir, param);
		} else {
		    continue;
		}
	    } catch (IOException e) {
		e.printStackTrace();
	    }
	}
	return result;
    }

    @Override
    protected void onProgressUpdate(Object... values) {
	Log.i(TAG, "progress update");
	if (values.length == 0) {
	    Log.i(TAG, "filesize:" + mTotalSize);
	    showProgress(mTotalSize);
	} else if (values[0] instanceof Integer) {
	    updateProgress((Integer) values[0]);
	} else if (values[0] instanceof String) {
	    updateMessage((String) values[0]);
	}
    }

    @Override
    protected void onPostExecute(Boolean result) {
	Log.d(TAG, "executed");
	dismissProgress();
	if (mFinishedListener != null) {
	    mFinishedListener.installFinished(result ? SUCCESSED : INSTALLED);
	}
    }

    @Override
    protected void onCancelled() {
	Log.d(TAG, "cancelled");
	if (mFinishedListener != null) {
	    mFinishedListener.installFinished(CANCELLED);
	}
    }

    // プログレスバー処理

    /**
     * プログレスダイアログを表示する
     *
     * @param max
     *            プログレスの最大値
     */
    private void showProgress(int max) {
	mDialogFragment = new ProgressDialogFragment();
	Bundle arguments = new Bundle();
	arguments.putInt(ProgressDialogFragment.PARAM_MAX, max);
	if (mDialogTitle != null) {
	    arguments.putString(ProgressDialogFragment.PARAM_TITLE,
		    mDialogTitle);
	}
	mDialogFragment.setArguments(arguments);
	mDialogFragment.show(mActivity.getSupportFragmentManager(), "");
    }

    /**
     * プログレスダイアログの値を更新する
     *
     * @param value
     *            更新する値
     */
    private void updateProgress(int value) {
	mDialogFragment.updateProgress(value);
    }

    /**
     * プログレスダイアログのメッセージを更新する。
     * <ul>
     * <li>メッセージ内の%1$sは展開するアーカイブファイル名
     * <li>メッセージ内の%2$sは現在展開中のZipEntryファイル名
     * </ul>
     * に展開される
     *
     * @param string
     */
    private void updateMessage(String message) {
	mDialogFragment.updateMessage(message);
    }

    /**
     * プログレスダイアログを破棄する
     */
    private void dismissProgress() {
	if (mDialogFragment != null) {
	    mDialogFragment.dismiss();
	}
    }

    // ユーティリティメソッド

    /**
     * Assetフォルダ内のZIPファイルを開き、そのストリームを返す
     *
     * @param fileName
     *            ファイル名(拡張子抜き)
     * @return ZIPストリーム
     * @throws IOException
     */
    private ZipInputStream getAssetZipInputStream(String fileName)
	    throws IOException {
	AssetFileDescriptor fd = mActivity.getResources().getAssets()
		.openFd(fileName + ".zip");

	return new ZipInputStream(new BufferedInputStream(
		fd.createInputStream()));
    }

    /**
     * Assetフォルダ内のZIPファイルを展開する必要があるかどうか確認する
     *
     * @param dir
     *            ベースディレクトリを示す{@link File}オブジェクト
     * @param fileName
     *            展開対象のファイル名
     * @return ファイルを展開する必要がある場合、trueを返す
     * @throws IOException
     */
    private boolean checkToCopyFile(File dir, String fileName)
	    throws IOException {
	mZippedFiles = new ArrayList<String>();
	mTotalSize = 0;
	ZipInputStream zis = null;
	try {
	    zis = getAssetZipInputStream(fileName);
	    ZipEntry zipEntry;
	    while ((zipEntry = zis.getNextEntry()) != null) {
		if (zipEntry.isDirectory()) {
		    zis.closeEntry();
		    continue;
		}
		long zipEntrySize = zipEntry.getSize();
		String zipEntryName = zipEntry.getName();
		Log.i(TAG, "check:" + zipEntryName);
		File unzippedFile = new File(dir, zipEntryName);
		long unzippedFileSize = unzippedFile.length();
		if (zipEntrySize != unzippedFileSize) {
		    Log.i(TAG, "unzip needed");
		    mZippedFiles.add(zipEntryName);
		    mTotalSize += zipEntrySize;
		}
	    }
	} finally {
	    if (zis != null)
		zis.close();
	}
	return !mZippedFiles.isEmpty();
    }

    /**
     * ZIPファイルを展開し、実フォルダに配置する
     *
     * @param dir
     *            ベースディレクトリを示す{@link File}オブジェクト
     * @param fileName
     *            展開対象のファイル名
     * @return ファイルが正しく展開された場合、trueを返す
     * @throws IOException
     */
    private boolean startCopyToFile(File dir, String fileName)
	    throws IOException {
	return copyFiles(dir, fileName);
    }

    /**
     * ZIPファイルを展開し、実フォルダに配置する
     *
     * @param dir
     *            ベースディレクトリを示す{@link File}オブジェクト
     * @param fileName
     *            展開対象のファイル名
     * @return ファイルが正しく展開された場合、trueを返す
     * @throws IOException
     */
    private boolean copyFiles(File dir, String fileName) throws IOException {
	boolean result = false;
	publishProgress();
	ZipInputStream zis = null;
	int sizes = 0;
	try {
	    zis = getAssetZipInputStream(fileName);
	    ZipEntry zipEntry;
	    while ((zipEntry = zis.getNextEntry()) != null) {
		if (zipEntry.isDirectory()) {
		    zis.closeEntry();
		    continue;
		}
		String zipEntryName = zipEntry.getName();
		if (mDialogMessage != null) {
		    publishProgress(String.format(mDialogMessage, fileName,
			    zipEntryName));
		}
		File unzippedFile = new File(dir, zipEntryName);
		if (!unzippedFile.getParentFile().exists()) {
		    unzippedFile.getParentFile().mkdirs();
		}
		if (unzippedFile.exists()) {
		    unzippedFile.delete();
		}
		OutputStream os = null;
		try {
		    os = new BufferedOutputStream(new FileOutputStream(
			    unzippedFile));
		    byte[] buf = new byte[1024];
		    int len;
		    while ((len = zis.read(buf, 0, buf.length)) != -1) {
			os.write(buf, 0, len);
			sizes += len;
			publishProgress(sizes);
		    }
		    os.flush();
		} finally {
		    if (os != null) {
			os.close();
		    }
		}
		unzippedFile.setReadable(true, false);
		unzippedFile.setWritable(true, false);
		zis.closeEntry();
		result = true;
	    }
	} finally {
	    if (zis != null) {
		zis.close();
	    }
	}
	return result;
    }

}

/**
 * プログレスダイアログフラグメント
 * @author 知英
 */
class ProgressDialogFragment extends DialogFragment {

    /**
     * プログレスバーの最大値
     */
    public final static String PARAM_MAX = "max";

    /**
     * プログレスダイアログの値
     */
    public final static String PARAM_TITLE = "title";

    private int mStoredProgress = 0;

    private String mStoredMessage = "";

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
	ProgressDialog dialog = new ProgressDialog(getActivity());
	dialog.setIndeterminate(false);
	dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
	dialog.setCancelable(false);
	dialog.incrementProgressBy(0);
	setCancelable(false);

	Bundle bundle = getArguments();
	if (bundle.containsKey(PARAM_TITLE)) {
	    dialog.setTitle(bundle.getString(PARAM_TITLE));
	}
	dialog.setMessage(mStoredMessage);
	dialog.setMax(bundle.getInt(PARAM_MAX));
	dialog.setProgress(mStoredProgress);
	return dialog;
    }

    /**
     * ダイアログのプログレスバー値を更新する
     * @param progress プログレスバー値
     */
    public void updateProgress(int progress) {
	Dialog d = getDialog();
	if (d != null) {
	    ((ProgressDialog) d).setProgress(progress);
	} else {
	    mStoredProgress = progress;
	}
    }

    /**
     * ダイアログのメッセージを更新する
     * @param message メッセージ
     */
    public void updateMessage(String message) {
	Dialog d = getDialog();
	if (d != null) {
	    ((ProgressDialog) d).setMessage(message);
	} else {
	    mStoredMessage = message;
	}
    }
}
```
ProgressDialogをDialogFragmentで使ってるサンプルはあまりないと思うので、その辺でも参考にしてね。