package com.ghostsq.commander.adapters;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.Enumeration;
import java.util.zip.ZipFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import org.apache.http.util.EncodingUtils;
import android.content.Context;
import android.net.Uri;
import android.os.Handler;
import android.util.Log;
import android.util.SparseBooleanArray;
import com.ghostsq.commander.Commander;
import com.ghostsq.commander.Panels;
import com.ghostsq.commander.R;
import com.ghostsq.commander.adapters.CommanderAdapter;
import com.ghostsq.commander.adapters.CommanderAdapterBase;
import com.ghostsq.commander.utils.ForwardCompat;
import com.ghostsq.commander.utils.Utils;
public class ZipAdapter extends CommanderAdapterBase {
public static final String TAG = "ZipAdapter";
protected static final int BLOCK_SIZE = 100000;
// Java compiler creates a thunk function to access to the private owner class member from a subclass
// to avoid that all the member accessible from the subclasses are public
public Uri uri = null;
public ZipFile zip = null;
public ZipEntry[] items = null;
private ZipEntry cachedEntry = null;
public ZipAdapter( Context ctx_ ) {
super( ctx_ );
parentLink = PLS;
}
@Override
public int getType() {
return CA.ZIP;
}
@Override
public boolean readSource( Uri tmp_uri, String pass_back_on_done ) {
try {
if( tmp_uri != null )
uri = tmp_uri;
if( uri == null )
return false;
if( reader != null ) { // that's not good.
if( reader.isAlive() ) {
commander.showInfo( ctx.getString( R.string.busy ) );
reader.interrupt();
Thread.sleep( 500 );
if( reader.isAlive() )
return false;
}
}
Log.v( TAG, "reading " + uri );
notify( Commander.OPERATION_STARTED );
reader = new ListEngine( readerHandler, pass_back_on_done );
reader.start();
return true;
}
catch( Exception e ) {
commander.showError( "Exception: " + e );
e.printStackTrace();
}
notify( "Fail", Commander.OPERATION_FAILED );
return false;
}
class EnumEngine extends Engine {
protected EnumEngine() {
}
protected EnumEngine( Handler h ) {
super.setHandler( h );
}
protected final ZipEntry[] GetFolderList( String fld_path ) {
if( zip == null ) return null;
if( fld_path == null ) fld_path = "";
else
if( fld_path.length() > 0 && fld_path.charAt( 0 ) == SLC )
fld_path = fld_path.substring( 1 );
int fld_path_len = fld_path.length();
if( fld_path_len > 0 && fld_path.charAt( fld_path_len - 1 ) != SLC ) {
fld_path = fld_path + SLC;
fld_path_len++;
}
Enumeration<? extends ZipEntry> entries = zip.entries();
if( entries == null )
return null;
ArrayList<ZipEntry> array = new ArrayList<ZipEntry>();
while( entries.hasMoreElements() ) {
if( isStopReq() ) return null;
ZipEntry e = entries.nextElement();
if( e != null ) {
String entry_name = fixName( e );
//Log.v( TAG, "Found an Entry: " + entry_name );
if( entry_name == null || fld_path.compareToIgnoreCase(entry_name) == 0 )
continue;
/* There are at least two kinds of zips - with dedicated folder entry and without one.
* The code below should process both.
* Do not change until you fully understand how it works.
*/
if( fld_path.regionMatches( true, 0, entry_name, 0, fld_path_len ) ) {
int sl_pos = entry_name.indexOf( SLC, fld_path_len );
if( sl_pos > 0 ) {
String sub_dir = entry_name.substring( fld_path_len, sl_pos+1 );
int sub_dir_len = sub_dir.length();
boolean not_yet = true;
for( int i = 0; i < array.size(); i++ ) {
String a_name = fixName( array.get( i ) );
if( a_name.regionMatches( fld_path_len, sub_dir, 0, sub_dir_len ) ) {
not_yet = false;
break;
}
}
if( not_yet ) { // a folder
ZipEntry sur_fld = new ZipEntry( entry_name.substring( 0, sl_pos+1 ) );
byte[] eb = { 1, 2 };
sur_fld.setExtra( eb );
array.add( sur_fld );
}
}
else
array.add( e ); // a leaf
}
}
}
return array.toArray( new ZipEntry[array.size()] );
}
}
class ListEngine extends EnumEngine {
private ZipEntry[] items_tmp = null;
public String pass_back_on_done;
ListEngine( Handler h, String pass_back_on_done_ ) {
super( h );
pass_back_on_done = pass_back_on_done_;
}
public ZipEntry[] getItems() {
return items_tmp;
}
@Override
public void run() {
try {
if( uri != null ) {
String zip_path = uri.getPath();
if( zip_path != null ) {
zip = new ZipFile( zip_path );
String cur_path = null;
try {
cur_path = uri.getFragment();
}
catch( NullPointerException e ) {
// it happens only when the Uri is built by Uri.Builder
Log.e( TAG, "uri.getFragment()", e );
}
items_tmp = GetFolderList( cur_path );
if( items_tmp != null ) {
ZipItemPropComparator comp = new ZipItemPropComparator( mode & MODE_SORTING, (mode & MODE_CASE) != 0, ascending );
Arrays.sort( items_tmp, comp );
sendProgress( null, Commander.OPERATION_COMPLETED, pass_back_on_done );
return;
}
}
}
}
catch( Exception e ) {
Log.e( TAG, "ListEngine: " + pass_back_on_done, e );
sendProgress( e.getLocalizedMessage(), Commander.OPERATION_FAILED, pass_back_on_done );
}
finally {
super.run();
}
sendProgress( "ZIP error", Commander.OPERATION_FAILED, pass_back_on_done );
}
}
@Override
protected void onReadComplete() {
if( reader instanceof ListEngine ) {
ListEngine list_engine = (ListEngine)reader;
ZipEntry[] tmp_items = list_engine.getItems();
if( tmp_items != null && ( mode & MODE_HIDDEN ) == HIDE_MODE ) {
int cnt = 0;
for( int i = 0; i < tmp_items.length; i++ )
if( tmp_items[i].getName().charAt( 0 ) != '.' )
cnt++;
items = new ZipEntry[cnt];
int j = 0;
for( int i = 0; i < tmp_items.length; i++ )
if( tmp_items[i].getName().charAt( 0 ) != '.' )
items[j++] = tmp_items[i];
}
else
items = tmp_items;
numItems = items != null ? items.length + 1 : 1;
notifyDataSetChanged();
}
}
@Override
public String toString() {
return uri != null ? Uri.decode( uri.toString() ) : "";
}
/*
* CommanderAdapter implementation
*/
@Override
public Uri getUri() {
return uri;
}
@Override
public void setUri( Uri uri_ ) {
uri = uri_;
}
@Override
public void setIdentities( String name, String pass ) {
}
@Override
public void reqItemsSize( SparseBooleanArray cis ) {
notify( "Not supported.", Commander.OPERATION_FAILED );
}
@Override
public boolean copyItems( SparseBooleanArray cis, CommanderAdapter to, boolean move ) {
try {
if( zip == null )
throw new RuntimeException( "Invalid ZIP" );
ZipEntry[] subItems = bitsToItems( cis );
if( subItems == null )
throw new RuntimeException( "Nothing to extract" );
if( !checkReadyness() ) return false;
Engines.IReciever recipient = null;
File dest = null;
if( to instanceof FSAdapter ) {
dest = new File( to.toString() );
if( !dest.exists() ) dest.mkdirs();
if( !dest.isDirectory() )
throw new RuntimeException( ctx.getString( R.string.dest_exist ) );
} else {
dest = new File( createTempDir() );
recipient = to.getReceiver();
}
notify( Commander.OPERATION_STARTED );
commander.startEngine( new CopyFromEngine( subItems, dest, recipient ) );
return true;
}
catch( Exception e ) {
commander.showError( "Exception: " + e.getMessage() );
}
return false;
}
class CopyFromEngine extends EnumEngine
{
private File dest_folder;
private ZipEntry[] mList = null;
private String base_pfx;
private int base_len;
CopyFromEngine( ZipEntry[] list, File dest, Engines.IReciever recipient_ ) {
recipient = recipient_; // member of a superclass
mList = list;
dest_folder = dest;
try {
base_pfx = uri.getFragment();
if( base_pfx == null )
base_pfx = "";
base_len = base_pfx.length();
}
catch( NullPointerException e ) {
Log.e( TAG, "", e );
}
}
@Override
public void run() {
sendProgress( ZipAdapter.this.ctx.getString( R.string.wait ), 1, 1 );
synchronized( ZipAdapter.this ) {
int total = copyFiles( mList, "" );
if( recipient != null ) {
sendReceiveReq( dest_folder );
return;
}
sendResult( Utils.getOpReport( ctx, total, R.string.unpacked ) );
}
super.run();
}
private final int copyFiles( ZipEntry[] list, String path ) {
int counter = 0;
try {
long dir_size = 0, byte_count = 0;
for( int i = 0; i < list.length; i++ ) {
ZipEntry f = list[i];
if( !f.isDirectory() )
dir_size += f.getSize();
}
double conv = 100./(double)dir_size;
for( int i = 0; i < list.length; i++ ) {
ZipEntry entry = list[i];
if( entry == null ) continue;
String entry_name_fixed = fixName( entry );
if( entry_name_fixed == null ) continue;
String file_name = new File( entry_name_fixed ).getName();
File dest_file = new File( dest_folder, path + file_name );
String rel_name = entry_name_fixed.substring( base_len );
if( entry.isDirectory() ) {
if( !dest_file.mkdir() ) {
if( !dest_file.exists() || !dest_file.isDirectory() ) {
errMsg = "Can't create folder \"" + dest_file.getAbsolutePath() + "\"";
break;
}
}
ZipEntry[] subItems = GetFolderList( entry_name_fixed );
if( subItems == null ) {
errMsg = "Failed to get the file list of the subfolder '" + rel_name + "'.\n";
break;
}
counter += copyFiles( subItems, rel_name );
if( errMsg != null ) break;
}
else {
if( dest_file.exists() ) {
int res = askOnFileExist( ctx.getString( R.string.file_exist, dest_file.getAbsolutePath() ), commander );
if( res == Commander.ABORT ) break;
if( res == Commander.SKIP ) continue;
if( res == Commander.REPLACE ) {
if( !dest_file.delete() ) {
error( ctx.getString( R.string.cant_del, dest_file.getAbsoluteFile() ) );
break;
}
}
}
InputStream in = zip.getInputStream( entry );
FileOutputStream out = new FileOutputStream( dest_file );
byte buf[] = new byte[BLOCK_SIZE];
int n = 0;
int so_far = (int)(byte_count * conv);
int fnl = rel_name.length();
String unp_msg = ctx.getString( R.string.unpacking,
fnl > CUT_LEN ? "\u2026" + rel_name.substring( fnl - CUT_LEN ) : rel_name );
while( true ) {
n = in.read( buf );
if( n < 0 ) break;
out.write( buf, 0, n );
byte_count += n;
sendProgress( unp_msg, so_far, (int)(byte_count * conv) );
if( stop || isInterrupted() ) {
in.close();
out.close();
dest_file.delete();
errMsg = "File '" + dest_file.getName() + "' was not completed, delete.";
break;
}
}
}
final int GINGERBREAD = 9;
if( android.os.Build.VERSION.SDK_INT >= GINGERBREAD )
ForwardCompat.setFullPermissions( dest_file );
if( stop || isInterrupted() ) {
error( ctx.getString( R.string.canceled ) );
break;
}
if( i >= list.length-1 )
sendProgress( ctx.getString( R.string.unpacked_p, rel_name ), (int)(byte_count * conv) );
counter++;
}
}
catch( Exception e ) {
Log.e( TAG, "copyFiles()", e );
error( "Exception: " + e.getMessage() );
}
return counter;
}
}
@Override
public boolean createFile( String fileURI ) {
notify( "Operation not supported", Commander.OPERATION_FAILED );
return false;
}
@Override
public void createFolder( String string ) {
notify( "Not supported", Commander.OPERATION_FAILED );
}
@Override
public boolean deleteItems( SparseBooleanArray cis ) {
try {
if( !checkReadyness() ) return false;
ZipEntry[] to_delete = bitsToItems( cis );
if( to_delete != null && zip != null && uri != null ) {
notify( Commander.OPERATION_STARTED );
commander.startEngine( new DelEngine( new File( uri.getPath() ), to_delete ) );
return true;
}
}
catch( Exception e ) {
Log.e( TAG, "deleteItems()", e );
}
notify( null, Commander.OPERATION_FAILED );
return false;
}
class DelEngine extends Engine {
private ZipEntry[] mList = null;
private File zipFile;
DelEngine( File zipFile_, ZipEntry[] list ) {
zipFile = zipFile_;
mList = list;
}
@Override
public void run() {
if( zip == null ) return;
sendProgress( ZipAdapter.this.ctx.getString( R.string.wait ), 1, 1 );
synchronized( ZipAdapter.this ) {
Init( null );
File old_file = new File( zipFile.getAbsolutePath() + "_tmp_" + ( new Date() ).getSeconds() + ".zip" );
try {
ZipFile zf = new ZipFile( zipFile );
int removed = 0, processed = 0, num_entries = zf.size();
long total_size = zipFile.length(), bytes_saved = 0;
final String del = ctx.getString( R.string.deleting_a );
if( !zipFile.renameTo( old_file ) ) {
error( "could not rename the file " + zipFile.getAbsolutePath() + " to " + old_file.getAbsolutePath() );
} else {
ZipInputStream zin = new ZipInputStream( new FileInputStream( old_file ) );
ZipOutputStream out = new ZipOutputStream( new FileOutputStream( zipFile ) );
byte[] buf = new byte[BLOCK_SIZE];
ZipEntry entry = zin.getNextEntry();
while( entry != null ) {
if( isStopReq() )
break;
String name = entry.getName();
boolean spare_this = true;
for( ZipEntry z : mList ) {
if( isStopReq() )
break;
String name_to_delete = z.getName();
if( name.startsWith( name_to_delete ) ) {
spare_this = false;
removed++;
break;
}
}
if( spare_this ) {
int pp = ++processed * 100 / num_entries;
// Add ZIP entry to output stream.
out.putNextEntry( new ZipEntry( name ) );
// Transfer bytes from the ZIP file to the output file
int len;
while( ( len = zin.read( buf ) ) > 0 ) {
if( isStopReq() )
break;
out.write( buf, 0, len );
bytes_saved += len;
sendProgress( del, pp, (int)( bytes_saved * 100 / total_size ) );
}
}
entry = zin.getNextEntry();
}
// Close the streams
zin.close();
try {
out.close();
} catch( Exception e ) {
Log.e( TAG, "DelEngine.run()->out.close()", e );
}
if( isStopReq() ) {
zipFile.delete();
old_file.renameTo( zipFile );
processed = 0;
error( s( R.string.interrupted ) );
} else {
old_file.delete();
zip = null;
sendResult( Utils.getOpReport( ctx, removed, R.string.deleted ) );
return;
}
}
} catch( Exception e ) {
error( e.getMessage() );
}
sendResult( Utils.getOpReport( ctx, 0, R.string.deleted ) );
super.run();
}
}
}
@Override
public Uri getItemUri( int position ) {
if( uri == null ) return null;
return uri.buildUpon().encodedFragment( fixName( items[position-1] ) ).build();
}
@Override
public String getItemName( int position, boolean full ) {
if( items != null && position > 0 && position <= items.length ) {
if( full ) {
if( uri != null ) {
Uri item_uri = getItemUri( position );
if( item_uri != null )
return item_uri.toString();
}
}
else
return new File( fixName( items[position-1] ) ).getName();
}
return null;
}
@Override
public void openItem( int position ) {
if( position == 0 ) { // ..
if( uri != null ) {
String cur = null;
try {
cur = uri.getFragment();
} catch( Exception e ) {
}
if( cur == null || cur.length() == 0 ||
( cur.length() == 1 && cur.charAt( 0 ) == SLC ) ) {
File zip_file = new File( uri.getPath() );
String parent_dir = Utils.escapePath( zip_file.getParent() );
commander.Navigate( Uri.parse( parent_dir != null ? parent_dir : Panels.DEFAULT_LOC ), null,
zip_file.getName() );
}
else {
File cur_f = new File( cur );
String parent_dir = cur_f.getParent();
commander.Navigate( uri.buildUpon().fragment( parent_dir != null ? parent_dir : "" ).build(), null, cur_f.getName() );
}
}
return;
}
if( items == null || position < 0 || position > items.length )
return;
ZipEntry item = items[position - 1];
if( item.isDirectory() ) {
/*
String cur = null;
try {
cur = uri.getFragment();
}
catch( NullPointerException e ) {}
if( cur == null )
cur = "";
else
if( cur.length() == 0 || cur.charAt( cur.length()-1 ) != SLC )
cur += SLS;
*/
commander.Navigate( uri.buildUpon().fragment( fixName( item ) ).build(), null, null );
} else {
commander.Open( uri.buildUpon().fragment( fixName( item ) ).build(), null );
}
}
@Override
public boolean receiveItems( String[] uris, int move_mode ) {
try {
if( !checkReadyness() ) return false;
if( uris == null || uris.length == 0 ) {
notify( s( R.string.copy_err ), Commander.OPERATION_FAILED );
return false;
}
File[] list = Utils.getListOfFiles( uris );
if( list == null ) {
notify( "Something wrong with the files", Commander.OPERATION_FAILED );
return false;
}
notify( Commander.OPERATION_STARTED );
zip = null;
items = null;
commander.startEngine( new CopyToEngine( list, new File( uri.getPath() ), uri.getFragment(), move_mode ) );
return true;
} catch( Exception e ) {
notify( "Exception: " + e.getMessage(), Commander.OPERATION_FAILED );
}
return false;
}
public boolean createZip( File[] list, String zip_fn ) {
try {
if( !checkReadyness() ) return false;
notify( Commander.OPERATION_STARTED );
commander.startEngine( new CopyToEngine( list, new File( zip_fn ) ) );
return true;
} catch( Exception e ) {
notify( "Exception: " + e.getMessage(), Commander.OPERATION_FAILED );
}
return false;
}
class CopyToEngine extends Engine {
private File[] topList;
private int basePathLen;
private File zipFile;
private String destPath;
private long totalSize = 0;
private boolean newZip = false;
private boolean move = false;
private boolean del_src_dir = false;
private String prep;
/**
* Add files to existing zip
*/
CopyToEngine( File[] list, File zip_file, String dest_sub, int move_mode_ ) {
topList = list;
zipFile = zip_file;
if( dest_sub != null )
destPath = dest_sub.endsWith( SLS ) ? dest_sub : dest_sub + SLS;
else
destPath = "";
basePathLen = list.length > 0 ? list[0].getParent().length() + 1 : 0;
move = ( move_mode_ & MODE_MOVE ) != 0;
del_src_dir = ( move_mode_ & CommanderAdapter.MODE_DEL_SRC_DIR ) != 0;
}
/**
* Create a new shiny ZIP
*/
CopyToEngine( File[] list, File zip_file ) {
topList = list;
zipFile = zip_file;
destPath = "";
basePathLen = list.length > 0 ? list[0].getParent().length() + 1 : 0;
newZip = true;
prep = ZipAdapter.this.ctx.getString( R.string.preparing );
}
@Override
public void run() {
int num_files = 0;
try {
sendProgress( prep, 1, 1 );
synchronized( ZipAdapter.this ) {
Init( null );
ArrayList<File> full_list = new ArrayList<File>( topList.length );
totalSize = addToList( topList, full_list );
sendProgress( prep, 2, 2 );
num_files = addFilesToZip( full_list );
if( del_src_dir ) {
File src_dir = topList[0].getParentFile();
if( src_dir != null )
src_dir.delete();
}
}
} catch( Exception e ) {
error( "Exception: " + e.getMessage() );
}
sendResult( Utils.getOpReport( ctx, num_files, R.string.packed ) );
super.run();
}
// adds files to the global full_list, and returns the total size
private final long addToList( File[] sub_list, ArrayList<File> full_list ) {
long total_size = 0;
try {
for( int i = 0; i < sub_list.length; i++ ) {
if( stop || isInterrupted() ) {
errMsg = "Canceled";
break;
}
File f = sub_list[i];
if( f != null && f.exists() ) {
if( f.isFile() ) {
total_size += f.length();
full_list.add( f );
}
else
if( f.isDirectory() ) {
long dir_sz = addToList( f.listFiles(), full_list );
if( errMsg != null ) break;
if( dir_sz == 0 )
full_list.add( f );
else
total_size += dir_sz;
}
}
}
}
catch( Exception e ) {
Log.e( TAG, "addToList()", e );
errMsg = "Exception: " + e.getMessage();
}
return total_size;
}
// the following method was based on the one from http://snippets.dzone.com/posts/show/3468
private final int addFilesToZip( ArrayList<File> files ) throws IOException {
File old_file = null;
try {
byte[] buf = new byte[BLOCK_SIZE];
ZipOutputStream out;
if( newZip ) {
out = new ZipOutputStream( new FileOutputStream( zipFile ) );
}
else {
ZipFile zf = new ZipFile( zipFile );
int num_entries = zf.size();
long total_size = zipFile.length(), bytes_saved = 0;
old_file = new File( zipFile.getAbsolutePath() + "_tmp_" + (new Date()).getSeconds() + ".zip" );
if( !zipFile.renameTo( old_file ) )
throw new RuntimeException("could not rename the file " + zipFile.getAbsolutePath() + " to " + old_file.getAbsolutePath() );
ZipInputStream zin = new ZipInputStream( new FileInputStream( old_file ) );
out = new ZipOutputStream( new FileOutputStream( zipFile ) );
int e_i = 0, pp;
ZipEntry entry = zin.getNextEntry();
while( entry != null ) {
if( isStopReq() ) break;
pp = e_i++ * 100 / num_entries;
sendProgress( prep, pp, 0 );
String name = entry.getName(); // in this case the name is not corrupted! no need to fix
boolean notInFiles = true;
for( File f : files ) {
if( isStopReq() ) break;
String f_path = f.getAbsolutePath();
if( f_path.regionMatches( true, basePathLen, name, 0, name.length() ) ) {
notInFiles = false;
break;
}
}
if( notInFiles ) {
// Add ZIP entry to output stream.
out.putNextEntry( new ZipEntry( name ) );
// Transfer bytes from old ZIP file to the output file
int len;
while( (len = zin.read( buf )) > 0 ) {
if( isStopReq() ) break;
out.write(buf, 0, len);
bytes_saved += len;
sendProgress( prep, pp, (int)(bytes_saved * 100 / total_size) );
}
}
entry = zin.getNextEntry();
}
// Close the streams
zin.close();
if( isStopReq() ) {
out.close();
zipFile.delete();
old_file.renameTo( zipFile );
return 0;
}
}
double conv = PERC/(double)totalSize;
long byte_count = 0;
// Compress the files
int i;
for( i = 0; i < files.size(); i++ ) {
if( isStopReq() ) break;
File f = files.get( i );
// Add ZIP entry to output stream.
String fn = f.getAbsolutePath();
String rfn = destPath + fn.substring( basePathLen );
if( f.isDirectory() ) {
out.putNextEntry( new ZipEntry( rfn + SLS ) );
}
else {
out.putNextEntry( new ZipEntry( rfn ) );
// Transfer bytes from the file to the ZIP file
int fnl = fn.length();
String pack_s = ctx.getString( R.string.packing,
fnl > CUT_LEN ? "\u2026" + fn.substring( fnl - CUT_LEN ) : fn );
InputStream in = new FileInputStream( f );
int len;
int so_far = (int)(byte_count * conv);
while( (len = in.read( buf )) > 0 ) {
if( isStopReq() ) break;
out.write(buf, 0, len);
byte_count += len;
sendProgress( pack_s, so_far, (int)(byte_count * conv) );
}
// Complete the entry
in.close();
}
out.closeEntry();
//Log.v( TAG, "Packed: " + rfn );
if( move )
f.delete();
}
// Complete the ZIP file
out.close();
if( isStopReq() ) {
zipFile.delete();
if( !newZip )
old_file.renameTo( zipFile );
return 0;
}
if( !newZip )
old_file.delete();
return i;
}
catch( Exception e ) {
error( e.getMessage() );
e.printStackTrace();
if( !newZip ) {
zipFile.delete();
if( !newZip && old_file != null )
old_file.renameTo( zipFile );
}
return 0;
}
}
}
@Override
public boolean renameItem( int position, String newName, boolean c ) {
// TODO
return false;
}
@Override
public void prepareToDestroy() {
super.prepareToDestroy();
items = null;
}
/*
* BaseAdapter implementation
*/
@Override
public Object getItem( int position ) {
Item item = new Item();
item.name = "";
{
if( position == 0 ) {
item.name = parentLink;
}
else {
if( items != null && position > 0 && position <= items.length ) {
ZipEntry zip_entry = items[position - 1];
item.dir = zip_entry.isDirectory();
String name = fixName( zip_entry );
int lsp = name.lastIndexOf( SLC, item.dir ? name.length() - 2 : name.length() );
item.name = lsp > 0 ? name.substring( lsp + 1 ) : name;
item.size = zip_entry.getSize();
long item_time = zip_entry.getTime();
item.date = item_time > 0 ? new Date( item_time ) : null;
}
}
}
return item;
}
private final String fixName( ZipEntry entry ) {
try {
String entry_name = entry.getName();
if( android.os.Build.VERSION.SDK_INT >= 10 )
return entry_name; // already fixed?
byte[] ex = entry.getExtra();
if( ex != null && ex.length == 2 && ex[0] == 1 && ex[1] == 2 )
return entry_name;
byte bytes[];
/*
bytes = EncodingUtils.getAsciiBytes( entry_name );
bytes = EncodingUtils.getBytes( entry_name, "windows-1250" );
*/
bytes = EncodingUtils.getBytes( entry_name, "iso-8859-1" );
return new String( bytes );
} catch( Exception e ) {
e.printStackTrace();
}
return null;
}
private final ZipEntry[] bitsToItems( SparseBooleanArray cis ) {
try {
int counter = 0;
for( int i = 0; i < cis.size(); i++ )
if( cis.valueAt( i ) )
counter++;
ZipEntry[] subItems = new ZipEntry[counter];
int j = 0;
for( int i = 0; i < cis.size(); i++ )
if( cis.valueAt( i ) )
subItems[j++] = items[ cis.keyAt( i ) - 1 ];
return subItems;
} catch( Exception e ) {
Log.e( TAG, "", e );
}
return null;
}
private final boolean checkReadyness()
{
// FIXME check that the zip file is processed by some other engine!!!!!!!!!!!!
/*
if( ??? ) {
notify( ctx.getString( R.string.busy ), Commander.OPERATION_FAILED );
return false;
}
*/
return true;
}
public class ZipItemPropComparator implements Comparator<ZipEntry> {
int type;
boolean case_ignore, ascending;
public ZipItemPropComparator( int type_, boolean case_ignore_, boolean ascending_ ) {
type = type_;
case_ignore = case_ignore_;
ascending = ascending_;
}
@Override
public int compare( ZipEntry f1, ZipEntry f2 ) {
boolean f1IsDir = f1.isDirectory();
boolean f2IsDir = f2.isDirectory();
if( f1IsDir != f2IsDir )
return f1IsDir ? -1 : 1;
int ext_cmp = 0;
switch( type ) {
case SORT_EXT:
ext_cmp = case_ignore ?
Utils.getFileExt( f1.getName() ).compareToIgnoreCase( Utils.getFileExt( f2.getName() ) ) :
Utils.getFileExt( f1.getName() ).compareTo( Utils.getFileExt( f2.getName() ) );
break;
case SORT_SIZE:
ext_cmp = f1.getSize() - f2.getSize() < 0 ? -1 : 1;
break;
case SORT_DATE:
ext_cmp = f1.getTime() - f2.getTime() < 0 ? -1 : 1;
break;
}
if( ext_cmp == 0 )
ext_cmp = case_ignore ? f1.getName().compareToIgnoreCase( f2.getName() ) : f1.getName().compareTo( f2.getName() );
return ascending ? ext_cmp : -ext_cmp;
}
}
@Override
protected void reSort() {
if( items == null ) return;
ZipItemPropComparator comp = new ZipItemPropComparator( mode & MODE_SORTING, (mode & MODE_CASE) != 0, ascending );
Arrays.sort( items, comp );
}
@Override
public Item getItem( Uri u ) {
try {
String zip_path = u.getPath();
if( zip_path == null ) return null;
String opened_zip_path = uri != null ? uri.getPath() : null;
if( opened_zip_path == null )
zip = new ZipFile( zip_path );
else if( !zip_path.equalsIgnoreCase( opened_zip_path ) )
return null; // do not want to reopen the current zip to something else!
String entry_name = u.getFragment();
if( entry_name != null ) {
ZipEntry zip_entry = zip.getEntry( entry_name );
if( zip_entry != null ) {
String name = fixName( zip_entry );
Item item = new Item();
item.dir = zip_entry.isDirectory();
int lsp = name.lastIndexOf( SLC, item.dir ? name.length() - 2 : name.length() );
item.name = lsp > 0 ? name.substring( lsp + 1 ) : name;
item.size = zip_entry.getSize();
long item_time = zip_entry.getTime();
item.date = item_time > 0 ? new Date( item_time ) : null;
return item;
}
}
} catch( Throwable e ) {
e.printStackTrace();
}
return null;
}
@Override
public InputStream getContent( Uri u, long offset ) {
try {
String zip_path = u.getPath();
if( zip_path == null ) return null;
String opened_zip_path = uri != null ? uri.getPath() : null;
if( opened_zip_path == null )
zip = new ZipFile( zip_path );
else if( !zip_path.equalsIgnoreCase( opened_zip_path ) )
return null; // do not want to reopen the current zip to something else!
String entry_name = u.getFragment();
if( entry_name != null ) {
cachedEntry = zip.getEntry( entry_name );
if( cachedEntry != null ) {
InputStream is = zip.getInputStream( cachedEntry );
if( offset > 0 )
is.skip( offset );
return is;
}
}
} catch( Throwable e ) {
e.printStackTrace();
}
return null;
}
@Override
public void closeStream( Closeable is ) {
if( zip != null ) {
try {
zip.close();
} catch( IOException e ) {
e.printStackTrace();
}
zip = null;
}
}
}