LoginSignup
2
2

More than 5 years have passed since last update.

AssetInstaller

Last updated at Posted at 2013-03-20

プロジェクトの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は、定義はしていますが実際には使用されません(今後使うかも)

つかいかた
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) {
      /* インストールしたファイルの利用 */
    }
  }
}
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で使ってるサンプルはあまりないと思うので、その辺でも参考にしてね。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2