assetのzipファイルを展開する

起動時に assets の tessdata.zip を展開する。

■展開元
tessdata.zip

■展開先
/data/data/com.example.android.tesseract/files

zipファイルをそのまま展開するので、

  • tessdata.zip
    • tessdata
      • eng.traineddata

という構成になっていること。

MainActivity.java

package com.example.android.tesseract;

import com.googlecode.tesseract.android.TessBaseAPI;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        new AssetInstaller2(this, mHandler).execute();
    }

    private static MainHandler extends Handler {
        private final WeakReference<MainActivity> mActivity;
        MainHandler(MainActivity activity) {
            mActivity = new WeakReference<MainActivity>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case AssetInstaller.SHOW_TEXT:
                MainActivity activity = mActivity.get();
                if (activity != null) {
                    showTextView();
                }
            }
        }
    });

    private MainHandler mHandler = new MainHandler(this);

    private void showTextView() {
        String text = getText();
        TextView textView = new TextView(MainActivity.this);
        textView.setText(text);
        setContentView(textView);
    }

    private String getText() {
        final Bitmap bitmap =
                BitmapFactory.decodeResource(
                        getResources(), R.drawable.sample);
        final Bitmap copy =
                bitmap.copy(Config.ARGB_8888, true);

        final String path = getFilesDir().getPath();
        TessBaseAPI baseApi;
        baseApi = new TessBaseAPI();
        baseApi.init(path, "eng");
        baseApi.setImage(copy);
        String text = baseApi.getUTF8Text();
        baseApi.end();
        return text;
    }
}

AssetInstaller.java

package com.example.android.tesseract;

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.ProgressDialog;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.os.AsyncTask;
import android.os.Handler;

public class AssetInstaller2 extends AsyncTask<Void, Integer, Void> {

    public static final int SHOW_TEXT = 0;

    private static String ASSET_TESSDATA = "tessdata.zip";

    private Context mContext;
    private Handler mHandler;

    private ArrayList<String> mZippedFiles;
    private long mTotalSize;

    private ProgressDialog mProgressDialog;

    public AssetInstaller2(Context context, Handler handler) {
        mContext = context;
        mHandler = handler;
    }

    @Override
    protected Void doInBackground(Void... params) {
        try {
            File filesDir = mContext.getFilesDir();
            if (!checkToCopyFile(filesDir)) {
                return null;
            }
            startToCopyFile(filesDir);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        if (mProgressDialog == null) {
            showProgress((int)mTotalSize);
        } else {
            updateProgress(values[0]);
        }
    }

    @Override
    protected void onPostExecute(Void result) {
        dismissProgress();
        mHandler.sendEmptyMessage(SHOW_TEXT);
    }

    private ZipInputStream getAssetZipInputStream()
            throws IOException {
        AssetFileDescriptor fd =
                mContext.getResources().getAssets().openFd(
                        ASSET_TESSDATA);
                
        assert(fd != null);
        return new ZipInputStream(new BufferedInputStream(
                fd.createInputStream()));
    }

    private boolean checkToCopyFile(final File filesDir)
            throws IOException {
        assert(filesDir != null);
        mZippedFiles = new ArrayList<String>();
        mTotalSize = 0;
        ZipInputStream zis = null;
        try {
            zis = getAssetZipInputStream();
            ZipEntry zipEntry;
            while ((zipEntry = zis.getNextEntry()) != null) {
                if (zipEntry.isDirectory()) {
                    zis.closeEntry();
                    continue;
                }
                long zipEntrySize = zipEntry.getSize();
                String zipEntryName = zipEntry.getName();
                File unzippedFile =
                        new File(filesDir, zipEntryName);
                long unzippedFileSize = unzippedFile.length();
                if (zipEntrySize != unzippedFileSize) {
                    mZippedFiles.add(zipEntryName);
                    mTotalSize += zipEntrySize;
                }
                zis.closeEntry();
            }
        } finally {
            if (zis != null) zis.close();
        }
        return !mZippedFiles.isEmpty();
    }

    private void startToCopyFile(final File filesDir)
            throws IOException {
        assert(filesDir != null);
        assert(mZippedFiles != null);
        assert(!mZippedFiles.isEmpty());
        copyFiles(filesDir);
    }

    private void copyFiles(final File filesDir)
            throws IOException {
        assert(filesDir != null);
        assert(mZippedFiles != null);
        assert(!mZippedFiles.isEmpty());
        publishProgress();
        ZipInputStream zis = null;
        try {
            zis = getAssetZipInputStream();
            ZipEntry zipEntry;
            while ((zipEntry = zis.getNextEntry()) != null) {
                if (zipEntry.isDirectory()) {
                    zis.closeEntry();
                    continue;
                }
                String zipEntryName = zipEntry.getName();
                if (!mZippedFiles.contains(zipEntryName)) {
                    zis.closeEntry();
                    continue;
                }
                File unzippedFile =
                        new File(filesDir, 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[256];
                    int len;
                    while ((len = zis.read(buf, 0, buf.length)) != -1) {
                        os.write(buf, 0, len);
                        publishProgress(buf.length);
                    }
                    os.flush();
                } finally {
                    if (os != null) os.close();
                }
                unzippedFile.setReadable(true, false);
                unzippedFile.setWritable(true, false);
                zis.closeEntry();
            }
        } finally {
            if (zis != null) zis.close();
        }
    }

    private void showProgress(final int max) {
        mProgressDialog = new ProgressDialog(mContext);
        mProgressDialog.setIndeterminate(false);
        mProgressDialog.setProgressStyle(
                ProgressDialog.STYLE_HORIZONTAL);
        mProgressDialog.setCancelable(false);
        mProgressDialog.setMax(max);
        mProgressDialog.incrementProgressBy(0);
        mProgressDialog.show();
    }

    private void updateProgress(final int diff) {
        assert(mProgressDialog != null);
        mProgressDialog.incrementProgressBy(diff);
        mProgressDialog.show();
    }

    private void dismissProgress() {
        if (mProgressDialog != null)
            mProgressDialog.dismiss();
    }
}

ソースを書く前のメモ

  • assetsにはzipファイルを配置する
    • 非圧縮の場合、サイズ制限がある
  • unzip前にファイルサイズのチェック
  • unzipは別スレッド処理
    • eng.traineddataは3MBくらいあるので、コピーに時間がかかる
    • ProgressDialog表示
  • unzipしたファイルにRW権限を付与
    • 権限がないとtesseractが落ちる?