Android6.0 CMFileManager文件管理器源碼分析-文件分類與文件操作
一、主要內容:
如何學習一個新的模塊:學習新模塊時主要通過如下3個方面來學習,以CMFileManager文件管理器為例:
1、模塊源碼目錄結構、界面
2、模塊入口(清單文件)
3、模塊主要功能(文件分類、文件操作<刪除、粘貼、復制、剪切、重命名>)
二、模塊目錄結構及主界面
1、目錄結構
2、主界面
3、功能類關系圖
此圖只是大致描述下文件操作涉及到的類之間的跳轉流程,不是規范的流程圖、類圖
三、模塊入口
清單文件:
AndroidManifest.xml//清單文件是看一個陌生模塊的入口,通過清單文件可以了解此模塊大致的功能
1 <activity 2 3 android:name="com.cyanogenmod.filemanager.activities.LoadActivity" 4 5 android:theme="@style/FileManager.Theme.Material.NoActionBar" 6 7 android:screenOrientation="portrait"> 8 9 <intent-filter> 10 11 <action android:name="android.intent.action.MAIN" /> 12 13 <category android:name="android.intent.category.LAUNCHER" /> 14 15 </intent-filter> 16 17 </activity>
LoadActivity.java
1 TimerTask task = new TimerTask() { 2 3 @Override 4 5 public void run() { 6 7 initsharedPreferences(); 8 9 if(mShared.getBoolean("First", true)){ 10 11 mEditor.putBoolean("First", false); 12 13 mEditor.commit(); 14 15 initFileTime(); 16 17 } 18 19 mEditor.putInt("limitCount", 0); 20 21 mEditor.commit(); 22 23 Intent intent = new Intent(LoadActivity.this,NavigationActivity.class); 24 25 startActivity(intent); 26 27 finish(); 28 29 } 30 31 };
四、文件分類代碼分析
1、onCreate
1 @Override 2 3 protected void onCreate(Bundle state) { 4 5 … 6 7 getMimeTypes();//根據文件擴展名獲取文件類型(判斷其屬於image,audio等) 8 9 initNavigationViews();//初始化文件管理器首界面(9個方塊的界面) 10 11 … 12 13 }
2、onResume
1 @Override 2 3 protected void onResume() { 4 5 … 6 7 /*modify by zh start*/ 8 9 isExternalSdMounted = isExternalStorageMounted(); 10 11 initSdpath(); 12 13 14 15 /*modifystart*/ 16 17 if (mCountTask != null) { 18 19 mCountTask.cancel(true); 20 21 } 22 23 /*modif5 end*/ 24 25 mCountTask = new SearchCountAsyncTask();//異步任務,用來對不同種類的文件進行計數 26 27 mCountTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 28 29 mSizeTask = new SearchSizeAsyncTask(); 30 31 if(!isExternalSdMounted){ 32 33 mSizeTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,new String[]{DEFAULT_PATH}); 34 35 visibleSdcard.setVisibility(View.GONE); 36 37 }else{ 38 39 visibleSdcard.setVisibility(View.VISIBLE); 40 41 EXTERNAL_SDCARD_PATH = StorageHelper.getPhoneSDPath(this); 42 43 mSizeTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,new String[]{DEFAULT_PATH,EXTERNAL_SDCARD_PATH}); 44 45 } 46 47 /*modify31 end*/ 48 49 if(!mPassOnCreate) { 50 51 if(mCountTask != null) { 52 53 mCountTask.canceled = true; 54 55 mCountTask.cancel(true); 56 57 } 58 59 mCountTask = new SearchCountAsyncTask(); 60 61 mCountTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 62 63 } 64 65 66 67 if (mSearchView.getVisibility() == View.VISIBLE) { 68 69 closeSearch(); 70 71 } 72 73 … 74 75 }
3、getMimeTypes
1 void getMimeTypes(){//讀取配置文件來判斷文件屬於哪一類(比如對於讀取到的.jpg,.png屬於image) 2 try { 3 Properties mimeTypes = new Properties(); 4 mimeTypes.load(this.getResources().openRawResource(R.raw.mime_types)); 5 Enumeration<Object> e = mimeTypes.keys(); 6 while (e.hasMoreElements()) { 7 String extension = (String)e.nextElement(); 8 String data = mimeTypes.getProperty(extension); 9 String[] datas = data.split(","); 10 for (String theData : datas) { 11 String[] mimeData = theData.split("\\|"); //$NON-NLS-1$ 12 13 if(mimeData[0].toString().trim().equalsIgnoreCase(MimeTypeCategory.APP.toString())){ 14 app.add(extension); 15 }else if(mimeData[0].toString().trim().equalsIgnoreCase(MimeTypeCategory.AUDIO.toString())){ 16 audio.add(extension); 17 } 18 … 19 } 20 21 } 22 }catch (Exception e2) { 23 // TODO: handle exception 24 } 25 }
4、initNavigationViews
1 private void initNavigationViews() { 2 //Get the navigation views (wishlist: multiple view; for now only one view) 3 this.mNavigationViews = new NavigationView[1]; 4 this.mCurrentNavigationView = 0; 5 this.mNavigationViews[0] = (NavigationView)findViewById(R.id.navigation_view); 6 this.mNavigationViews[0].setId(0); 7 this.mEasyModeListView = (GridView) findViewById(R.id.lv_easy_mode); 8 this.mEasyModeListView.setVisibility(View.VISIBLE); 9 mEasyModeAdapter = new ArrayAdapter<MimeTypeCategory>(this, R.layout.navigation_view_simple_item2) { 10 @Override 11 public View getView(int position, View convertView, ViewGroup parent) { 12 convertView = (convertView == null) ?getLayoutInflater().inflate(R.layout.navigation_view_simple_item2, parent, false) : convertView; 13 MimeTypeCategory item = getItem(position); 14 String typeTitle = MIME_TYPE_LOCALIZED_NAMES[item.ordinal()]; 15 TextView typeTitleTV = (TextView) convertView.findViewById(R.id.navigation_text); 16 … 17 if(parent.getChildCount()==position){ 18 switch (position) { 19 case 0: 20 limitPostion++; 21 if(limitPostion>1) break; 22 if(mCountTask.countTaskFlag){ 23 int sharedCount = getSharedCount("news",mCountTask.mCount.newCount); 24 textCount.setText("(" + sharedCount + ")"); 25 }else{ 26 savaSdcardOrinternar("news",mCountTask.mCount.newCount); 27 textCount.setText("(" + mCountTask.mCount.newCount + ")"); 28 } 29 Log.e("zqq","news"); 30 break; 31 case 1: 32 if(mCountTask.countTaskFlag){ 33 int sharedCount = getSharedCount("apps",mCountTask.mCount.appCount); 34 textCount.setText("("+sharedCount+")"); 35 }else{ 36 textCount.setText("("+mCountTask.mCount.appCount+")"); 37 savaSdcardOrinternar("apps",mCountTask.mCount.appCount); 38 } 39 Log.e("zqq","apps"); 40 break; 41 … 42 default: 43 textCount.setText("0"); 44 break; 45 } 46 } 47 if(null != mEditor) { 48 mEditor.commit(); 49 } 50 typeTitleTV.setText(typeTitle); 51 typeIconIV.setImageDrawable(EASY_MODE_ICONS.get(item)); 52 convertView.setOnClickListener(mEasyModeItemClickListener); 53 convertView.setTag(position); 54 return convertView; 55 } 56 57 };
5、initSdpath
1 private void initSdpath() { 2 pathList.clear(); /*modify 331*/ 3 pathList.add(FileHelper.INNER_SDCARD_DIRECTORY); 4 if(isExternalSdMounted) { 5 /*modify start*/ 6 pathList.add(StorageHelper.getPhoneSDPath(this)/*FileHelper.EXTERNAL_SDCARD_DIRECTORY*/); 7 /*modifyer end*/ 8 } 9 }
6、SearchAllFileCount
1 void SearchAllFileCount(SearchCountAsyncTask task){ 2 if(this.canceled) return; 3 for (String path : pathList) {//pathList是list集合,值為 /storage/sdcard0 , /storage/sdcard1 4 if(null == path) continue; 5 File[] files = new File(path).listFiles(); 6 if(null == files) { 7 continue ; 8 } 9 for (int i = 0; i < files.length; i++) { 10 File f = files[i]; 11 if(null == f) continue; 12 if(f.isFile()&&!(f.getName().substring(0,1).contains("."))){ 13 FileSystemObject fso = FileHelper 14 .createFileSystemObject(files[i]); //FileSystemObject封裝了文件的屬性 15 if(!timeFilter(fso)){//根據文件距上次修改時間的大小判斷是否為最新文件 task.mCount.newCount ++ ; 16 } 17 18 String ext = FileHelper.getExtension(fso); 19 if("3gp".equals(ext) || "3gpp".equals(ext) || "3g2".equals(ext) || "3gpp2".equals(ext)) { 20 String category = MimeTypeHelper.getCategory(NavigationActivity.this, fso).toString(); 21 if("AUDIO".equals(category)) { 22 task.mCount.audioCount ++; 23 continue; 24 } else if ("VIDEO".equals(category)) { 25 task.mCount.voideoCount ++; 26 continue; 27 } 28 } 29 … 30 for (int j = 0; j < audio.size(); j++) { 31 if(f.getName().substring(f.getName().lastIndexOf(".")+1). 32 equalsIgnoreCase(audio.get(j).toString())){ 33 task.mCount.audioCount ++; 34 } 35 } 36 … 37 for (int j = 0; j < image.size(); j++) { 38 if(f.getName().substring(f.getName().lastIndexOf(".")+1). 39 equalsIgnoreCase(image.get(j).toString())){ 40 task.mCount.imageCount ++; 41 } 42 } 43 }else if (f.isDirectory() && f.getPath().indexOf("/.") == -1)//如果是目錄(排除隱藏文件、隱藏文件夾) 44 SearchFileCount(f.getPath(),task); 45 } 46 } 47 } 48 49 void SearchFileCount(String path,SearchCountAsyncTask task){ 50 if(this.canceled) return; 51 File[] files = new File(path).listFiles(); 52 if(null == files) return ; 53 for (int i = 0; i < files.length; i++) { 54 File f = files[i]; 55 if(null == f) continue; 56 if(f.isFile()&&!(f.getName().substring(0,1).contains("."))){ 57 FileSystemObject fso = FileHelper 58 .createFileSystemObject(files[i]); 59 if(!timeFilter(fso)){ 60 task.mCount.newCount ++ ; 61 } 62 String ext = FileHelper.getExtension(fso); 63 if("3gp".equals(ext) || "3gpp".equals(ext) || "3g2".equals(ext) || "3gpp2".equals(ext)) { 64 String category = MimeTypeHelper.getCategory(NavigationActivity.this, fso).toString(); 65 if("AUDIO".equals(category)) { 66 task.mCount.audioCount ++; 67 continue; 68 } else if ("VIDEO".equals(category)) { 69 task.mCount.voideoCount ++; 70 continue; 71 } 72 } 73 … 74 for (int j = 0; j < audio.size(); j++) { 75 if(f.getName().substring(f.getName().lastIndexOf(".")+1). 76 equalsIgnoreCase(audio.get(j).toString())){ 77 task.mCount.audioCount ++; 78 } 79 } 80 … 81 for (int j = 0; j < image.size(); j++) { 82 if(f.getName().substring(f.getName().lastIndexOf(".")+1). 83 equalsIgnoreCase(image.get(j).toString())){ 84 task.mCount.imageCount ++; 85 } 86 } 87 }else if (f.isDirectory() && f.getPath().indexOf("/.") == -1) 88 SearchFileCount(f.getPath(),task);//遞歸調用分類不同的文件 89 } 90 } 91 }
7、getCategory
public static final MimeTypeCategory getCategory(Context context, FileSystemObject fso) { // Ensure that have a context … //Get the extension and delivery final MimeTypeCategory category = getCategoryFromExt(context, FileHelper.getExtension(fso), fso.getFullPath()); // Check system file if (category == MimeTypeCategory.NONE && fso instanceof SystemFile) { return MimeTypeCategory.SYSTEM; } return category; } public static final MimeTypeCategory getCategoryFromExt(Context context, String ext, String absolutePath) { … if (ext != null) { //Load from the database of mime types MimeTypeInfo mimeTypeInfo = getMimeTypeInternal(absolutePath, ext); if (mimeTypeInfo != null) { return mimeTypeInfo.mCategory; } } … } private static final MimeTypeInfo getMimeTypeInternal(String absolutePath, String ext) { MimeTypeInfo mimeTypeInfo = null; ArrayList<MimeTypeInfo> mimeTypeInfoList = sMimeTypes.get(ext.toLowerCase(Locale.ROOT)); // Multiple mimetypes map to the same extension, try to resolve it. if (mimeTypeInfoList != null && mimeTypeInfoList.size() > 1) { if (absolutePath != null) { String mimeType = getAmbiguousExtensionMimeType(absolutePath, ext); mimeTypeInfo = sExtensionMimeTypes.get(ext + mimeType); } else { // We don't have the ability to read the file to resolve the ambiguity, // so default to the first available mimetype. mimeTypeInfo = mimeTypeInfoList.get(0);//NONE } } else if (mimeTypeInfoList != null && mimeTypeInfoList.size() == 1) { // Only one possible mimetype, so pick that one. mimeTypeInfo = mimeTypeInfoList.get(0); } return mimeTypeInfo; } private static final String getAmbiguousExtensionMimeType(String absolutePath, String ext) { if (AmbiguousExtensionHelper.AMBIGUOUS_EXTENSIONS_MAP.containsKey(ext)) { AmbiguousExtensionHelper helper = AmbiguousExtensionHelper.AMBIGUOUS_EXTENSIONS_MAP.get(ext); String mimeType = helper.getMimeType(absolutePath, ext); //返回video/3gpp或者audio/3gpp、video/3gpp2、audio/3gpp3、 if (!TextUtils.isEmpty(mimeType)) { return mimeType; } } return null; } public String getMimeType(String absolutePath, String extension) { MediaMetadataRetriever retriever = new MediaMetadataRetriever();// try { retriever.setDataSource(absolutePath); boolean hasVideo = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO) != null;//調用native方法去觸探文件是音頻還是視頻 if (is3GPP(extension)) { return hasVideo ? VIDEO_3GPP_MIME_TYPE : AUDIO_3GPP_MIME_TYPE; } else if (is3GPP2(extension)) { return hasVideo ? VIDEO_3GPP2_MIME_TYPE : AUDIO_3GPP2_MIME_TYPE; } } catch (RuntimeException e) { Log.e(TAG, "Unable to open 3GP file to determine mimetype"); } // Default to video 3gp if the file is unreadable as this was the default before // ambiguous resolution support was added. return VIDEO_3GPP_MIME_TYPE; }
五、文件操作
1、文件操作總括
1 public void guiAction(View v) { 2 openActionsDialog(getCurrentNavigationView().getCurrentDir(), true); 3 switch (v.getId()) { 4 case R.id.gui_deletes://刪除 5 android.util.Log.i("wangqx","gui_deletes"); 6 if (ActionsDialog.mOnSelectionListener != null) { 7 List<FileSystemObject> selection = ActionsDialog.mOnSelectionListener 8 .onRequestSelectedFiles(); 9 DeleteActionPolicy.removeFileSystemObjects(this, selection, 10 ActionsDialog.mOnSelectionListener, 11 ActionsDialog.mOnRequestRefreshListener, null); 12 } 13 break; 14 15 case R.id.gui_cuts://剪切 16 android.util.Log.i("wangqx","gui_cuts"); 17 fileCutOrCopy = 1; 18 pasteAction.setVisibility(View.VISIBLE); 19 hiddenPopupWindowActionBar(); 20 hiddenActionBarSetting(); 21 // if(viewPopupButtom.getVisibility()==View.GONE){ 22 // getCurrentNavigationView().onDeselectItem(); 23 // } 24 break; 25 case R.id.gui_copys://復制 26 android.util.Log.i("wangqx","gui_copys"); 27 fileCutOrCopy = 2; 28 pasteAction.setVisibility(View.VISIBLE); 29 hiddenPopupWindowActionBar(); 30 hiddenActionBarSetting(); 31 // if(viewPopupButtom.getVisibility()==View.GONE){ 32 // getCurrentNavigationView().onDeselectItem(); 33 // } 34 break; 35 case R.id.action_paste://粘貼 36 android.util.Log.i("wangqx","action_paste"); 37 Log.i("hewei", "fileCutOrCopy ===" + fileCutOrCopy); 38 if (fileCutOrCopy == 1) {// Cut 39 if (ActionsDialog.mOnSelectionListener != null) { 40 if (finishFlag) { 41 CopyMoveActionPolicy.moveFileSystemObjects(this, 42 ActionsDialog.createLinkedResource(SearchActivity.fosList, fso), 43 ActionsDialog.mOnSelectionListener, 44 ActionsDialog.mOnRequestRefreshListener); 45 SearchActivity.fosList.clear(); 46 } else { 47 List<FileSystemObject> selection = ActionsDialog.mOnSelectionListener 48 .onRequestSelectedFiles(); 49 CopyMoveActionPolicy.moveFileSystemObjects(this, 50 ActionsDialog.createLinkedResource(selection, fso), 51 ActionsDialog.mOnSelectionListener, 52 ActionsDialog.mOnRequestRefreshListener); 53 } 54 finishFlag = false ; 55 fileCutOrCopy = 0 ; 56 } 57 } else if (fileCutOrCopy == 2) {// Copy 58 if (ActionsDialog.mOnSelectionListener != null) { 59 if (finishFlag) { 60 CopyMoveActionPolicy.copyFileSystemObjects(this, 61 ActionsDialog.createLinkedResource(SearchActivity.fosList, fso), 62 ActionsDialog.mOnSelectionListener, 63 ActionsDialog.mOnRequestRefreshListener); 64 SearchActivity.fosList.clear(); 65 finishFlag = false ; 66 fileCutOrCopy = 0 ; 67 } else { 68 List<FileSystemObject> selection = ActionsDialog.mOnSelectionListener 69 .onRequestSelectedFiles(); 70 CopyMoveActionPolicy.copyFileSystemObjects(this, 71 ActionsDialog.createLinkedResource(selection, fso), 72 ActionsDialog.mOnSelectionListener, 73 ActionsDialog.mOnRequestRefreshListener); 74 } 75 finishFlag = false ; 76 fileCutOrCopy = 0 ; 77 } 78 } 79 hiddenActionPaste(); 80 break; 81 case R.id.action_rename://重命名 82 if (ActionsDialog.mOnSelectionListener != null) { 83 TextView menuItemView = (TextView)viewPopupButtom.findViewById(R.id.action_rename); 84 dialog.showFsoInputNameDialog(menuItemView, this.newFso, false); 85 selectAll.setImageResource(R.drawable.ic_new_gui_select_all); 86 hiddenPopupWindowActionBar(); 87 if(viewPopupButtom.getVisibility()==View.GONE){ 88 getCurrentNavigationView().onDeselectAll(); 89 } 90 return; 91 } 92 break; 93 default: 94 break; 95 } 96 }
2、刪除:
1>、removeFileSystemObjects
1 public static void removeFileSystemObjects( 2 final Context ctx, final List<FileSystemObject> files, 3 final OnSelectionListener onSelectionListener, 4 final OnRequestRefreshListener onRequestRefreshListener, 5 final OnItemFlingerResponder onItemFlingerResponder) { 6 Log.i(TAG, files.size()+"----++++---"); 7 // Ask the user before remove 8 AlertDialog dialog = DialogHelper.createYesNoDialog( 9 ctx, 10 R.string.confirm_deletion, 11 R.string.actions_ask_undone_operation_msg, 12 new DialogInterface.OnClickListener() { 13 @Override 14 public void onClick(DialogInterface alertDialog, int which) { 15 if (which == DialogInterface.BUTTON_POSITIVE) { 16 // Remove the items 17 removeFileSystemObjectsInBackground( 18 ctx, 19 files, 20 onSelectionListener, 21 onRequestRefreshListener, 22 onItemFlingerResponder); 23 // SearchActivity.fosList.clear(); 24 } 25 … 26 //SearchActivity.mEditMode = false; 27 } 28 } 29 }); 30 DialogHelper.delegateDialogShow(ctx, dialog); 31 }
2>、removeFileSystemObjectsInBackground
1 static void removeFileSystemObjectsInBackground( 2 final Context ctx, final List<FileSystemObject> files, 3 final OnSelectionListener onSelectionListener, 4 final OnRequestRefreshListener onRequestRefreshListener, 5 final OnItemFlingerResponder onItemFlingerResponder) { 6 … 7 // 2.- Sort the items by path to avoid delete parents fso prior to child fso 8 final List<FileSystemObject> sortedFsos = new ArrayList<FileSystemObject>(files); 9 Collections.sort(sortedFsos, new Comparator<FileSystemObject>() { 10 @Override 11 public int compare(FileSystemObject lhs, FileSystemObject rhs) { 12 return lhs.compareTo(rhs) * -1; //Reverse 13 } 14 }); 15 16 // The callable interface 17 final BackgroundCallable callable = new BackgroundCallable() { 18 // The current items 19 private int mCurrent = 0; 20 final Context mCtx = ctx; 21 final List<FileSystemObject> mFiles = sortedFsos; 22 final OnRequestRefreshListener mOnRequestRefreshListener = onRequestRefreshListener; 23 24 final Object mSync = new Object(); 25 Throwable mCause; 26 27 … 28 29 @Override 30 public Spanned requestProgress() { 31 FileSystemObject fso = this.mFiles.get(this.mCurrent); 32 33 // Return the current operation 34 String progress = 35 this.mCtx.getResources(). 36 getString( 37 R.string.waiting_dialog_deleting_msg, 38 fso.getFullPath()); 39 return Html.fromHtml(progress); 40 } 41 … 42 @Override 43 public void doInBackground(Object... params) throws Throwable { 44 this.mCause = null; 45 46 // This method expect to receive 47 // 1.- BackgroundAsyncTask 48 BackgroundAsyncTask task = (BackgroundAsyncTask)params[0]; 49 50 int cc = this.mFiles.size(); 51 for (int i = 0; i < cc; i++) { 52 FileSystemObject fso = this.mFiles.get(i); 53 doOperation(this.mCtx, fso); 54 // Next file 55 this.mCurrent++; 56 if (this.mCurrent < this.mFiles.size()) { 57 task.onRequestProgress(); 58 } 59 } 60 }
3>、doOperation
1 /** 2 * Method that deletes the file or directory 3 * 4 * @param ctx The current context 5 * @param fso The file or folder to be deleted 6 */ 7 private void doOperation( 8 final Context ctx, final FileSystemObject fso) throws Throwable { 9 try { 10 // Remove the item 11 if (FileHelper.isDirectory(fso)) { 12 CommandHelper.deleteDirectory(ctx, fso.getFullPath(), null); 13 } else { 14 CommandHelper.deleteFile(ctx, fso.getFullPath(), null); 15 } 16 … 17 } 18 } 19 … 20 }; 21 final BackgroundAsyncTask task = new BackgroundAsyncTask(ctx, callable); 22 23 // Execute background task 24 task.execute(task); 25 }
4>、deleteFile
1 public static boolean deleteFile(Context context, String file, Console console) 2 throws FileNotFoundException, IOException, ConsoleAllocException, 3 NoSuchFileOrDirectory, InsufficientPermissionsException, 4 CommandNotFoundException, OperationTimeoutException, 5 ExecutionException, InvalidCommandDefinitionException, ReadOnlyFilesystemException, 6 CancelledOperationException { 7 Console c = ensureConsoleForFile(context, console, file); 8 DeleteFileExecutable executable = 9 c.getExecutableFactory().newCreator().createDeleteFileExecutable(file); 10 writableExecute(context, executable, c); 11 12 // Remove from mediascanner 13 MediaScannerConnection.scanFile(context, new String[]{ 14 MediaHelper.normalizeMediaPath(file) 15 }, null, null); 16 17 return executable.getResult().booleanValue(); 18 }
5>、execute
1 DeleteFileExecutable createDeleteFileExecutable(String file) throws CommandNotFoundException, 2 NoSuchFileOrDirectory, InsufficientPermissionsException; 3 DeleteFileExecutable 是一個接口類。//接口或者抽象類,就看實現類(子類) 4 DeleteFileCommend.java 5 @Override 6 public void execute() 7 throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException { 8 if (isTrace()) { 9 Log.v(TAG, 10 String.format("Deleting file: %s", this.mPath)); //$NON-NLS-1$ 11 } 12 File f = new File(this.mPath); 13 … 14 // Delete the file 15 if (!f.delete()) { 16 if (isTrace()) { 17 Log.v(TAG, "Result: FAIL. InsufficientPermissionsException"); //$NON-NLS-1$ 18 } 19 throw new InsufficientPermissionsException(); 20 } 21 … 22 }
3、剪切、復制、粘貼、重命名
1>、showFsoInputNameDialog
1 public void showFsoInputNameDialog(final View v, 2 final FileSystemObject fso, final boolean allowFsoName) { 3 android.util.Log.i("wangqx", "showFsoInputNameDialog"); 4 TextView text = (TextView) v; 5 // Hide the dialog 6 this.mDialog.hide(); 7 // Show the input name dialog 8 final InputNameDialog inputNameDialog = new InputNameDialog( 9 this.mContext, 10 this.mOnSelectionListener.onRequestCurrentItems(), fso, 11 allowFsoName, text.getText().toString().trim()); 12 inputNameDialog 13 .setOnCancelListener(new DialogInterface.OnCancelListener() { 14 @Override 15 public void onCancel(DialogInterface dialog) { 16 // Show the menu again 17 // DialogHelper.delegateDialogShow( 18 // ActionsDialog.this.mContext, 19 // ActionsDialog.this.mDialog); 20 } 21 }); 22 inputNameDialog 23 .setOnDismissListener(new DialogInterface.OnDismissListener() { 24 @Override 25 public void onDismiss(DialogInterface dialog) { 26 android.util.Log.i("wangqx", "onDismiss8888888888"); 27 // Retrieve the name an execute the action 28 try { 29 String name = inputNameDialog.getName(); 30 Log.i(TAG, inputNameDialog.mFso.getName()+"inputNameDialog.mFso.getName()"); 31 switch (v.getId()) { 32 case R.id.action_rename: 33 android.util.Log.i("wangqx", "R.id.action_rename:"); 34 // Rename the fso 35 if (ActionsDialog.this.mOnSelectionListener != null) { 36 CopyMoveActionPolicy 37 .renameFileSystemObject( 38 ActionsDialog.this.mContext, 39 inputNameDialog.mFso, 40 name, 41 ActionsDialog.this.mOnSelectionListener, 42 ActionsDialog.this.mOnRequestRefreshListener); 43 } 44 break; 45 … 46 default: 47 break; 48 } 49 } finally { 50 ActionsDialog.this.mDialog.dismiss(); 51 } 52 } 53 }); 54 inputNameDialog.show(); 55 }
2>、moveFileSystemObjects
1 public static void moveFileSystemObjects( 2 final Context ctx, 3 final List<LinkedResource> files, 4 final OnSelectionListener onSelectionListener, 5 final OnRequestRefreshListener onRequestRefreshListener) { 6 // Internal move 7 copyOrMoveFileSystemObjects( 8 ctx, 9 COPY_MOVE_OPERATION.MOVE, 10 files, 11 onSelectionListener, 12 onRequestRefreshListener); 13 }
3>、copyOrMoveFileSystemObjects
1 private static void copyOrMoveFileSystemObjects( 2 final Context ctx, 3 final COPY_MOVE_OPERATION operation, 4 final List<LinkedResource> files, 5 final OnSelectionListener onSelectionListener, 6 final OnRequestRefreshListener onRequestRefreshListener) { 7 // Some previous checks prior to execute 8 ... 9 // 3.- Check the operation consistency 10 if (operation.compareTo(COPY_MOVE_OPERATION.MOVE) == 0) { 11 if (!checkMoveConsistency(ctx, files, currentDirectory)) { 12 return; 13 } 14 } 15 16 // The callable interface 17 final BackgroundCallable callable = new BackgroundCallable() { 18 // The current items 19 private int mCurrent = 0; 20 final Context mCtx = ctx; 21 final COPY_MOVE_OPERATION mOperation = operation; 22 final List<LinkedResource> mFiles = files; 23 final OnRequestRefreshListener mOnRequestRefreshListener = onRequestRefreshListener; 24 25 final Object mSync = new Object(); 26 Throwable mCause; 27 28 29 30 @Override 31 public Spanned requestProgress() { 32 File src = this.mFiles.get(this.mCurrent).mSrc; 33 File dst = this.mFiles.get(this.mCurrent).mDst; 34 35 // Return the current operation 36 String progress = 37 this.mCtx.getResources(). 38 getString( 39 this.mOperation.compareTo(COPY_MOVE_OPERATION.MOVE) == 0 || 40 this.mOperation.compareTo(COPY_MOVE_OPERATION.RENAME) == 0 ? 41 R.string.waiting_dialog_moving_msg : 42 R.string.waiting_dialog_copying_msg, 43 src.getAbsolutePath(), 44 dst.getAbsolutePath()); 45 return Html.fromHtml(progress); 46 } 47 48 private void refreshUIAfterCompletion() { 49 // Remove orphan bookmark paths 50 51 if (files != null && (this.mOperation.compareTo(COPY_MOVE_OPERATION.MOVE) == 0 || 52 this.mOperation.compareTo(COPY_MOVE_OPERATION.RENAME) == 0)) { 53 for (LinkedResource linkedFiles : files) { 54 Bookmarks.deleteOrphanBookmarks(ctx, linkedFiles.mSrc.getAbsolutePath()); 55 } 56 } 57 //Operation complete. Refresh 58 if (this.mOnRequestRefreshListener != null) { 59 // The reference is not the same, so refresh the complete navigation view 60 this.mOnRequestRefreshListener.onRequestRefresh(null, true); 61 } 62 } 63 64 @Override 65 public void onSuccess() { 66 refreshUIAfterCompletion(); 67 ActionsPolicy.showOperationSuccessMsg(ctx); 68 } 69 70 @Override 71 public void doInBackground(Object... params) throws Throwable { 72 this.mCause = null; 73 74 // This method expect to receive 75 // 1.- BackgroundAsyncTask 76 BackgroundAsyncTask task = (BackgroundAsyncTask)params[0]; 77 78 int cc2 = this.mFiles.size(); 79 for (int i = 0; i < cc2; i++) { 80 File src = this.mFiles.get(i).mSrc; 81 File dst = this.mFiles.get(i).mDst; 82 doOperation(this.mCtx, src, dst, this.mOperation); 83 // Next file 84 this.mCurrent++; 85 if (this.mCurrent < this.mFiles.size()) { 86 task.onRequestProgress(); 87 } 88 //add by hewei for JIRA [21] 89 Uri uri = Uri.fromFile(dst); 90 Intent intent = new Intent("com.android.fileexplorer.action.MEDIA_SCANNER_SCAN_ALL"); 91 intent.setData(uri); 92 ctx.sendBroadcast(intent); 93 //end by hewei for JIRA [21] 94 } 95 } 96 ... 97 private void doOperation( 98 Context ctx, File src, File dst, COPY_MOVE_OPERATION operation) 99 throws Throwable { 100 // If the source is the same as destiny then don't do the operation 101 if (src.compareTo(dst) == 0) return; 102 103 try { 104 // Be sure to append a / if source is a folder (otherwise system crashes 105 // under using absolute paths) Issue: CYAN-2791 106 String source = src.getAbsolutePath() + 107 (src.isDirectory() ? File.separator : ""); 108 String dest = dst.getAbsolutePath() + 109 (dst.isDirectory() ? File.separator : ""); 110 111 /* 112 There is a possibility that the src and dst can have different consoles. 113 A possible case: 114 - src is from sd card and dst is secure storage 115 This could happen with anything that goes from a real console to a virtual 116 console or visa versa. Here we grab a handle on the console such that we 117 may explicitly kill the actions happening in both consoles. 118 */ 119 // Need to derive the console for the source 120 121 mSrcConsole = CommandHelper.ensureConsoleForFile(ctx, null, source); 122 // Need to derive the console for the destination 123 mDstConsole = CommandHelper.ensureConsoleForFile(ctx, null, dest); 124 125 // Copy or move? 126 if (operation.compareTo(COPY_MOVE_OPERATION.MOVE) == 0 || 127 operation.compareTo(COPY_MOVE_OPERATION.RENAME) == 0) { 128 CommandHelper.move( 129 ctx, 130 source, 131 dst.getAbsolutePath(), 132 mSrcConsole); 133 } else { 134 CommandHelper.copy( 135 ctx, 136 source, 137 dst.getAbsolutePath(), 138 mSrcConsole); 139 } 140 } 141 ... 142 // Check that the operation was completed retrieving the fso modified 143 FileSystemObject fso = 144 CommandHelper.getFileInfo(ctx, dst.getAbsolutePath(), false, null); 145 if (fso == null) { 146 throw new NoSuchFileOrDirectory(dst.getAbsolutePath()); 147 } 148 } 149 @Override 150 public void onFailRefresh() { 151 152 } 153 }; 154 final BackgroundAsyncTask task = new BackgroundAsyncTask(ctx, callable); 155 156 // Prior to execute, we need to check if some of the files will be overwritten 157 List<FileSystemObject> curFiles = onSelectionListener.onRequestCurrentItems(); 158 if (curFiles != null) { 159 // Is necessary to ask the user? 160 if (isOverwriteNeeded(files, curFiles)) { 161 //Show a dialog asking the user for overwrite the files 162 AlertDialog dialog = 163 DialogHelper.createTwoButtonsQuestionDialog( 164 ctx, 165 android.R.string.cancel, 166 R.string.overwrite, 167 R.string.confirm_overwrite, 168 ctx.getString(R.string.msgs_overwrite_files), 169 new DialogInterface.OnClickListener() { 170 @Override 171 public void onClick(DialogInterface alertDialog, int which) { 172 // NEGATIVE (overwrite) POSITIVE (cancel) 173 if (which == DialogInterface.BUTTON_POSITIVE) { 174 // Execute background task 175 task.execute(task); 176 } else if (which == DialogInterface.BUTTON_NEGATIVE) { 177 Message msg = new Message(); 178 msg.what = NavigationActivity.COPYORCUTCANCELFLAG; 179 //modify for bug20160714 start 180 if (ctx instanceof NavigationActivity) { 181 ((NavigationActivity)ctx).refreshAndTost.sendMessage(msg); 182 } 183 //modify for bug20160714 end 184 } 185 } 186 }); 187 DialogHelper.delegateDialogShow(ctx, dialog); 188 return; 189 } 190 } 191 192 // Execute background task 193 task.execute(task); 194 }
4>、move
1 public static boolean move(final Context context, final String src, final String dst, Console console) 2 throws FileNotFoundException, IOException, ConsoleAllocException, 3 NoSuchFileOrDirectory, InsufficientPermissionsException, 4 CommandNotFoundException, OperationTimeoutException, 5 ExecutionException, InvalidCommandDefinitionException, ReadOnlyFilesystemException, 6 CancelledOperationException { 7 Console cSrc = ensureConsoleForFile(context, console, src); 8 Console cDst = ensureConsoleForFile(context, console, dst); 9 boolean ret = true; 10 if (cSrc.equals(cDst)) { 11 // Is safe to use the same console 12 MoveExecutable executable = 13 cSrc.getExecutableFactory().newCreator().createMoveExecutable(src, dst); 14 writableExecute(context, executable, cSrc); 15 ret = executable.getResult().booleanValue(); 16 //added by huangyang 2015.12.16 for JIra [Angus3-e7 ANGUSIIIE-777] 17 if(ret) {} 18 //end 19 } else {} 20 21 // Do media scan (don't scan the file if is virtual file) 22 if (ret) { 23 File parent = new File(dst).getParentFile(); 24 if (parent != null && !VirtualMountPointConsole.isVirtualStorageResource(parent 25 .getAbsolutePath())) { 26 27 //modified by huangyang 2015.12.21 for Jira Angus3-a4 16GB ANGUSIIIGP-11 28 if("AUDIO".equals(MimeTypeHelper.getCategory(context, new File(src)).toString()) || 29 "VIDEO".equals(MimeTypeHelper.getCategory(context, new File(src)).toString())) { 30 Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; 31 ContentResolver resolver = context.getContentResolver(); 32 Cursor cursor = resolver.query(uri, new String[] {MediaStore.Audio.Media.ALBUM, MediaStore.Audio.Media._ID}, 33 MediaStore.Audio.Media.DATA + "='"+ src + "'", null, null); 34 String album = null; 35 int id = 0; 36 if (cursor != null) { 37 if(cursor.getCount() > 0){ 38 cursor.moveToFirst(); 39 album = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM)); 40 id = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media._ID)); 41 } 42 cursor.close(); 43 } 44 45 if(album !=null ){ 46 if(album.equals("FM recordings") || album.equals("CallRecord") || album.equals("Audio recordings")) { 47 if(id > 0) { 48 ContentValues values = new ContentValues(); 49 values.put(MediaStore.Audio.Media.DATA, dst); 50 String newTitle = dst.substring(0, dst.lastIndexOf(".")); 51 if(newTitle.lastIndexOf(File.separator) > -1) { 52 newTitle = newTitle.substring(newTitle.lastIndexOf(File.separator)+1); 53 } 54 values.put(MediaStore.Audio.Media.DISPLAY_NAME, newTitle); 55 values.put(MediaStore.Audio.Media.TITLE, newTitle);//add by fengertong ANGUSIIIE-1230 56 resolver.update(uri, values, MediaStore.Audio.Media._ID + "=?", 57 new String[]{String.valueOf(id)}); 58 } 59 } else { 60 // Remove from mediascanner 61 MediaScannerConnection.scanFile(context, new String[]{ 62 MediaHelper.normalizeMediaPath(src), 63 MediaHelper.normalizeMediaPath(dst) 64 }, null, new OnScanCompletedListener() { 65 66 @Override 67 public void onScanCompleted(String path, Uri uri) { 68 69 ContentValues values = new ContentValues(); 70 String newTitle = dst.substring(0, dst.lastIndexOf(".")); 71 if(newTitle.lastIndexOf(File.separator) > -1) { 72 newTitle = newTitle.substring(newTitle.lastIndexOf(File.separator)+1); 73 } 74 values.put(MediaStore.Audio.Media.TITLE, newTitle); 75 context.getContentResolver().update(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values, 76 MediaStore.Audio.Media.DATA + "=?", new String[]{dst}); 77 } 78 }); 79 } 80 } 81 } else { 82 // Remove from mediascanner 83 MediaScannerConnection.scanFile(context, new String[]{ 84 MediaHelper.normalizeMediaPath(src), 85 MediaHelper.normalizeMediaPath(dst) 86 }, null, null); 87 } 88 //end 89 } 90 } 91 92 return ret; 93 }
5>、createMoveExecutable
1 MoveExecutable createMoveExecutable(String src, String dst) throws CommandNotFoundException, 2 NoSuchFileOrDirectory, InsufficientPermissionsException; 3 MoveCommand.java 4 5 @Override 6 public void execute() 7 throws InsufficientPermissionsException, NoSuchFileOrDirectory, ExecutionException, 8 CancelledOperationException { 9 if (isTrace()) { 10 Log.v(TAG, 11 String.format("Creating from %s to %s", this.mSrc, this.mDst)); //$NON-NLS-1$ 12 } 13 14 File s = new File(this.mSrc); 15 File d = new File(this.mDst); 16 if (!s.exists()) { 17 if (isTrace()) { 18 Log.v(TAG, "Result: FAIL. NoSuchFileOrDirectory"); //$NON-NLS-1$ 19 } 20 throw new NoSuchFileOrDirectory(this.mSrc); 21 } 22 23 //Move or copy recursively 24 if (d.exists()) { 25 if (!FileHelper.copyRecursive(s, d, getBufferSize(), this)) { 26 if (isTrace()) { 27 Log.v(TAG, "Result: FAIL. InsufficientPermissionsException"); //$NON-NLS-1$ 28 } 29 throw new InsufficientPermissionsException(); 30 } 31 if (!FileHelper.deleteFolder(s)) { 32 if (isTrace()) { 33 Log.v(TAG, "Result: OK. WARNING. Source not deleted."); //$NON-NLS-1$ 34 } 35 } 36 } else { 37 // Move between filesystem is not allow. If rename fails then use copy operation 38 if (!s.renameTo(d)) { 39 if (!FileHelper.copyRecursive(s, d, getBufferSize(), this)) { 40 if (isTrace()) { 41 Log.v(TAG, "Result: FAIL. InsufficientPermissionsException"); //$NON-NLS-1$ 42 } 43 throw new InsufficientPermissionsException(); 44 } 45 if (!FileHelper.deleteFolder(s)) { 46 if (isTrace()) { 47 Log.v(TAG, "Result: OK. WARNING. Source not deleted."); //$NON-NLS-1$ 48 } 49 } 50 } 51 } 52 53 if (isTrace()) { 54 Log.v(TAG, "Result: OK"); //$NON-NLS-1$ 55 } 56 }
6>、copyRecursive
1 /** 2 * Method that copies recursively to the destination 3 * 4 * @param src The source file or folder 5 * @param dst The destination file or folder 6 * @param bufferSize The buffer size for the operation 7 * @return boolean If the operation complete successfully 8 * @throws ExecutionException If a problem was detected in the operation 9 */ 10 public static boolean copyRecursive( 11 final File src, final File dst, int bufferSize, Program program) 12 throws ExecutionException, CancelledOperationException { 13 if (src.isDirectory()) { 14 // Create the directory 15 if (dst.exists() && !dst.isDirectory()) { 16 Log.e(TAG, 17 String.format("Failed to check destionation dir: %s", dst)); //$NON-NLS-1$ 18 throw new ExecutionException("the path exists but is not a folder"); //$NON-NLS-1$ 19 } 20 boolean result = false; 21 if (!dst.exists()) { 22 result = dst.mkdir(); 23 if (!result) { 24 Log.e(TAG, String.format("Failed to create directory: %s", dst)); //$NON-NLS-1$ 25 return false; 26 } 27 } 28 File[] files = src.listFiles(); 29 if (files != null) { 30 int len = files.length; 31 32 /*modify by z start*/ 33 /*if (result) { 34 len -= 1; 35 }*/ 36 /*modify by z end*/ 37 38 for (int i = 0; i < len; i++) { 39 // Short circuit if we've been cancelled. Show's over :( 40 if (program.isCancelled()) { 41 throw new CancelledOperationException(); 42 } 43 44 if (!copyRecursive(files[i], new File(dst, files[i].getName()), bufferSize, 45 program)) { 46 return false; 47 } 48 } 49 } 50 } else { 51 // Copy the directory 52 if (!bufferedCopy(src, dst,bufferSize, program)) { 53 return false; 54 } 55 } 56 return true; 57 }
7>、bufferedCopy
1 /** 2 * Method that copies a file 3 * 4 * @param src The source file 5 * @param dst The destination file 6 * @param bufferSize The buffer size for the operation 7 * @return boolean If the operation complete successfully 8 */ 9 public static boolean bufferedCopy(final File src, final File dst, 10 int bufferSize, Program program) 11 throws ExecutionException, CancelledOperationException { 12 BufferedInputStream bis = null; 13 BufferedOutputStream bos = null; 14 try { 15 bis = new BufferedInputStream(new FileInputStream(src), bufferSize); 16 bos = new BufferedOutputStream(new FileOutputStream(dst), bufferSize); 17 int read = 0; 18 byte[] data = new byte[bufferSize]; 19 while ((read = bis.read(data, 0, bufferSize)) != -1) { 20 // Short circuit if we've been cancelled. Show's over :( 21 if (program.isCancelled()) { 22 throw new CancelledOperationException(); 23 } 24 bos.write(data, 0, read); //看到了吧,最終的文件拷貝,黏貼就是io流的操作 25 } 26 return true; 27 28 } catch (Throwable e) {} finally {} 29 }