package com.ghostsq.commander.adapters; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.ref.SoftReference; import java.nio.channels.ClosedByInterruptException; import java.security.MessageDigest; import java.util.Date; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.Locale; import java.util.Map; import com.ghostsq.commander.Commander; import com.ghostsq.commander.adapters.Engine; import com.ghostsq.commander.adapters.Engines.IReciever; import com.ghostsq.commander.R; import com.ghostsq.commander.utils.ForwardCompat; import com.ghostsq.commander.utils.MnfUtils; import com.ghostsq.commander.utils.Utils; import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.os.Handler; import android.os.Message; import android.os.PowerManager; import android.os.StatFs; import android.provider.BaseColumns; import android.provider.MediaStore.Images.Media; import android.provider.MediaStore.Images.Thumbnails; import android.text.format.DateFormat; import android.text.format.Formatter; import android.util.Log; import android.util.SparseBooleanArray; public class FSAdapter extends CommanderAdapterBase implements Engines.IReciever { private final static String TAG = "FSAdapter"; class FileItem extends Item { public File f = null; public long size = -1; public FileItem( String name ) { f = new File( name ); origin = f; } public FileItem( File f_ ) { f = f_; origin = f; } } private String dirName; protected FileItem[] items; ThumbnailsThread tht = null; public static final Map<Integer, SoftReference<Drawable> > thumbnailCache = new HashMap<Integer, SoftReference<Drawable> >(); public static int[] ext_h = { ".jpg".hashCode(), ".JPG".hashCode(), ".jpeg".hashCode(), ".JPEG".hashCode(), ".png".hashCode(), ".PNG".hashCode(), ".gif".hashCode(), ".GIF".hashCode() }; public FSAdapter( Context ctx_ ) { super( ctx_ ); dirName = null; items = null; } @Override public int getType() { return CA.FS; } @Override public String toString() { return Utils.mbAddSl( dirName ); } /* * CommanderAdapter implementation */ @Override public Uri getUri() { try { return Uri.parse( Utils.escapePath( toString() ) ); } catch( Exception e ) { e.printStackTrace(); } return null; } @Override public void setUri( Uri uri ) { dirName = Utils.mbAddSl( uri.toString() ); } @Override public boolean readSource( Uri d, String pass_back_on_done ) { try { //if( worker != null ) worker.reqStop(); File[] files_ = null; String dir_name = null; File dir = null; String err_msg = null; while( true ) { if( d != null ) dir_name = d.getPath(); else dir_name = dirName; if( dir_name == null ) { notify( s( R.string.inv_path ) + ": " + ( d == null ? "null" : d.toString() ), Commander.OPERATION_FAILED ); Log.e( TAG, "Unable to obtain folder of the folder name" ); return false; } //Log.v( TAG, "readSource() path=" + dir_name ); dir = new File( dir_name ); files_ = dir.listFiles(); if( files_ != null ) break; if( err_msg == null ) err_msg = ctx.getString( R.string.no_such_folder, dir_name ); String parent_path; if( dir == null || ( parent_path = dir.getParent() ) == null || ( d = Uri.parse( parent_path ) ) == null ) { notify( s( R.string.inv_path ), Commander.OPERATION_FAILED ); Log.e( TAG, "Wrong folder '" + dir_name + "'" ); return false; } } dirName = dir_name; items = filesToItems( files_ ); parentLink = dir.getParent() == null ? SLS : PLS; notifyDataSetChanged(); startThumbnailCreation(); notify( pass_back_on_done ); return true; } catch( Exception e ) { Log.e( TAG, "readSource() excception", e ); } catch( OutOfMemoryError err ) { Log.e( TAG, "Out Of Memory", err ); notify( s( R.string.oom_err ), Commander.OPERATION_FAILED ); } return false; } protected void startThumbnailCreation() { if( thumbnail_size_perc > 0 ) { //Log.i( TAG, "thumbnails " + thumbnail_size_perc ); if( tht != null ) tht.interrupt(); tht = new ThumbnailsThread( this, new Handler() { public void handleMessage( Message msg ) { notifyDataSetChanged(); } }, items ); tht.start(); } } protected FileItem[] filesToItems( File[] files_ ) { int num_files = files_.length; int num = num_files; boolean hide = ( mode & MODE_HIDDEN ) == HIDE_MODE; if( hide ) { int cnt = 0; for( int i = 0; i < num_files; i++ ) if( !files_[i].isHidden() ) cnt++; num = cnt; } FileItem[] items_ = new FileItem[num]; int j = 0; for( int i = 0; i < num_files; i++ ) { if( !hide || !files_[i].isHidden() ) { FileItem file_ex = new FileItem( files_[i] ); items_[j++] = file_ex; } } reSort( items_ ); return items_; } class ThumbnailsThread extends Thread { private final static int NOTIFY_THUMBNAIL_CHANGED = 653; private CommanderAdapterBase owner; private ContentResolver cr; private Handler thread_handler; private FileItem[] mList; private BitmapFactory.Options options; private Resources res; private byte[] buf; private int thumb_sz; private final int apk_h = ".apk".hashCode(); private final int[] ext_h = { ".jpg".hashCode(), ".JPG".hashCode(), ".jpeg".hashCode(), ".JPEG".hashCode(), ".png".hashCode(), ".PNG".hashCode(), ".gif".hashCode(), ".GIF".hashCode(), apk_h }; ThumbnailsThread( CommanderAdapterBase owner_, Handler h, FileItem[] list ) { owner = owner_; setName( getClass().getName() ); thread_handler = h; mList = list; buf = new byte[100*1024]; cr = owner.ctx.getContentResolver(); } @Override public void run() { try { if( mList == null ) return; setPriority( Thread.MIN_PRIORITY ); thumb_sz = getImgWidth(); options = new BitmapFactory.Options(); res = ctx.getResources(); int fails_count = 0; boolean visible_only = mList.length > 100; // too many icons for( int a = 0; a < 2; a++ ) { boolean succeeded = true; boolean need_update = false, proc_visible = false, proc_invisible = false; int processed = 0; for( int i = 0; i < mList.length ; i++ ) { visible_only = visible_only || fails_count > 1; //if( visible_only ) Log.v( TAG, "thumb on requests only" ); int n = -1; while( true ) { for( int j = 0; j < mList.length ; j++ ) { if( mList[j].need_thumb ) { n = j; proc_visible = true; //Log.v( TAG, "A thumbnail requested ahead of time!!! " + n + ", " + mList[n].f.getName() ); break; } else { mList[j].remThumbnailIfOld( visible_only ? 10000 : 60000 ); // clear not in use to free memory } } if( !visible_only || proc_visible ) break; //Log.v( TAG, "Tired. Waiting for a request to proceed" ); synchronized( owner ) { owner.wait(); } } proc_invisible = n < 0; if( proc_invisible ) n = i; else i--; if( !proc_visible ) { yield(); sleep( 10 ); } FileItem f = mList[n]; f.need_thumb = false; if( f.isThumbNail() ) continue; // already exist String fn = f.f.getAbsolutePath(); int fn_h = ( fn + " " + f.f.length() ).hashCode(); SoftReference<Drawable> cached_soft = null; synchronized( thumbnailCache ) { cached_soft = thumbnailCache.get( fn_h ); } if( cached_soft != null ) f.setThumbNail( cached_soft.get() ); String ext = Utils.getFileExt( fn ); if( ext == null ) continue; if( ext.equals( ".apk" ) ) f.thumb_is_icon = true; if( !f.isThumbNail() ) { int ext_hash = ext.hashCode(), ht_sz = ext_h.length; boolean not_img = true; for( int j = 0; j < ht_sz; j++ ) { if( ext_hash == ext_h[j] ) { not_img = false; break; } } if( not_img ) { f.no_thumb = true; f.setThumbNail( null ); continue; } //Log.v( TAG, "Creating a thumbnail for " + n + ", " + fn ); if( createThumbnail( fn, f, ext_hash ) ) { synchronized( thumbnailCache ) { thumbnailCache.put( fn_h, new SoftReference<Drawable>( f.getThumbNail() ) ); } } else { succeeded = false; if( fails_count++ > 10 ) { Log.e( TAG, "To many fails, give up" ); return; } } } need_update = true; if( f.isThumbNail() && ( processed++ > 3 || ( proc_visible && proc_invisible ) ) ) { //Log.v( TAG, "Time to refresh!" ); Message msg = thread_handler.obtainMessage( NOTIFY_THUMBNAIL_CHANGED ); msg.sendToTarget(); yield(); proc_visible = false; need_update = false; processed = 0; } } if( need_update ) { Message msg = thread_handler.obtainMessage( NOTIFY_THUMBNAIL_CHANGED ); msg.sendToTarget(); } if( succeeded ) break; } } catch( Exception e ) { //Log.e( TAG, "ThumbnailsThread.run()", e ); } } private final boolean createThumbnail( String fn, FileItem f, int h ) { final String func_name = "createThubnail()"; try { if( h == apk_h ) { try { f.thumb_is_icon = true; PackageManager pm = ctx.getPackageManager(); PackageInfo info = pm.getPackageArchiveInfo( fn, 0 ); Drawable icon = null; if( info != null ) { try { icon = pm.getApplicationIcon( info.packageName ); } catch( Exception e ) { } if( icon != null ) { f.setIcon( icon ); return true; } } try { String filePath = f.f.getPath(); PackageInfo packageInfo = ctx.getPackageManager().getPackageArchiveInfo( filePath, PackageManager.GET_ACTIVITIES ); if( packageInfo != null ) { ApplicationInfo appInfo = packageInfo.applicationInfo; if( Build.VERSION.SDK_INT >= 8 ) { appInfo.sourceDir = filePath; appInfo.publicSourceDir = filePath; } icon = appInfo.loadIcon( ctx.getPackageManager() ); //bmpIcon = ((BitmapDrawable) icon).getBitmap(); } } catch( Exception e ) { Log.e( TAG, "File: " + fn, e ); } if( icon != null ) { f.setIcon( icon ); return true; } MnfUtils mnfu = new MnfUtils( fn ); icon = mnfu.extractIcon(); if( icon != null ) { f.setIcon( icon ); return true; } f.setIcon( pm.getDefaultActivityIcon() ); return true; } catch( Exception e ) { } return false; } // let's try to take it from the mediastore try { String[] proj = { BaseColumns._ID }; String where = Media.DATA + " = '" + fn + "'"; Cursor cursor = cr.query( Media.EXTERNAL_CONTENT_URI, proj, where, null, null ); if( cursor != null && cursor.getCount() > 0 ) { cursor.moveToPosition( 0 ); long id = cursor.getLong( 0 ); cursor.close(); String[] th_proj = new String[] { BaseColumns._ID, // 0 Thumbnails.WIDTH, // 1 Thumbnails.HEIGHT, // 2 Thumbnails.IMAGE_ID // 3 }; cursor = Thumbnails.queryMiniThumbnail( cr, id, Thumbnails.MINI_KIND, th_proj ); if( cursor != null && cursor.getCount() > 0 ) { cursor.moveToPosition( 0 ); Uri tcu = ContentUris.withAppendedId( Thumbnails.EXTERNAL_CONTENT_URI, cursor.getLong(0) ); int tw = cursor.getInt( 1 ); int th = cursor.getInt( 2 ); //Log.v( TAG, "th id: " + cursor.getLong(0) + ", org id: " + cursor.getLong(3) + ", w: " + tw + ", h: " + th ); cursor.close(); InputStream in = cr.openInputStream( tcu ); if( tw > 0 && th > 0 ) { int greatest = Math.max( tw, th ); int factor = greatest / thumb_sz; int b; for( b = 0x8000000; b > 0; b >>= 1 ) if( b <= factor ) break; options.inSampleSize = b; } else options.inSampleSize = 4; options.inJustDecodeBounds = false; options.inTempStorage = buf; Bitmap bitmap = BitmapFactory.decodeStream( in, null, options ); if( bitmap != null ) { BitmapDrawable drawable = new BitmapDrawable( res, bitmap ); f.setThumbNail( drawable ); in.close(); //Log.v( TAG, "a thumbnail was stolen from " + tcu ); return true; } } } } catch( Exception e ) { //Log.e( TAG, fn, e ); } options.inSampleSize = 1; options.inJustDecodeBounds = true; options.outWidth = 0; options.outHeight = 0; options.inTempStorage = buf; FileInputStream fis = new FileInputStream( fn ); BitmapFactory.decodeStream( fis, null, options); //BitmapFactory.decodeFile( fn, options ); if( options.outWidth > 0 && options.outHeight > 0 ) { f.attr = "" + options.outWidth + "x" + options.outHeight; int greatest = Math.max( options.outWidth, options.outHeight ); int factor = greatest / thumb_sz; int b; for( b = 0x8000000; b > 0; b >>= 1 ) if( b < factor ) break; options.inSampleSize = b; options.inJustDecodeBounds = false; Bitmap bitmap = BitmapFactory.decodeFile( fn, options ); if( bitmap != null ) { BitmapDrawable drawable = new BitmapDrawable( res, bitmap ); // drawable.setGravity( Gravity.CENTER ); // drawable.setBounds( 0, 0, 60, 60 ); f.setThumbNail( drawable ); fis.close(); return true; } } else Log.w( TAG, "failed to get an image bounds" ); fis.close(); Log.e( TAG, func_name + " failed for " + fn ); } catch( RuntimeException rte ) { Log.e( TAG, func_name, rte ); } catch( FileNotFoundException fne ) { Log.e( TAG, func_name, fne ); } catch( IOException ioe ) { Log.e( TAG, func_name, ioe ); } catch( Error err ) { Log.e( TAG, func_name, err ); } return false; } } @Override public void openItem( int position ) { if( position == 0 ) { if( parentLink == SLS ) commander.Navigate( Uri.parse( HomeAdapter.DEFAULT_LOC ), null, null ); else { if( dirName == null ) return; File cur_dir_file = new File( dirName ); String parent_dir = cur_dir_file.getParent(); commander.Navigate( Uri.parse( Utils.escapePath( parent_dir != null ? parent_dir : DEFAULT_DIR ) ), null, cur_dir_file.getName() ); } } else { File file = items[position - 1].f; if( file == null ) return; Uri open_uri = Uri.parse( Utils.escapePath( file.getAbsolutePath() ) ); if( file.isDirectory() ) commander.Navigate( open_uri, null, null ); else commander.Open( open_uri, null ); } } @Override public Uri getItemUri( int position ) { try { String item_name = getItemName( position, true ); return Uri.parse( Utils.escapePath( item_name ) ); } catch( Exception e ) { e.printStackTrace(); } return null; } @Override public String getItemName( int position, boolean full ) { if( position < 0 || items == null || position > items.length ) return position == 0 ? parentLink : null; if( full ) return position == 0 ? (new File( dirName )).getParent() : items[position - 1].f.getAbsolutePath(); else return position == 0 ? parentLink : items[position - 1].f.getName(); } @Override public void reqItemsSize( SparseBooleanArray cis ) { try { FileItem[] list = bitsToFilesEx( cis ); notify( Commander.OPERATION_STARTED ); commander.startEngine( new CalcSizesEngine( list ) ); } catch(Exception e) { } } class CalcSizesEngine extends Engine { private FileItem[] mList; protected int num = 0, dirs = 0, depth = 0; CalcSizesEngine( FileItem[] list ) { mList = list; setName( ".CalcSizesEngine" ); } @Override public void run() { try { Init( null ); Context c = ctx; StringBuffer result = new StringBuffer( ); if( mList != null && mList.length > 0 ) { long sum = getSizes( mList ); if( sum < 0 ) { sendProgress( "Interrupted", Commander.OPERATION_FAILED ); return; } if( (mode & MODE_SORTING) == SORT_SIZE ) synchronized( items ) { reSort( items ); } if( mList.length == 1 ) { File f = mList[0].f; if( f.isDirectory() ) { result.append( c.getString( R.string.sz_folder, f.getName(), num ) ); if( dirs > 0 ) result.append( c.getString( R.string.sz_dirnum, dirs, ( dirs > 1 ? c.getString( R.string.sz_dirsfx_p ) : c.getString( R.string.sz_dirsfx_s ) ) ) ); } else result.append( c.getString( R.string.sz_file, f.getName() ) ); } else result.append( c.getString( R.string.sz_files, num ) ); if( sum > 0 ) result.append( c.getString( R.string.sz_Nbytes, Formatter.formatFileSize( ctx, sum ).trim() ) ); if( sum > 1024 ) result.append( c.getString( R.string.sz_bytes, sum ) ); if( mList.length == 1 ) { result.append( c.getString( R.string.sz_lastmod ) ); result.append( " " ); String date_s; Date date = new Date( mList[0].f.lastModified() ); if( Locale.getDefault().getLanguage().compareTo( "en" ) != 0 ) { java.text.DateFormat locale_date_format = DateFormat.getDateFormat( ctx ); java.text.DateFormat locale_time_format = DateFormat.getTimeFormat( ctx ); date_s = locale_date_format.format( date ) + " " + locale_time_format.format( date ); } else date_s = (String)DateFormat.format( "MMM dd yyyy hh:mm:ss", date ); result.append( date_s ); if( mList[0].f.isFile() ) { FileInputStream in = new FileInputStream( mList[0].f ); MessageDigest digester = MessageDigest.getInstance( "MD5" ); byte[] bytes = new byte[8192]; int byteCount; while((byteCount = in.read(bytes)) > 0) { digester.update( bytes, 0, byteCount ); } byte[] digest = digester.digest(); in.close(); result.append( "\n\nMD5:\n" ); result.append( Utils.toHexString( digest, null ) ); } } result.append( "\n\n" ); } StatFs stat = new StatFs( dirName ); long block_size = stat.getBlockSize( ); result.append( c.getString( R.string.sz_total, Formatter.formatFileSize( ctx, stat.getBlockCount() * block_size ), Formatter.formatFileSize( ctx, stat.getAvailableBlocks() * block_size ) ) ); sendReport( result.toString() ); } catch( Exception e ) { sendProgress( e.getMessage(), Commander.OPERATION_FAILED ); } } protected final long getSizes( FileItem[] list ) throws Exception { long count = 0; for( int i = 0; i < list.length; i++ ) { if( isStopReq() ) return -1; FileItem f = list[i]; if( f.f.isDirectory() ) { dirs++; if( depth++ > 20 ) throw new Exception( s( R.string.too_deep_hierarchy ) ); File[] subfiles = f.f.listFiles(); if( subfiles != null ) { int l = subfiles.length; FileItem[] subfiles_ex = new FileItem[l]; for( int j = 0; j < l; j++ ) subfiles_ex[j] = new FileItem( subfiles[j] ); long sz = getSizes( subfiles_ex ); if( sz < 0 ) return -1; f.size = sz; count += f.size; } depth--; } else { num++; count += f.f.length(); } } return count; } } private class AskEngine extends Engine { private String msg; private File from, to; AskEngine( Handler h_, String msg_, File from_, File to_ ) { super.setHandler( h_ ); msg = msg_; from = from_; to = to_; } @Override public void run() { try { int resolution = askOnFileExist( msg, commander ); if( ( resolution & Commander.REPLACE ) != 0 ) { if( to.delete() && from.renameTo( to ) ) sendResult( "ok" ); } } catch( InterruptedException e ) { e.printStackTrace(); } } } @Override public boolean renameItem( int position, String newName, boolean copy ) { if( position <= 0 || position > items.length ) return false; try { if( copy ) { // newName could be just name notify( Commander.OPERATION_STARTED ); File[] list = { items[position - 1].f }; String dest_name; if( newName.indexOf( SLC ) < 0 ) { dest_name = dirName; if( dest_name.charAt( dest_name.length()-1 ) != SLC ) dest_name += SLS; dest_name += newName; } else dest_name = newName; commander.startEngine( new CopyEngine( list, dest_name, MODE_COPY, true ) ); return true; } boolean ok = false; File f = items[position - 1].f; File new_file = new File( dirName, newName ); if( new_file.exists() ) { if( f.equals( new_file ) ) return false; String old_ap = f.getAbsolutePath(); String new_ap = new_file.getAbsolutePath(); if( old_ap.equalsIgnoreCase( new_ap ) ) { File tmp_file = new File( dirName, newName + "_TMP_" ); ok = f.renameTo( tmp_file ); ok = tmp_file.renameTo( new_file ); } else { AskEngine ae = new AskEngine( simpleHandler, ctx.getString( R.string.file_exist, newName ), f, new_file ); return true; } } else ok = f.renameTo( new_file ); if( ok ) notifyRefr( newName ); else notify( s( R.string.error ), Commander.OPERATION_FAILED ); return ok; } catch( SecurityException e ) { commander.showError( ctx.getString( R.string.sec_err, e.getMessage() ) ); return false; } } @Override public Item getItem( Uri u ) { try { File f = new File( u.getPath() ); if( f.exists() ) { Item item = new Item( f.getName() ); item.size = f.length(); item.date = new Date( f.lastModified() ); item.dir = f.isDirectory(); return item; } } catch( Throwable e ) { e.printStackTrace(); } return null; } @Override public InputStream getContent( Uri u, long skip ) { try { String path = u.getPath(); File f = new File( path ); if( f.exists() && f.isFile() ) { FileInputStream fis = new FileInputStream( f ); if( skip > 0 ) fis.skip( skip ); return fis; } } catch( Throwable e ) { e.printStackTrace(); } return null; } @Override public OutputStream saveContent( Uri u ) { if( u != null ) { File f = new File( u.getPath() ); try { return new FileOutputStream( f ); } catch( FileNotFoundException e ) { Log.e( TAG, u.getPath(), e ); } } return null; } @Override public boolean createFile( String fileURI ) { try { File f = new File( fileURI ); boolean ok = f.createNewFile(); notify( null, ok ? Commander.OPERATION_COMPLETED_REFRESH_REQUIRED : Commander.OPERATION_FAILED ); return ok; } catch( Exception e ) { commander.showError( ctx.getString( R.string.cant_create, fileURI, e.getMessage() ) ); } return false; } @Override public void createFolder( String new_name ) { try { if( (new File( dirName, new_name )).mkdir() ) { notifyRefr( new_name ); return; } } catch( Exception e ) { Log.e( TAG, "createFolder", e ); } notify( ctx.getString( R.string.cant_md, new_name ), Commander.OPERATION_FAILED ); } @Override public boolean deleteItems( SparseBooleanArray cis ) { try { FileItem[] list = bitsToFilesEx( cis ); if( list != null ) { notify( Commander.OPERATION_STARTED ); commander.startEngine( new DeleteEngine( list ) ); } } catch( Exception e ) { notify( e.getMessage(), Commander.OPERATION_FAILED ); } return false; } class DeleteEngine extends Engine { private File[] mList; DeleteEngine( FileItem[] list ) { setName( ".DeleteEngine" ); mList = new File[list.length]; for( int i = 0; i < list.length; i++ ) mList[i] = list[i].f; } @Override public void run() { try { Init( null ); int cnt = deleteFiles( mList ); sendResult( Utils.getOpReport( ctx, cnt, R.string.deleted ) ); } catch( Exception e ) { sendProgress( e.getMessage(), Commander.OPERATION_FAILED_REFRESH_REQUIRED ); } } private final int deleteFiles( File[] l ) throws Exception { if( l == null ) return 0; int cnt = 0; int num = l.length; double conv = 100./num; for( int i = 0; i < num; i++ ) { sleep( 1 ); if( isStopReq() ) throw new Exception( s( R.string.canceled ) ); File f = l[i]; sendProgress( ctx.getString( R.string.deleting, f.getName() ), (int)(cnt * conv) ); if( f.isDirectory() ) cnt += deleteFiles( f.listFiles() ); if( f.delete() ) cnt++; else { error( ctx.getString( R.string.cant_del, f.getName() ) ); break; } } return cnt; } } @Override public boolean copyItems( SparseBooleanArray cis, CommanderAdapter to, boolean move ) { boolean ok = to.receiveItems( bitsToNames( cis ), move ? MODE_MOVE : MODE_COPY ); if( !ok ) notify( Commander.OPERATION_FAILED ); return ok; } @Override public boolean receiveItems( String[] uris, int move_mode ) { try { if( uris == null || uris.length == 0 ) return false; File dest_file = new File( dirName ); if( dest_file.exists() ) { if( !dest_file.isDirectory() ) return false; } else { if( !dest_file.mkdirs() ) return false; } File[] list = Utils.getListOfFiles( uris ); if( list != null ) { notify( Commander.OPERATION_STARTED ); commander.startEngine( new CopyEngine( list, dirName, move_mode, false ) ); return true; } } catch( Exception e ) { e.printStackTrace(); } return false; } @Override public void prepareToDestroy() { super.prepareToDestroy(); if( tht != null ) tht.interrupt(); items = null; } class CopyEngine extends CalcSizesEngine { private String mDest; private int counter = 0; private long totalBytes = 0; private double conv; private File[] fList = null; private boolean move, del_src_dir, destIsFullName; private byte[] buf; private static final int BUFSZ = 524288; private PowerManager.WakeLock wakeLock; CopyEngine( File[] list, String dest, int move_mode, boolean dest_is_full_name ) { super( null ); setName( ".CopyEngine" ); fList = list; mDest = dest; move = ( move_mode & MODE_MOVE ) != 0; del_src_dir = ( move_mode & MODE_DEL_SRC_DIR ) != 0; destIsFullName = dest_is_full_name; buf = new byte[BUFSZ]; PowerManager pm = (PowerManager)ctx.getSystemService( Context.POWER_SERVICE ); wakeLock = pm.newWakeLock( PowerManager.PARTIAL_WAKE_LOCK, TAG ); } @Override public void run() { sendProgress( ctx.getString( R.string.preparing ), 0, 0 ); try { int l = fList.length; FileItem[] x_list = new FileItem[l]; File src_dir_f = null; boolean in_same_src = true; for( int j = 0; j < l; j++ ) { x_list[j] = new FileItem( fList[j] ); if( in_same_src ) { File parent_f = fList[j].getParentFile(); if( src_dir_f == null ) src_dir_f = parent_f; else in_same_src = src_dir_f.equals( parent_f ); } } wakeLock.acquire(); long sum = getSizes( x_list ); conv = 100 / (double)sum; int num = copyFiles( fList, mDest, destIsFullName ); if( del_src_dir && in_same_src && src_dir_f != null ) src_dir_f.delete(); wakeLock.release(); String report = Utils.getOpReport( ctx, num, move ? R.string.moved : R.string.copied ); sendResult( report ); } catch( Exception e ) { sendProgress( e.getMessage(), Commander.OPERATION_FAILED_REFRESH_REQUIRED ); return; } } private final int copyFiles( File[] list, String dest, boolean dest_is_full_name ) throws InterruptedException { Context c = ctx; File file = null; for( int i = 0; i < list.length; i++ ) { boolean existed = false; InputStream is = null; OutputStream os = null; File outFile = null; file = list[i]; if( file == null ) { error( c.getString( R.string.unkn_err ) ); break; } String uri = file.getAbsolutePath(); try { if( isStopReq() ) { error( c.getString( R.string.canceled ) ); break; } long last_modified = file.lastModified(); String fn = file.getName(); outFile = dest_is_full_name ? new File( dest ) : new File( dest, fn ); String out_dir_path = new File( dest ).getCanonicalPath(); if( file.isDirectory() ) { if( out_dir_path.startsWith( file.getCanonicalPath() ) ) { error( ctx.getString( R.string.cant_copy_to_itself, file.getName() ) ); break; } if( depth++ > 40 ) { error( ctx.getString( R.string.too_deep_hierarchy ) ); break; } else if( outFile.exists() || outFile.mkdir() ) { copyFiles( file.listFiles(), outFile.getAbsolutePath(), false ); if( errMsg != null ) break; } else error( c.getString( R.string.cant_md, outFile.getAbsolutePath() ) ); depth--; counter++; } else { if( existed = outFile.exists() ) { int res = askOnFileExist( c.getString( R.string.file_exist, outFile.getAbsolutePath() ), commander ); if( res == Commander.SKIP ) continue; if( res == Commander.REPLACE ) { if( outFile.equals( file ) ) continue; else outFile.delete(); } if( res == Commander.ABORT ) break; } if( move ) { // first try to move by renaming long len = file.length(); if( file.renameTo( outFile ) ) { counter++; totalBytes += len; int so_far = (int)(totalBytes * conv); sendProgress( outFile.getName() + " " + c.getString( R.string.moved ), so_far, 0 ); continue; } } is = new FileInputStream( file ); os = new FileOutputStream( outFile ); long copied = 0, size = file.length(); long start_time = 0; int speed = 0; int so_far = (int)(totalBytes * conv); String sz_s = Utils.getHumanSize( size ); int fnl = fn.length(); String rep_s = c.getString( R.string.copying, fnl > CUT_LEN ? "\u2026" + fn.substring( fnl - CUT_LEN ) : fn ); int n = 0; long nn = 0; while( true ) { if( nn == 0 ) { start_time = System.currentTimeMillis(); sendProgress( rep_s + sizeOfsize( copied, sz_s ), so_far, (int)(totalBytes * conv), speed ); } n = is.read( buf ); if( n < 0 ) { long time_delta = System.currentTimeMillis() - start_time; if( time_delta > 0 ) { speed = (int)(MILLI * nn / time_delta ); sendProgress( rep_s + sizeOfsize( copied, sz_s ), so_far, (int)(totalBytes * conv), speed ); } break; } os.write( buf, 0, n ); nn += n; copied += n; totalBytes += n; if( isStopReq() ) { Log.d( TAG, "Interrupted!" ); error( c.getString( R.string.canceled ) ); return counter; } long time_delta = System.currentTimeMillis() - start_time; if( time_delta > DELAY ) { speed = (int)(MILLI * nn / time_delta); //Log.v( TAG, "bytes: " + nn + " time: " + time_delta + " speed: " + speed ); nn = 0; } } is.close(); os.close(); is = null; os = null; if( i >= list.length-1 ) sendProgress( c.getString( R.string.copied_f, fn ) + sizeOfsize( copied, sz_s ), (int)(totalBytes * conv) ); // debug only, to remove! //Log.v( TAG, c.getString( R.string.copied_f, fn ) ); counter++; } if( move ) file.delete(); outFile.setLastModified( last_modified ); final int GINGERBREAD = 9; if( android.os.Build.VERSION.SDK_INT >= GINGERBREAD ) ForwardCompat.setFullPermissions( outFile ); } catch( SecurityException e ) { Log.e( TAG, "", e ); error( c.getString( R.string.sec_err, e.getMessage() ) ); } catch( FileNotFoundException e ) { Log.e( TAG, "", e ); error( c.getString( R.string.not_accs, e.getMessage() ) ); } catch( ClosedByInterruptException e ) { Log.e( TAG, "", e ); error( c.getString( R.string.canceled ) ); } catch( IOException e ) { Log.e( TAG, "", e ); String msg = e.getMessage(); error( c.getString( R.string.acc_err, uri, msg != null ? msg : "" ) ); } catch( RuntimeException e ) { Log.e( TAG, "", e ); error( c.getString( R.string.rtexcept, uri, e.getMessage() ) ); } finally { try { if( is != null ) is.close(); if( os != null ) os.close(); if( !move && errMsg != null && outFile != null && !existed ) { Log.i( TAG, "Deleting failed output file" ); outFile.delete(); } } catch( IOException e ) { error( c.getString( R.string.acc_err, uri, e.getMessage() ) ); } } } return counter; } } private final FileItem[] bitsToFilesEx( SparseBooleanArray cis ) { try { int counter = 0; for( int i = 0; i < cis.size(); i++ ) if( cis.valueAt( i ) && cis.keyAt( i ) > 0) counter++; FileItem[] res = new FileItem[counter]; int j = 0; for( int i = 0; i < cis.size(); i++ ) if( cis.valueAt( i ) ) { int k = cis.keyAt( i ); if( k > 0 ) res[j++] = items[ k - 1 ]; } return res; } catch( Exception e ) { Log.e( TAG, "bitsToFilesEx()", e ); } return null; } public final File[] bitsToFiles( SparseBooleanArray cis ) { try { int counter = 0; for( int i = 0; i < cis.size(); i++ ) if( cis.valueAt( i ) && cis.keyAt( i ) > 0) counter++; File[] res = new File[counter]; int j = 0; for( int i = 0; i < cis.size(); i++ ) if( cis.valueAt( i ) ) { int k = cis.keyAt( i ); if( k > 0 ) res[j++] = items[ k - 1 ].f; } return res; } catch( Exception e ) { Log.e( TAG, "bitsToFiles()", e ); } return null; } @Override protected int getPredictedAttributesLength() { return 10; // "1024x1024" } /* * ListAdapter implementation */ @Override public int getCount() { if( items == null ) return 1; return items.length + 1; } @Override public Object getItem( int position ) { Item item = null; if( position == 0 ) { item = new Item(); item.name = parentLink; item.dir = true; } else { if( items != null && position <= items.length ) { synchronized( items ) { try { FileItem f = items[position - 1]; item = f; //item.origin = f.f; item.dir = f.f.isDirectory(); if( item.dir ) { /* if( ( mode & ICON_MODE ) == ICON_MODE ) item.name = f.f.getName() + SLS; else */ item.name = SLS + f.f.getName(); } else item.name = f.f.getName(); item.size = item.dir ? f.size : f.f.length(); long msFileDate = f.f.lastModified(); if( msFileDate != 0 ) item.date = new Date( msFileDate ); //Log.v( TAG, "getItem(" + (position-1) + ") for " + item.name ); // DEBUG!!! } catch( Exception e ) { Log.e( TAG, "getItem(" + position + ")", e ); } } } else { item = new Item(); item.name = "???"; } } return item; } public class FilePropComparator implements Comparator<FileItem> { int type; boolean case_ignore, ascending; public FilePropComparator( int type_, boolean case_ignore_, boolean ascending_ ) { type = type_; case_ignore = case_ignore_; ascending = ascending_; } public int compare( FileItem f1, FileItem f2 ) { boolean f1IsDir = f1.f.isDirectory(); boolean f2IsDir = f2.f.isDirectory(); if( f1IsDir != f2IsDir ) return f1IsDir ? -1 : 1; int ext_cmp = 0; switch( type ) { case SORT_EXT: ext_cmp = case_ignore ? Utils.getFileExt( f1.f.getName() ).compareToIgnoreCase( Utils.getFileExt( f2.f.getName() ) ) : Utils.getFileExt( f1.f.getName() ).compareTo( Utils.getFileExt( f2.f.getName() ) ); break; case SORT_SIZE: ext_cmp = ( f1IsDir ? f1.size - f2.size : f1.f.length() - f2.f.length() ) < 0 ? -1 : 1; break; case SORT_DATE: ext_cmp = f1.f.lastModified() - f2.f.lastModified() < 0 ? -1 : 1; break; } if( ext_cmp == 0 ) ext_cmp = case_ignore ? f1.f.getName().compareToIgnoreCase( f2.f.getName() ) : f1.f.compareTo( f2.f ); return ascending ? ext_cmp : -ext_cmp; } } @Override protected void reSort() { reSort( items ); } public void reSort( FileItem[] items_ ) { if( items_ == null ) return; FilePropComparator comp = new FilePropComparator( mode & MODE_SORTING, (mode & MODE_CASE) != 0, ascending ); Arrays.sort( items_, comp ); } @Override public IReciever getReceiver() { return this; } }