package net.itsuha.android.zip4j; import net.itsuha.android.zip4j.MainActivity.FragmentBackKeyListener; import net.itsuha.android.zip4j.PasswordDialog.PasswordDialogCallback; import net.itsuha.android.zip4j.ZipEntry.ZipDirectory; import net.lingala.zip4j.core.ZipFile; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.FileHeader; import net.lingala.zip4j.util.Zip4jUtil; import org.apache.commons.io.IOUtils; import android.content.Context; import android.os.Bundle; import android.os.Environment; import android.support.v4.app.ListFragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.Loader; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.text.Collator; import java.text.DateFormat; import java.util.Collection; import java.util.Comparator; import java.util.Date; import java.util.List; /** * Perform alphabetical comparison of application entry objects. */ public class ZipListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<List<ZipEntry>> , FragmentBackKeyListener, PasswordDialogCallback { private static final int LOADER_ID = 0; public static final String ARGUMENT_ZIP_FILE = "arg_zipfile"; private static final String FRAGMENT_TAG = "password"; // This is the Adapter being used to display the list's data. ZipListAdapter mAdapter; // If non-null, this is the current filter the user has provided. String mCurFilter; ZipListLoader mLoader; String mFileName; String mPassword; @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // Give some text to display if there is no data. In a real // application this would come from a resource. setEmptyText("No files"); // We have a menu item to show in action bar. setHasOptionsMenu(true); // Create an empty adapter we will use to display the loaded data. mFileName = getArguments().getString(ARGUMENT_ZIP_FILE); mAdapter = new ZipListAdapter(getActivity()); setListAdapter(mAdapter); // Start out with a progress indicator. setListShown(false); // Prepare the loader. Either re-connect with an existing one, // or start a new one. getLoaderManager().initLoader(LOADER_ID, null, this).forceLoad(); } @Override public void onListItemClick(ListView l, View v, int position, long id) { ZipEntry item = (ZipEntry) l.getItemAtPosition(position); if (item instanceof ZipDirectory) { mLoader.cd(item.getName()); } else { if (item.getFileHeader().isEncrypted()) { if (TextUtils.isEmpty(mPassword)) { PasswordDialog dialog = new PasswordDialog(); Bundle args = new Bundle(); args.putSerializable(PasswordDialog.ARGUMENT_ZIPENTRY, item); dialog.setArguments(args); dialog.setTargetFragment(this, 0); dialog.show(getFragmentManager(), FRAGMENT_TAG); } else { openZipFile(item, mPassword); } } else { openZipFile(item, null); } } } private void openZipFile(ZipEntry entry, String password) { FileOutputStream fos = null; InputStream is = null; File f = null; try { String path = Environment.getExternalStorageDirectory() + "/" + entry.getName(); f = new File(path); fos = new FileOutputStream(f); final ZipFile z = mLoader.mZipFile; if (!TextUtils.isEmpty(password)) { z.setPassword(password); } is = z.getInputStream(entry.getFileHeader()); IOUtils.copy(is, fos); fos.close(); is.close(); } catch (ZipException e) { Toast.makeText(getActivity(), "Wrong password", Toast.LENGTH_LONG).show(); mPassword = null; if (f != null) { f.delete(); } e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { Toast.makeText(getActivity(), "Wrong password", Toast.LENGTH_LONG).show(); mPassword = null; if (f != null) { f.delete(); } e.printStackTrace(); } finally { IOUtils.closeQuietly(fos); IOUtils.closeQuietly(is); } } @Override public Loader<List<ZipEntry>> onCreateLoader(int id, Bundle args) { // This is called when a new Loader needs to be created. This // sample only has one Loader with no arguments, so it is simple. mLoader = new ZipListLoader(getActivity(), mFileName); return mLoader; } @Override public void onLoadFinished(Loader<List<ZipEntry>> loader, List<ZipEntry> data) { // Set the new data in the adapter. mAdapter.setData(data); // The list should now be shown. if (isResumed()) { setListShown(true); } else { setListShownNoAnimation(true); } } @Override public void onLoaderReset(Loader<List<ZipEntry>> loader) { // Clear the data in the adapter. mAdapter.setData(null); } @Override public boolean onBackKeyUp() { if (mLoader.canGoUp()) { mLoader.up(); return true; } else { return false; } } /* * (non-Javadoc) * @see * net.itsuha.android.zip4j.PasswordDialog.PasswordDialogCallback#onOkButtonPressed(net.itsuha * .android.zip4j.ZipEntry, java.lang.String) */ @Override public void onOkButtonPressed(ZipEntry entry, String password) { mPassword = password; openZipFile(entry, password); } /* * (non-Javadoc) * @see net.itsuha.android.zip4j.PasswordDialog.PasswordDialogCallback#onCancel() */ @Override public void onCancel() { // TODO Auto-generated method stub } /** * A custom Loader that loads all of the installed applications. */ public static class ZipListLoader extends AsyncTaskLoader<List<ZipEntry>> { List<ZipEntry> mItems; ZipDirectory mBackendTree = null; Context mContext; ZipFile mZipFile; String mFileName; public ZipListLoader(Context context, String fileName) { super(context); mContext = context.getApplicationContext(); mFileName = fileName; } /** * This is where the bulk of our work is done. This function is called * in a background thread and should generate a new set of data to be * published by the loader. */ @Override public List<ZipEntry> loadInBackground() { if (mBackendTree == null) { ZipDirectory root = null; try { root = readZipInFilesDir(); } catch (ZipException e) { // TODO Auto-generated catch block e.printStackTrace(); } mBackendTree = root; } // Create corresponding array of entries and load their labels. // Sort the list. // Done! return mBackendTree.getChildren(); } private ZipDirectory readZipInFilesDir() throws ZipException { File f = new File(mContext.getFilesDir(), mFileName); ZipFile z = new ZipFile(f); mZipFile = z; z.setFileNameCharset("Shift_JIS"); ZipDirectory root = new ZipDirectory(); @SuppressWarnings("unchecked") List<FileHeader> l = (List<FileHeader>) z.getFileHeaders(); for (FileHeader h : l) { if (h.isDirectory()) { root.addDirectory(h.getFileName()); } else { root.addFile(h.getFileName(), h); } } return root; } /** * Called when there is new data to deliver to the client. The super * class will take care of delivering it; the implementation here just * adds a little more logic. */ @Override public void deliverResult(List<ZipEntry> items) { if (isReset()) { // An async query came in while the loader is stopped. We // don't need the result. if (items != null) { onReleaseResources(items); } } List<ZipEntry> oldItems = items; mItems = items; if (isStarted()) { // If the Loader is currently started, we can immediately // deliver its results. super.deliverResult(items); } // At this point we can release the resources associated with // 'oldItems' if needed; now that the new result is delivered we // know that it is no longer in use. if (oldItems != null) { onReleaseResources(oldItems); } } /** * Handles a request to start the Loader. */ @Override protected void onStartLoading() { if (mItems != null) { // If we currently have a result available, deliver it // immediately. deliverResult(mItems); } } /** * Handles a request to stop the Loader. */ @Override protected void onStopLoading() { // Attempt to cancel the current load task if possible. cancelLoad(); } /** * Handles a request to cancel a load. */ @Override public void onCanceled(List<ZipEntry> items) { super.onCanceled(items); // At this point we can release the resources associated with 'apps' // if needed. onReleaseResources(items); } /** * Handles a request to completely reset the Loader. */ @Override protected void onReset() { super.onReset(); // Ensure the loader is stopped onStopLoading(); // At this point we can release the resources associated with 'apps' // if needed. if (mItems != null) { onReleaseResources(mItems); mItems = null; } } /** * Helper function to take care of releasing resources associated with * an actively loaded data set. */ protected void onReleaseResources(List<ZipEntry> apps) { // For a simple List<> there is nothing to do. For something // like a Cursor, we would close it here. } public void cd(String fileName) { mBackendTree = mBackendTree.changeDirectory(fileName); onContentChanged(); } public boolean canGoUp() { return mBackendTree.hasParent(); } public void up() { mBackendTree = mBackendTree.getParent(); onContentChanged(); } /** * Perform alphabetical comparison of application entry objects. */ public static final Comparator<ZipEntry> ALPHA_COMPARATOR = new Comparator<ZipEntry>() { private final Collator sCollator = Collator.getInstance(); @Override public int compare(ZipEntry object1, ZipEntry object2) { return sCollator.compare(object1.getName(), object2.getName()); } }; } public static class ZipListAdapter extends ArrayAdapter<ZipEntry> { private final LayoutInflater mInflater; public ZipListAdapter(Context context) { super(context, android.R.layout.simple_list_item_2); mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } public void setData(Collection<ZipEntry> data) { clear(); if (data != null) { for (ZipEntry e : data) { add(e); } } } /** * Populate new items in the list. */ @Override public View getView(int position, View convertView, ViewGroup parent) { View view; if (convertView == null) { view = mInflater.inflate(R.layout.zip_list_items, parent, false); } else { view = convertView; } ZipEntry item = getItem(position); if (item instanceof ZipDirectory) { ((TextView) view.findViewById(R.id.ziplist_filename)) .setText(item.getName()); ((TextView) view.findViewById(R.id.ziplist_size)) .setText(""); ((TextView) view.findViewById(R.id.ziplist_date)) .setText(""); } else { view.findViewById(R.id.ziplist_icon).setVisibility(View.INVISIBLE); ((TextView) view.findViewById(R.id.ziplist_filename)) .setText(item.getName()); FileHeader h = item.getFileHeader(); ((TextView) view.findViewById(R.id.ziplist_size)) .setText(String.valueOf(h.getUncompressedSize())); long time = Zip4jUtil.dosToJavaTme(h.getLastModFileTime()); DateFormat sdf = getDateFormatInstance(); ((TextView) view.findViewById(R.id.ziplist_date)) .setText(sdf.format(new Date(time))); } return view; } private DateFormat mSDF; private DateFormat getDateFormatInstance() { if (mSDF == null) { mSDF = DateFormat.getDateTimeInstance(); } return mSDF; } } }