根据AndroidManifest.xml了解到文件管理器的主活动为FileManagerOperationActivity,因此下面的源码都是以此活动为基础进行学习的。
作为文件管理器的主活动,public class FileManagerOperationActivityextendsAbsBaseActivityimplementsAdapterView.OnItemLongClickListener,NfcAdapter.CreateBeamUrisCallback{...},该活动继承了一个名为AbsBaseActivity的类,并且实现了一些接口。其中AbsBaseActivity类是文件管理器的基类,AbsBaseActivity直接继承于Activity。
从主活动开始根据文件管理器的功能进行源码的学习,以达到对源码的用法与用在哪更加的清晰明了,并且在一个方法被调用时,大多数的时候都可以在logcat中观察到打印的log,再根据log信息去查找方法,就可以找到实现某项功能时用到了什么方法,并以此为根据进行深入学习。
1.1 应用未运行时打开文件管理器:
1.1首先调用的方法是 onCreate(Bundle savedInstanceState)方法,
onCreate(Bundle savedInstanceState)方法的作用有:
1.PDebug.Start(...)与PDebug.End(...)分别在刚发的开始与结束时打印log进行提示,其中通过引入utils文件夹下的PDebug.java来使用Start与End方法进行log的打印;
2.LogUtils.d(TAG, "onCreate()");,进行log的打印,也是引入utils下的LogUtils类里的d()方法;
3.mSortType = getPrefsSortBy(),从首选项中获取排序类型,其中mSortType在AbsBaseActivity中定义protected int mSortType = 0;getPrefsSortBy()方法在FileManagerOperationActivity中定义作用就是从首选项中获取排序类型;
4.getResources().getConfiguration().orientation方法获得配置中屏幕的方向,getResources()在ContextImpl.java中定义的方法,getConfiguration()在ResourcesManager.java中定义的方法。该方法可以直接在应用时使用;
5.NfcAdapter.getdefaultAdapter(this)方法获取默认的NFC,但是由于我们公司不使用这个功能所以没深入了解;
6.new IntentFilter(...)实例化一个活动,为注册广播提供活动参数;
7.registerReceiver(...)进行广播的注册,其中传入两个参数分别为广播接收器与6步骤中定义的过滤器,广播接收器mLocaleChangedReceiver在本类中private BroadcastReceiver mLocaleChangedReceiver并且重写了onReceive方法,onReceive方法中的AlertDialogFragment通过引入的AlertDialogFragment类使用,AlertDialogFragment extends DialogFragment implements OnClickListener,调用了getFragmentManager().findFragmentBuTag()与getArguement().getString()方法,这些方法都是在DialogFragment中定义的;
8.mAdapter在AbsBaseActivity中定义类型为FileInfoAdapter,FileInfoAdapter extends BaseAdapter ,用到了FileInfoAdapter类中的getCheckedItemCount()与getCheckedFileInfoItemsList()方法,分别获取已检查项目的数量和列表,getCheckedFileInfoItemsList()方法是public List
9.DetailInfoListenerimplementsFileManagerService.OperationEventListener,OnDismissListener,此处用到的是DetailInfoListene类中的getDetailInfo方法获取大小。
1.2 onResume()方法,该方法的作用有:
1. 判断文件的最后修改时间与更新文件最后的修改时间是否相同,不相同则更新文件信息。该判断中使用了本类中定义的变量 private FileInfo mTxtFile = null;当mTxtFile不为空时进行判断,使用到了FileInfo.java中的三个方法进行判断的操作, getFileLastModifiedTime()//得到文件的最后修改时间
getNewModifiedTime()//更新文件的最后修改时间
updateFileInfo()//更新文件信息
然后重新调用onResume()方法
2.进行服务是否忙碌与服务是否为空的判断,其中用到的mService变量在AbsBaseActivity中进行了定义,类型为FileManagerService而mService使用的isBusy方法是在service包下的FileManagerService类中进行定义的public boolean isBusy(String activityName)//判断服务是否忙碌,当服务不为空并且不忙碌的时候判断时候显示删除对话框与重命名对话框,这两个在启动时并未用到因此先不提,之后再删除与重命名功能中进行介绍;
1.3在第一次运行的时候会弹出对话框进行权限的授予和拒绝,
弹出对话框进行权限的申请选择(第一次打开时需要,授予权限之后就不需要了,若拒绝则退出程序)拒绝授予权限:
允许授予权限:
如果拒绝授予权限时没有全部拒绝,下次打开时只会进行拒绝掉的权限的申请操作,已允许的权限不会进行询问。
此时用到的方法是onRequestPermissionsResult(int requestCode, String permissions[],int[] grantResults){...},该方法的作用是在打开文件管理器是进行授权操作;
1.4 调用serviceConnected(){...}方法,进行服务的连接操作,该方法的作用有:
1.LogUtils.d(..)//打印log进行提示
2. Bundle mSavedInstanceState = null(在AbsBaseActivity中定义),当mSavedinstanceState不为空时,使用getInt()方法得到restoreViewMode方法需要传入的三个参数,restoreViewMode(int mode, int position, int top)方法主要是进行用于在不同模式时的各项功能的显示与使用问题,其中用到了FileInfoAdapter.MODE_EDIT,这个是在长按文件夹或者文件之后进入到的模式为编辑模式,默认的模式是MODE_NORMAL模式,FileInfoAdapter中总共有三种模式:MODE_EDIT、MODE_NORMAL、MODE_SEARCH三种模式,切换这三种模式使用到的方法为changMode(*)方法,changMode(*)方法在此方法中使用,用于改变模式的时候的视图的改变。其中在AbsBaseActivity中protected ListView mListView = null;用于显示文件,然后使用LIstView中的setFastScrollEnabled方法将电机进入文件功能取消,private ActionMode mActionMode;与public final ActionModeCallBack mActionModeCallBack= new ActionModeCallBack();使用startActionMode(mActionModeCallBack)与updateActionMode方法进行活动模式的启动与更新。然后使用Bundle中的getString方法判断模式改变时功能的失效与显示问题;
3.进行是否显示删除对话框和重命名对话框的判断;
4.mListView.setOnItemLongClickListener(this):使用ListView中的监听触发响应事件;
1.5 调用onCreateOptionsMenu(Menu menu){...}//创建菜单
1.MenuInflater 是Android.View.MenuInflater中的类并且使用了其中的getMenuInflater()方法与inflate()方法;
1.6 调用onPrepareOptionsmenu(*)方法//创建菜单栏中的复制等功能的显示,并且根据不同的目录进行不同功能的显示与不显示等等判断操作;
1.7 关于onCreateOptionsMenu()方法与onPrepareOptionsMenu方法调用两次的情况,在启动文件管理器时第一次调用这两个方法,第二次调用实在serviceConnected方法中的restoreViewMode时进行的第二次调用;这两个方法在每次视图模式发生变化的时候都会重新调用;
1.3点击home键后点击文件管理器:
小结:1、2、3是Activity生命周期的不同的操作只是调用方法比 第一次进入到文件管理器有些减少因此不进行重复;
根据情况4、5、6都是点击操作只是点击的东西不同而已,三种情况都需要用到onItemClick方法然后再根据点击的文件等属性的不同进行区分;
Public void onItemClick(Adapter> parent,View view,int position,long id){
1.LogUtils.d(...)//打印点击的位置
2.mService!=null&&mService.isBusy(...)//判断若服务忙碌则退出;
3.通过mAdapter.isMode(...)//判断是否为正常模式若为正常模式;
4.正常模式下(3条件下)通过判断position>=mAdapter.getCount()或position<0则通过LogUtils.e打印错误信息并返回;
5.如果4条件不成立即(position <= mAdapter.getCount() && position >=0)则使用FileInfo selecteItemFileInfo = (FileInfo) mAdapter.getItem(position);得到选择项目的信息,其中getItem是FileInfoAdapter中的一个方法,public FileInfo getItem(int pos){...}//该方法获取指定位置上的项目的名称,传入项目的位置并返回项的名称;
6.在5条件成立下,如果选择的文件是目录则LogUtils.v打印信息。
然后使用addToNavigationList(*)方法,addToNavigationList方法在AbsBaseActivity类中定义为protected void addToNavigationList(String path,FileInfo selectedFileInfo,int top){...}//在导航列表中添加一条路径。然后调用showDirectoryContent(*)方法,该方法在AbsBaseActivity中定义protected void showDirectoryContent(String path){...}//该方法是从一个目录获取所有文件/文件夹,并显示他们的列表视图,传入目录路径;
如果选择的文件不是目录而是文件,则进行打开操作。先boolean canOpen = true;定义一个标志位位置后判断做准备,之后String mimeType = selectItemFileInfo.getFileMimeType(*);其中使用了FileInfo.java中的getFileMimeType()方法,public String getFileMimeType(FileManagerService service){...}//(MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型。是设定某种扩展名的文件用一种应用程序来打开的方式)类型该方法获取此文件的mime类型,继续进行判断(使用isDrmFile()方法,该方法同样是在FileInfo类中定义:public boolean isDrmFile(){...}//若是DRM文件返回真,不是反之)如果文件是DRM文件(DRM,英文全称Digital Rights Management, 可以翻译为:数字版权管理。 由于数字化信息的特点决定了必须有另一种独特的技术,来加强保护这些数字化的音视频节目内容,文档、电子书籍的版权,该技术就是数字权限管理技术---DRM(digital right management))则使用utlis包下的DrmManager类中的getInstance().getOriginalMimeType(*)//其中getInstance方法用于得到一个DRMManager对象,getOriginalMimeType方法用于获取文件原始的MIME类型,public String getOriginalMimeType(String path){...}//传入一个文件的路径并返回它的原始的MIME类型;然后再使用TextUtils.isEmpty(mimeType)方法判断mime类型是否为空如果为空则把标志位置为false并打印log,其中TextUtils是android.text.TextUtils类并使用该类中的isEmpty方法进行判断。最后使用了mToastHelper.showToast(*)方法,显示一个toast,其中变量mToastHelper在AbsBaseActivity中定义类型为ToastHelper,showToast();方法在ToastHelper.java中定义用法是显示一个短的toast;
如果标志位为true,则使用隐式启动的活动的方式打开文件,先使用Intent+Uri,若Uri为空则使用setFlags+setDataAndType的方式;
7.如果现在的模式不是正常的模式则使用LogUtils.d打印log提示现在是编辑视图,然后使用
mAdapter.getItem(position).isChecked()方法//设置背景颜色,然后调用setChecked(...);方法设置复选框,然后调用updateActionMode()方法更新活动的模式,其中mActionModeCallBack变量在本类中定义为ActionModeCallBack类型这个类在android.view.ActionMode类中,最后用notifyDataSetChanged()方法通知观察者,数据已经更改刷新自己,该方法在Baseadapter中定义void类型,该方法的作用就是有改变时刷新自己;}
最后调用完onItemClick(AdapterView> parent, View view, int position, long id)方法之后会重新调用onCreateOptionsMenu与onPrepareOptionsMenu方法刷新视图菜单;
功能:菜单栏上面会显示你所在当前目录下的路径,对菜单栏上的路径进行点击可以直接进入到你点击的目录下。
这时调用的方法是onClick(),该方法定义在FileManagerOperationActivity中为public void onClick(View view){...}
1.先使用mService.isBusy()方法进行判断服务是否忙碌若忙碌则使用LogUtils.d打印log并退出,若不忙碌则进行下一步;
2.通过LogUtils.d()与view.getId()方法打印点击选项的Id,然后判断当前模式是否为编辑模式并且挂载点是否为根目录下,若条件满足则更新视图并打印log,否则super调用父类AbsBaseActivity中的onClick()方法,利用该方法中的mTabManager.updateNavigationBar(id);进行更新。其中在AbsBaseActivity中定义了protected TabManager mTabManager = null;并且在本类中创建了一个TabManager类并使用类中的updateNavigationbar方法更新导航栏;
在正常模式(MODE_NORMAL)下,新建文件、查找、粘贴、排序、显示隐藏文件、选择文件或文件夹这几项功能在使用的时候调用的是onOptionsItemSelected方法进行判断与处理。
Public boolean onOptionsItemSelected(MenuItem item){
1.使用LogUtils.d与item.getItemId()打印log;
2.判断mService是否为空并且是否忙碌,若不为空且忙碌则退出,否则根据item.getItemId()获取到的点击的项目进行不同事件发生时的操作。
3.Switch(item.getItemId()){
3.1、新建文件夹:此时调用showCreateFolderDialog()方法,该方法是在AbsBaseActivity类中进行定义的一个方法:protected void showCreateFolderDialog(){
3.1.1、使用LogUtils.d打印log提示;
3.1.2、使用标志位mIsAlertDialogShowing(在AbsBaseActivity中定义为false)进行单一对话框的控制,如果存在对话框则返回,若不存在则进行下一步;
3.1.3、通过isResumed()方法进行判断(isResumed方法是定义在Activity类中(Activity的源码(frameworks/base/core/java/android/app/Activity.java)是个hide的方法)的boolean isResumed()方法,返回mResumed)。当为真时,将标志位mIsAlertDialogShowing设为true防止弹出其他窗口,然后实例化一个EditDialogFragmentbuilder类的对象,EditDialogFragmentbuilder类在AlertDialogFragment类中定义是一个静态类继承了AlertDialogFragmentBuilder,调用了EditDialogFragmentbuilder的setDefault()方法//设置默认的字符串和默认选择,与AlertDialogFragmentBuilder中的setDoneTitle()、setCancelTitle()、setTitle()方法分别用于响应、取消、标题,这三个方法中使用的是Bundle中的putInt方法进行功能的实现(Bundle类在android.os.Bundle中),使用EditTextDialogFragment实例化一个对象=builder.create(),EditTextDialogFragment create()方法是EditDialogFragmentbuilder的一个方法其作用就是实例化一个EditTextDialogFragment对象,然后调用了EditTextDialogFragment对象的setOnEditTextDoneListener与setOnDialogDismissListener两个方法,这两个方法同样是在AlertDialogFragment类中定义的,作用是为该事件的处理添加监听器,其中setOnEditTextDoneListener(EditTextDoneListener listener){}//该方法设置了一个监听器,可以相应按下按钮,其中EditTextDoneListener是本类中定义的一个接口,接口里面有一个onClick(String text)的抽象方法,而在这个新建文件夹的功能中传入的是CreateFolderListener()对象在AbsBaseActivity中定义protected final class CreateFolderListener implements EditTextDoneListener{
重写onClick(String text)方法{
当服务不为空时,使用mService.createFolder(*)方法进行文件夹的创建利用AbsBaseActivity.this.getClass().getName(), dstPath,new LightOperationListener(text)传入方法需要的参数;
}
}
setOnDialogDismissListener用于取消,与setOnEditTextDoneListener类似,定义在同一个类中,但是setOnDialogDismissListener传入的参数是(OnDialogDismissListener listener),OnDialogDismissListener接口中有一个onDialogDismiss()抽象方法,该方法在AbsBaseActivity类中进行了重写,通过标志位控制对话框的消失;
使用了show(*)方法,该方法是在DialogFragment源码中定义的AlertDialogFragment extends DialogFragment,该方法传入两个参数添加到碎片管理器的片段与标记这个片段的标记,这个方法的作用是显示对话框,将片段添加到给定的碎片管理器中。DialogFragment extends Fragment,其中使用Fragment中定义的getFragmentManager方法用于获得添加到碎片管理器的片段,
}
3.2 case:查找:
利用Intent启动Activity的方式进行查找功能的实现,其中使用到了setClass()、putExtra()、setFlags()都是在Intent源码(frameworks/base/core/java/android/content/Intent.java)中进行定义的方法;
3.3 case:粘贴:
在服务不为空的条件下,使用mService.pasteFiles(*)方法进行文件的粘贴操作。pasteFiles()方法在FileManagerService.java中定义,此方法通过启动一个新的CutPasteFilesTask或CopyPasteFilesTask根据类型参数进行粘贴操作,此方法共需要传入四个参数(StringactivityName,它是连接到的任务和操作、List
//进行复制文件的耗时操作
},CutPasteFilesTask等方法与此方法类似。FileOperationTask extends BaseAsyncTask,abstract class BaseAsyncTask extends AsyncTask
3.4 case:排序:
使用showSortDialog()方法,该方法是在FileManagerOperationActivity中定义作用是创建一个排序对话框。showSortDialog(){
3.4.1 判断是否有其他的对话框
3.4.2 在没有其他对话框存在的情况下,如果isResumed(),将mIsAlertDialogShowing标志位设置为true,防止弹出其他窗口。此方法与创建新文件夹的方法基本类似不同的是监听事件的不同与实例化对象的不同,此处只进行不同的说明,实例化对象也只是定义的类的名字不同而已,监听器的定义private class SortClickListener implements OnClickListener{
//重写onClick方法
setPrefsSortBy()方法//将排序类型设置为首选项
dismiss()//取消窗口
sortFileInfoList//从编辑视图切换至导航视图
(setPrefsSortBy sortFileInfoList这两个方法都是在FileManagerOperationActivity中定义的)
}
3.5 case:显示隐藏文件:
在服务不为空时,调用mService.setListType(*)方法,该方法设置列表过滤器,该过滤器将在listview中列出该类型的项目,传入两个参数:类型的列表过滤器、链接到的活动的名字。然后调用listFiles()方法,该方法通过启动一个新的ListFileTask来列出某个目录的文件,传入三个参数:附加到的ListFileTask、目录的路径、监听器,该方法中启动的ListFileTask在service包下面的ListFileTask类中定义extends BaseAsyncTask与前面的复制任务类似;
3.6 case:选择文件或文件夹:
调用switchToEditView();方法该方法就是从正常模式MODE_NORMAL进入到编辑模式MODE_EDIT,该方法在之后的编辑模式中进行操作时进行介绍
3.7 default:重新调用onOptionsItemSelected();方法;
}
}
}
3.1 模式切换
长按目录或文件:
长按状态中点击返回键:
3.1.1 切换至编辑模式
长按目录或文件时调用onItemLongClick(AdapterView> adapter, View v, int position, long id)方法,并在该方法中通过mAdapter.isMode
()方法,传入正常模式进行判断,如果在正常模式下进行长按操作,判断当前挂载点路径是否为根路径与服务是否忙碌,若不是根目录且不忙碌则调用switchToEditView()方法进入到编辑模式并返回为true,否则返回false。
其中使用到了mMountPointManager.isRootPath(mCurrentPath)方法,变量mMountPointManager在AbsBaseActivity类中定义 类型为MountPointManager,isRootPath()方法在MountPointManager类中,用来检查是否为根目录。
switchToEditView()方法在FileManagerOperationActivity类中定义与重载,本类中有两个switchToEditView方法,一个是带参数的一个是不带参数的。此时长按操作时调用的是带参数的switchToEditView方法,private void switchToEditView(int position,int top){
1.使用Logutils.d打印log;
2.使用mAdapter.setChecked(*)方法设置项目的复选框,该方法在FileInfoAdapter类中定义;
3.使用mListView.setSelectionFromTop(position, top);其中mListView在AbsBaseActivity中定义类型为ListView。
4.最后调用无参数的switchToEditView()方法;无参的方法在有参数的方法的下面定义switchToEditView(){
1.LogUtils.d打印log
2. mListView.setFastScrollEnabled(false);快速滚动设置为false;
3.changeMode(FileInfoAdapter.MODE_EDIT);改变模式为编辑模式;
4.ActionMode mActionMode调用startActionMode方法显示菜单选项;
5.mActionModeCallBack.updateActionMode()更新菜单选项;
}
}
3.1.2 切换至普通模式
从编辑模式切换至返回模式调用的方法是 onDestroyActionMode(ActionMode mode){
1.调用switchToNavigationView();方法切换至正常模式,该方法在本类中定义private void switchToNavigationView(){
1.LogUtils.d打印log;
2.View MNavigationView.setVisibility()方法显示View;
3.setFastScrollEnabled(true)因为在编辑模式中改为了false此处修改回来;
4.changeMode();改变模式为正常模式;
5.invalidateoptionsMenu();是选项菜单无效;}
2.如果mActionMode不为空则令其为空,mSelectPopupMenu不为空则令其为空(mSelectPopupMenu在onCreateActionMode中用到类型为PopupMenu);}
功能:复制、剪切、分享、删除、重命名、详情
在MODE_EDIT模式(编辑模式)下,进行的上面的各项的功能的操作调用的方法是onActionItemClicked(ActionMode mode,MenuItem item)switch(item.getItemId()){
3.2.1 case:复制:
调用mFileInfoManager.savePasteList(*)方法,该方法在FileInfoManager类中定义,作用是更新mPasteFilesInfoList,传入两个参数分别是粘贴前的操作是复制还是剪切、复制或剪切文件的列表。private final List
然后调用finish()方法退出编辑模式;
3.2.2 剪切:
剪切功能与复制功能的区别在于,剪切功能需要先进行权限有无的判断,若没有权限需要进行申请,判断权限的有无与申请权限使用的是utils/Permissionutils.java类中的方法,
若有权限则使用savePasteList方法,但此时savePasteList方法中传入的第一个参数是剪切。
之后调用finish()方法退出编辑模式;
3.2.3 删除:
与剪切类似先进行有无读写权限的判断,若没有则申请,有则进行下一步。
调用showDeleteDialog();方法显示删除对话框,显示删除对话框的方法showDeleteDialog的定义与新建文件夹对话框的显示类似都需要先判断有无其他对话框的存在,若没有其它对话框存在在进行操作。
通过mAdapter.getCheckedItemCount()方法判断删除的文件是多个还是单个。然后与新建文件夹对话框显示调用的方法相同,只是参数有些改变。不同的是监听事件的响应操作不同,此时使用的监听器是DeleteListener implements OnClickListener,然后也是重写onClick方法只是重写方法中的调用的方法是mService.deleteFiles()方法进行删除的操作。deleteFiles方法类似于新建文件夹中的createFiles方法;
3.2.4 分享:
调用share()方法;private void share(){
//该方法共享文件/文件夹MMS:只支持单个文件BT:支持单个和多个文件。
1.先判断是否是编辑模式,若不是则推迟,若是编辑模式则使用getCheckedfileInfoItemList()方法获取已选项目的列表;
2.调用files.size()方法判断是多个文件还是单个文件,若为多个文件,检查是否为DRM文件(使用isDrmFile()方法)且是否有权限转移受保护的内容(使用isRightsStatus()方法),若同时满足两个条件则将标志位置为true,然后退出判断。
3.当标志位为true的时候,使用Intent的方式进行文件的发送;
4.单个文件与多个文件类似区别是,单个文件直接获取文件信息而多个文件则需要使用getUri方法将信息打包;
}
3.2.5 重命名:
与删除功能类似,先进性权限的判断与申请,然后调用showRenameDialog(){
1.判断有无其他对话框存在;
2.使用FileInfo fileInfo = mAdapter.getFirstCheckedFileInfoItem()获取选中的项目列表中的第一项;
3.如果fileInfo不为空,则判断是否为文件夹且文件夹拓展是否为空,若不是文件夹且拓展不为空则更改名字的长度选择时去掉拓展的长度;
4.若isResumed(),则与新建文件夹类似,不同时监听事件的不同renameDialogFragment.setOnEditTextDoneListener(new RenameDoneListener(fileInfo));此时监听事件需要传入输入的信息,protected class RenameDoneListener implements EditTextDoneListener {
重写onClick方法,若修改的信息为空则返回,若FileUtils.isExtensionChange判断拓展名是否进行了改变,若拓展名改变则调用showRenameExtensionDialog方法创建一个警报删除对话框,否则当服务不为空切mActionMode不为空时,调用finish方法,若mActionMode为空则调用mService.rename(*)方法进行重命名操作;
}
}。
3.2.6 详情:
直接调用mService.getDetailInfo(*)方法,该方法定义在service/FileManagerService类中通过启动DetailInfotask获取文件或目录的详细信息。DetailInfotask方法在service/DetailInfotask.java中,DetailInfoTask extends BaseAsyncTask 与其他任务操作类似;
3.2.7 全选:
在ActionModeCallBack类中private boolean mSelectedAll = true;
通过判断mSelectAll是否为真进行操作并调用mAdapter.setAllItemChecked(*)方法,若为真则传入true,为假则传入false。setAllItemChecked(*)方法定义在FileInfoAdapter类中作用是设置所有项目的复选框。然后进行更新与失效化;
}