├── .classpath ├── .gitignore ├── .project ├── .settings └── org.eclipse.jdt.core.prefs ├── AndroidManifest.xml ├── README.md ├── libs └── android-support-v13.jar ├── proguard-project.txt ├── project.properties ├── res ├── drawable-hdpi │ └── ic_launcher.png ├── drawable-xhdpi │ ├── back_button.png │ ├── category_button_bg_left.9.png │ ├── category_button_bg_middle.9.png │ ├── category_button_bg_right.9.png │ ├── file_copy.png │ ├── file_cut.png │ ├── file_delete.png │ ├── file_new.png │ ├── file_refresh.png │ ├── file_search.png │ ├── file_viewmode_grid.png │ ├── file_viewmode_list.png │ ├── format_apk.png │ ├── format_app.png │ ├── format_chm.png │ ├── format_ebook.png │ ├── format_excel.png │ ├── format_flash.png │ ├── format_folder.png │ ├── format_html.png │ ├── format_media.png │ ├── format_music.png │ ├── format_pdf.png │ ├── format_picture.png │ ├── format_picture_broken.png │ ├── format_ppt.png │ ├── format_text.png │ ├── format_torrent.png │ ├── format_unkown.png │ ├── format_word.png │ ├── format_zip.png │ ├── ic_launcher.png │ └── sym_link.png ├── drawable-xxhdpi │ └── ic_launcher.png ├── drawable │ ├── bg_file_item_normal.xml │ ├── bg_file_item_select.xml │ ├── category_button_left.xml │ ├── category_button_middle.xml │ └── category_button_right.xml ├── layout │ ├── activity_image_browse.xml │ ├── activity_main.xml │ ├── activity_picker.xml │ ├── activity_search.xml │ ├── dialog_file_property.xml │ ├── layout_file_bookmark.xml │ ├── layout_file_category.xml │ ├── layout_file_explorer.xml │ ├── layout_file_list.xml │ ├── view_ancestor_item.xml │ ├── view_file_item.xml │ └── view_grid_file_item.xml ├── menu │ ├── filelist.xml │ ├── fileop.xml │ └── main.xml ├── values-sw600dp │ └── dimens.xml ├── values-sw720dp-land │ └── dimens.xml ├── values-v14 │ └── styles.xml └── values │ ├── arrays.xml │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── src └── net └── nashlegend └── legendexplorer ├── ImageBrowseActivity.java ├── MainActivity.java ├── PickerActivity.java ├── SearchActivity.java ├── adapter ├── FileListAdapter.java ├── FilePagerAdapter.java └── ImagePagerAdapter.java ├── application └── ExplorerApplication.java ├── consts └── FileConst.java ├── db ├── BookmarkColumn.java ├── BookmarkHelper.java └── FileDataBase.java ├── dialog └── FilePropertyDialog.java ├── fragment ├── BaseFragment.java ├── BookMarksFragment.java ├── CategoriedFragment.java ├── Explorable.java ├── FileListFragment.java └── FilesFragment.java ├── model └── FileItem.java ├── utils ├── FileCategoryHelper.java ├── IconContainer.java ├── SharePreferencesUtil.java └── StorageObserver.java └── view ├── DropDownAncestorList.java ├── FileGridItemView.java ├── FileItemView.java ├── FilePropertyView.java ├── FolderViewPager.java ├── ImageViewState.java ├── ImageViewer.java ├── TheImage.java └── ZoomImageView.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Files for the Dalvik VM 2 | *.dex 3 | 4 | # Java class files 5 | *.class 6 | 7 | # Generated files 8 | bin/ 9 | gen/ 10 | 11 | # Gradle files 12 | .gradle/ 13 | build/ 14 | 15 | # Local configuration file (sdk path, etc) 16 | local.properties 17 | 18 | # Proguard folder generated by Eclipse 19 | proguard/ 20 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | LegendExplorer 4 | 5 | 6 | 7 | 8 | 9 | com.android.ide.eclipse.adt.ResourceManagerBuilder 10 | 11 | 12 | 13 | 14 | com.android.ide.eclipse.adt.PreCompilerBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.jdt.core.javabuilder 20 | 21 | 22 | 23 | 24 | com.android.ide.eclipse.adt.ApkBuilder 25 | 26 | 27 | 28 | 29 | 30 | com.android.ide.eclipse.adt.AndroidNature 31 | org.eclipse.jdt.core.javanature 32 | 33 | 34 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 3 | org.eclipse.jdt.core.compiler.compliance=1.6 4 | org.eclipse.jdt.core.compiler.source=1.6 5 | -------------------------------------------------------------------------------- /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 50 | 51 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LegendExplorer 2 | ============== 3 | 文件管理器,功能齐全,依赖 https://github.com/NashLegend/LegendUtils 4 | -------------------------------------------------------------------------------- /libs/android-support-v13.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/libs/android-support-v13.jar -------------------------------------------------------------------------------- /proguard-project.txt: -------------------------------------------------------------------------------- 1 | # To enable ProGuard in your project, edit project.properties 2 | # to define the proguard.config property as described in that file. 3 | # 4 | # Add project specific ProGuard rules here. 5 | # By default, the flags in this file are appended to flags specified 6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt 7 | # You can edit the include path and order by changing the ProGuard 8 | # include property in project.properties. 9 | # 10 | # For more details, see 11 | # http://developer.android.com/guide/developing/tools/proguard.html 12 | 13 | # Add any project specific keep options here: 14 | 15 | # If your project uses WebView with JS, uncomment the following 16 | # and specify the fully qualified class name to the JavaScript interface 17 | # class: 18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 19 | # public *; 20 | #} 21 | -------------------------------------------------------------------------------- /project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-19 15 | android.library.reference.1=../LegendUtils 16 | -------------------------------------------------------------------------------- /res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/back_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/back_button.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/category_button_bg_left.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/category_button_bg_left.9.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/category_button_bg_middle.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/category_button_bg_middle.9.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/category_button_bg_right.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/category_button_bg_right.9.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/file_copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/file_copy.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/file_cut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/file_cut.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/file_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/file_delete.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/file_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/file_new.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/file_refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/file_refresh.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/file_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/file_search.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/file_viewmode_grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/file_viewmode_grid.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/file_viewmode_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/file_viewmode_list.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/format_apk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/format_apk.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/format_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/format_app.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/format_chm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/format_chm.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/format_ebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/format_ebook.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/format_excel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/format_excel.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/format_flash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/format_flash.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/format_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/format_folder.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/format_html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/format_html.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/format_media.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/format_media.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/format_music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/format_music.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/format_pdf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/format_pdf.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/format_picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/format_picture.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/format_picture_broken.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/format_picture_broken.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/format_ppt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/format_ppt.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/format_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/format_text.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/format_torrent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/format_torrent.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/format_unkown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/format_unkown.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/format_word.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/format_word.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/format_zip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/format_zip.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/sym_link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xhdpi/sym_link.png -------------------------------------------------------------------------------- /res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NashLegend/LegendExplorer/419db73d9005f508f59135c9d001c9b60482318b/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable/bg_file_item_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /res/drawable/bg_file_item_select.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /res/drawable/category_button_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /res/drawable/category_button_middle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /res/drawable/category_button_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /res/layout/activity_image_browse.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /res/layout/activity_picker.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /res/layout/activity_search.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /res/layout/dialog_file_property.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | 37 | 38 | 43 | 44 | 48 | 49 | 50 | 51 | 52 | 59 | 60 | 65 | 66 | 70 | 71 | 72 | 73 | 74 | 81 | 82 | 87 | 88 | 92 | 93 | 94 | 95 | 96 | 103 | 104 | 109 | 110 | 114 | 115 | 116 | 117 | 118 | 125 | 126 | 131 | 132 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /res/layout/layout_file_bookmark.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 23 | 24 | 30 | 31 | 37 | 38 | 39 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /res/layout/layout_file_category.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 17 | 18 | 24 | 25 | 31 | 32 | 36 | 37 | 40 | 41 | 44 | 45 | 48 | 49 | 50 | 54 | 55 | 58 | 59 | 62 | 63 | 66 | 67 | 68 | 72 | 73 | 76 | 77 | 80 | 81 | 84 | 85 | 86 | 87 | 93 | 94 | 98 | 99 | 102 | 103 | 106 | 107 | 110 | 111 | 112 | 116 | 117 | 120 | 121 | 124 | 125 | 128 | 129 | 130 | 134 | 135 | 138 | 139 | 142 | 143 | 146 | 147 | 148 | 149 | 150 | 154 | 155 | 158 | 159 | 164 | 165 | 170 | 171 | 172 | 177 | 178 | 179 | 180 | 181 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /res/layout/layout_file_explorer.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 23 | 24 | 30 | 31 | 37 | 38 | 39 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /res/layout/layout_file_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 12 | 13 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /res/layout/view_ancestor_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 20 | 21 | 29 | 30 | 31 | 35 | 36 | -------------------------------------------------------------------------------- /res/layout/view_file_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | 17 | 18 | 24 | 25 | 26 | 33 | 34 | 39 | 40 | -------------------------------------------------------------------------------- /res/layout/view_grid_file_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 23 | 24 | 31 | 32 | 43 | 44 | -------------------------------------------------------------------------------- /res/menu/filelist.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 16 | 22 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /res/menu/fileop.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 15 | 21 | 26 | 31 | 36 | 41 | 46 | 47 | -------------------------------------------------------------------------------- /res/menu/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /res/values-sw600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /res/values-sw720dp-land/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 128dp 8 | 9 | 10 | -------------------------------------------------------------------------------- /res/values-v14/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 11 | 12 | 13 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | png 6 | gif 7 | jpg 8 | jpeg 9 | bmp 10 | 11 | 12 | mp3 13 | wav 14 | ogg 15 | midi 16 | 17 | 18 | mp4 19 | rmvb 20 | avi 21 | flv 22 | mkv 23 | wmv 24 | 25 | 26 | apk 27 | 28 | 29 | jar 30 | zip 31 | rar 32 | gz 33 | 7z 34 | img 35 | tar 36 | 37 | 38 | htm 39 | html 40 | php 41 | jsp 42 | asp 43 | 44 | 45 | txt 46 | java 47 | c 48 | cpp 49 | py 50 | xml 51 | json 52 | log 53 | 54 | 55 | doc 56 | docx 57 | 58 | 59 | xls 60 | xlsx 61 | 62 | 63 | ppt 64 | pptx 65 | 66 | 67 | pdf 68 | 69 | 70 | epub 71 | caj 72 | umd 73 | ebk2 74 | ebk3 75 | 76 | 77 | torrent 78 | 79 | 80 | chm 81 | 82 | 83 | -------------------------------------------------------------------------------- /res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #ff00ffff 5 | 6 | -------------------------------------------------------------------------------- /res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16dp 5 | 16dp 6 | 100dp 7 | 8dp 8 | 9 | 10 | -------------------------------------------------------------------------------- /res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | LeX 5 | 设置 6 | 复制 7 | 剪切 8 | 删除 9 | 压缩为zip 10 | 解压zip 11 | 收藏 12 | 重命名 13 | 属性 14 | 搜索 15 | 添加 16 | 视图模式 17 | 显示/不显示隐藏文件 18 | 刷新 19 | 取消 20 | 确定 21 | 返回 22 | 全部 23 | 安装包 24 | 文档 25 | 音乐 26 | 图片 27 | 视频 28 | zip 29 | 30 | 文件名 31 | 文件类型 32 | 文件路径 33 | 文件大小 34 | 包含文件数量 35 | 创建时间 36 | 文件选择 37 | 昵嚎! 38 | 搜索 39 | 图片 40 | 音频 41 | 42 | -------------------------------------------------------------------------------- /res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 16 | 17 | 23 | 24 | 30 | 31 | 34 | 35 | 36 | 37 | 40 | 41 | 42 | 45 | 46 | 49 | 50 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/ImageBrowseActivity.java: -------------------------------------------------------------------------------- 1 | 2 | package net.nashlegend.legendexplorer; 3 | 4 | import net.nashlegend.legendexplorer.adapter.ImagePagerAdapter; 5 | import net.nashlegend.legendexplorer.consts.FileConst; 6 | import net.nashlegend.legendexplorer.view.ImageViewer; 7 | 8 | import net.nashlegend.legendexplorer.R; 9 | 10 | import android.annotation.SuppressLint; 11 | import android.app.Activity; 12 | import android.os.Bundle; 13 | import android.support.v4.view.ViewPager; 14 | import android.view.Window; 15 | import android.view.WindowManager; 16 | 17 | public class ImageBrowseActivity extends Activity { 18 | 19 | ImageViewer imageViewer; 20 | ViewPager viewPager; 21 | ImagePagerAdapter adapter; 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | requestWindowFeature(Window.FEATURE_NO_TITLE); 26 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 27 | WindowManager.LayoutParams.FLAG_FULLSCREEN); 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_image_browse); 30 | final String path = getIntent().getStringExtra(FileConst.Extra_File_Path); 31 | viewPager = (ViewPager) findViewById(R.id.fullscreen_content); 32 | adapter = new ImagePagerAdapter(this, path); 33 | if (adapter.getImageIndex() >= 0) { 34 | viewPager.setAdapter(adapter); 35 | viewPager.setCurrentItem(adapter.getImageIndex()); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/MainActivity.java: -------------------------------------------------------------------------------- 1 | package net.nashlegend.legendexplorer; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Timer; 5 | 6 | import net.nashlegend.legendexplorer.adapter.FilePagerAdapter; 7 | import net.nashlegend.legendexplorer.consts.FileConst; 8 | import net.nashlegend.legendexplorer.fragment.BaseFragment; 9 | import net.nashlegend.legendexplorer.fragment.BookMarksFragment; 10 | import net.nashlegend.legendexplorer.fragment.CategoriedFragment; 11 | import net.nashlegend.legendexplorer.fragment.FilesFragment; 12 | import net.nashlegend.legendexplorer.utils.StorageObserver; 13 | import net.nashlegend.legendexplorer.view.FolderViewPager; 14 | 15 | import net.nashlegend.legendexplorer.R; 16 | import net.nashlegend.legendutils.Tools.TimerUtil; 17 | import net.nashlegend.legendutils.Tools.ToastUtil; 18 | 19 | import android.os.Bundle; 20 | import android.os.Environment; 21 | import android.os.FileObserver; 22 | import android.app.Activity; 23 | import android.content.BroadcastReceiver; 24 | import android.content.Context; 25 | import android.content.Intent; 26 | import android.content.IntentFilter; 27 | import android.util.Log; 28 | import android.view.KeyEvent; 29 | import android.view.Menu; 30 | import android.view.MenuItem; 31 | import android.view.MenuItem.OnActionExpandListener; 32 | import android.widget.SearchView; 33 | import android.widget.SearchView.OnQueryTextListener; 34 | 35 | public class MainActivity extends Activity { 36 | 37 | private FolderViewPager pager; 38 | private FilePagerAdapter adapter; 39 | private ArrayList list; 40 | private FilesFragment filesFragment; 41 | private BookMarksFragment bookMarksFragment; 42 | private CategoriedFragment classifiedFragment; 43 | private FileBroadcastReceiver fileBroadcastReceiver; 44 | private Menu mMenu; 45 | private SearchView searchView; 46 | 47 | public static final int FlagSearchFileItem = 1; 48 | public static final int FlagToggleViewItem = 2; 49 | public static final int FlagAddFileItem = 4; 50 | public static final int FlagToggleHiddleItem = 8; 51 | public static final int FlagRefreshListItem = 16; 52 | public static final int MaskAllFileList = 31; 53 | 54 | public static final int FlagCopyFileItem = 1; 55 | public static final int FlagCutFileItem = 2; 56 | public static final int FlagDeleteFileItem = 4; 57 | public static final int FlagRenameFileItem = 8; 58 | public static final int FlagZipFileItem = 16; 59 | public static final int PropertyItemFlag = 32; 60 | public static final int FlagUnzipFileItem = 64; 61 | public static final int FlagFavorItem = 128; 62 | public static final int MaskAllOperation = 255; 63 | 64 | public static final int MaskNormalListUnzip = FlagFavorItem 65 | | FlagZipFileItem; 66 | public static final int MaskNormalListFavor = FlagUnzipFileItem; 67 | public static final int MaskNormalListNormal = FlagFavorItem 68 | | FlagUnzipFileItem; 69 | 70 | @Override 71 | protected void onCreate(Bundle savedInstanceState) { 72 | super.onCreate(savedInstanceState); 73 | setContentView(R.layout.activity_main); 74 | 75 | filesFragment = new FilesFragment(); 76 | bookMarksFragment = new BookMarksFragment(); 77 | classifiedFragment = new CategoriedFragment(); 78 | 79 | list = new ArrayList(); 80 | list.add(filesFragment); 81 | list.add(bookMarksFragment); 82 | list.add(classifiedFragment); 83 | 84 | pager = (FolderViewPager) findViewById(R.id.pager); 85 | adapter = new FilePagerAdapter(getFragmentManager()); 86 | adapter.setList(list); 87 | pager.setAdapter(adapter); 88 | pager.setScrollEnabled(true); 89 | 90 | IntentFilter filter = new IntentFilter(); 91 | filter.addAction(FileConst.Action_Open_Folder); 92 | filter.addAction(FileConst.Action_FileItem_Long_Click); 93 | filter.addAction(FileConst.Action_FileItem_Unselect); 94 | filter.addAction(FileConst.Action_FileItem_Select); 95 | filter.addAction(FileConst.Action_Set_File_Operation_ActionBar); 96 | filter.addAction(FileConst.Action_Set_File_View_ActionBar); 97 | filter.addAction(FileConst.Action_File_Operation_Done); 98 | filter.addAction(FileConst.Action_Quit_Search); 99 | fileBroadcastReceiver = new FileBroadcastReceiver(); 100 | registerReceiver(fileBroadcastReceiver, filter); 101 | } 102 | 103 | @Override 104 | public boolean onCreateOptionsMenu(Menu menu) { 105 | mMenu = menu; 106 | getMenuInflater().inflate(R.menu.filelist, menu); 107 | MenuItem searchItem = mMenu.findItem(R.id.action_search); 108 | searchView = (SearchView) searchItem.getActionView(); 109 | searchItem.setOnActionExpandListener(onActionExpandListener); 110 | searchView.setOnQueryTextListener(onQueryTextListener); 111 | return true; 112 | } 113 | 114 | @Override 115 | public boolean onOptionsItemSelected(MenuItem item) { 116 | switch (item.getItemId()) { 117 | case R.id.action_new: 118 | addNewFile(); 119 | break; 120 | case R.id.action_refresh: 121 | refreshFileList(); 122 | break; 123 | case R.id.action_viewmode: 124 | toggleViewMode(); 125 | break; 126 | case R.id.action_toggle_hidden: 127 | toggleShowHidden(); 128 | break; 129 | case R.id.action_copy: 130 | copyFile(); 131 | break; 132 | case R.id.action_cut: 133 | moveFile(); 134 | break; 135 | case R.id.action_delete: 136 | deleteFile(); 137 | break; 138 | case R.id.action_rename: 139 | renameFile(); 140 | break; 141 | case R.id.action_zip: 142 | zipFile(); 143 | break; 144 | case R.id.action_property: 145 | propertyFile(); 146 | break; 147 | case R.id.action_favor: 148 | favorFile(); 149 | break; 150 | case R.id.action_unzip: 151 | unzipFile(); 152 | break; 153 | 154 | default: 155 | break; 156 | } 157 | return super.onOptionsItemSelected(item); 158 | } 159 | 160 | public void showFileOperationMenu(int exclu) { 161 | if (mMenu == null) { 162 | return; 163 | } 164 | searchView.setOnQueryTextListener(null); 165 | pager.setScrollEnabled(false); 166 | if (mMenu.findItem(R.id.action_copy) == null) { 167 | mMenu.clear(); 168 | getMenuInflater().inflate(R.menu.fileop, mMenu); 169 | } 170 | if (exclu >= FlagFavorItem) { 171 | mMenu.findItem(R.id.action_favor).setVisible(false); 172 | exclu = exclu ^ FlagFavorItem; 173 | } else { 174 | mMenu.findItem(R.id.action_favor).setVisible(true); 175 | } 176 | if (exclu >= FlagUnzipFileItem) { 177 | mMenu.findItem(R.id.action_unzip).setVisible(false); 178 | exclu = exclu ^ FlagUnzipFileItem; 179 | } else { 180 | mMenu.findItem(R.id.action_unzip).setVisible(true); 181 | } 182 | if (exclu >= PropertyItemFlag) { 183 | mMenu.findItem(R.id.action_property).setVisible(false); 184 | exclu = exclu ^ PropertyItemFlag; 185 | } else { 186 | mMenu.findItem(R.id.action_property).setVisible(true); 187 | } 188 | if (exclu >= FlagZipFileItem) { 189 | mMenu.findItem(R.id.action_zip).setVisible(false); 190 | exclu = exclu ^ FlagZipFileItem; 191 | } else { 192 | mMenu.findItem(R.id.action_zip).setVisible(true); 193 | } 194 | if (exclu >= FlagRenameFileItem) { 195 | mMenu.findItem(R.id.action_rename).setVisible(false); 196 | exclu = exclu ^ FlagRenameFileItem; 197 | } else { 198 | mMenu.findItem(R.id.action_rename).setVisible(true); 199 | } 200 | if (exclu >= FlagDeleteFileItem) { 201 | mMenu.findItem(R.id.action_delete).setVisible(false); 202 | exclu = exclu ^ FlagDeleteFileItem; 203 | } else { 204 | mMenu.findItem(R.id.action_delete).setVisible(true); 205 | } 206 | if (exclu >= FlagCutFileItem) { 207 | mMenu.findItem(R.id.action_cut).setVisible(false); 208 | exclu = exclu ^ FlagCutFileItem; 209 | } else { 210 | mMenu.findItem(R.id.action_cut).setVisible(true); 211 | } 212 | if (exclu >= FlagCopyFileItem) { 213 | mMenu.findItem(R.id.action_copy).setVisible(false); 214 | } else { 215 | mMenu.findItem(R.id.action_copy).setVisible(true); 216 | } 217 | } 218 | 219 | public void showFileListMenu(int exclu) { 220 | if (mMenu == null) { 221 | return; 222 | } 223 | pager.setScrollEnabled(true); 224 | if (mMenu.findItem(R.id.action_search) == null) { 225 | mMenu.clear(); 226 | getMenuInflater().inflate(R.menu.filelist, mMenu); 227 | } 228 | 229 | if (exclu >= FlagRefreshListItem) { 230 | mMenu.findItem(R.id.action_refresh).setVisible(false); 231 | exclu = exclu ^ FlagRefreshListItem; 232 | } else { 233 | mMenu.findItem(R.id.action_refresh).setVisible(true); 234 | } 235 | if (exclu >= FlagToggleHiddleItem) { 236 | mMenu.findItem(R.id.action_toggle_hidden).setVisible(false); 237 | exclu = exclu ^ FlagToggleHiddleItem; 238 | } else { 239 | mMenu.findItem(R.id.action_toggle_hidden).setVisible(true); 240 | } 241 | if (exclu >= FlagAddFileItem) { 242 | mMenu.findItem(R.id.action_new).setVisible(false); 243 | exclu = exclu ^ FlagAddFileItem; 244 | } else { 245 | mMenu.findItem(R.id.action_new).setVisible(true); 246 | } 247 | if (exclu >= FlagToggleViewItem) { 248 | mMenu.findItem(R.id.action_viewmode).setVisible(false); 249 | exclu = exclu ^ FlagToggleViewItem; 250 | } else { 251 | mMenu.findItem(R.id.action_viewmode).setVisible(true); 252 | } 253 | if (exclu >= FlagSearchFileItem) { 254 | mMenu.findItem(R.id.action_search).setVisible(false); 255 | } else { 256 | MenuItem searchItem = mMenu.findItem(R.id.action_search); 257 | searchItem.setVisible(true); 258 | searchView = (SearchView) searchItem.getActionView(); 259 | searchItem.setOnActionExpandListener(onActionExpandListener); 260 | searchView.setOnQueryTextListener(onQueryTextListener); 261 | } 262 | } 263 | 264 | private void toggleViewMode() { 265 | Intent intent = new Intent(); 266 | intent.setAction(FileConst.Action_Toggle_View_Mode); 267 | adapter.getItem(pager.getCurrentItem()).doVeryAction(intent); 268 | } 269 | 270 | private void toggleShowHidden() { 271 | Intent intent = new Intent(); 272 | intent.setAction(FileConst.Action_Toggle_Show_Hidden); 273 | adapter.getItem(pager.getCurrentItem()).doVeryAction(intent); 274 | } 275 | 276 | private void copyFile() { 277 | Intent intent = new Intent(); 278 | intent.setAction(FileConst.Action_Copy_File); 279 | adapter.getItem(pager.getCurrentItem()).doVeryAction(intent); 280 | } 281 | 282 | private void moveFile() { 283 | Intent intent = new Intent(); 284 | intent.setAction(FileConst.Action_Move_File); 285 | adapter.getItem(pager.getCurrentItem()).doVeryAction(intent); 286 | } 287 | 288 | private void deleteFile() { 289 | Intent intent = new Intent(); 290 | intent.setAction(FileConst.Action_Delete_File); 291 | adapter.getItem(pager.getCurrentItem()).doVeryAction(intent); 292 | } 293 | 294 | private void renameFile() { 295 | Intent intent = new Intent(); 296 | intent.setAction(FileConst.Action_Rename_File); 297 | adapter.getItem(pager.getCurrentItem()).doVeryAction(intent); 298 | } 299 | 300 | private void zipFile() { 301 | Intent intent = new Intent(); 302 | intent.setAction(FileConst.Action_Zip_File); 303 | adapter.getItem(pager.getCurrentItem()).doVeryAction(intent); 304 | } 305 | 306 | private void propertyFile() { 307 | Intent intent = new Intent(); 308 | intent.setAction(FileConst.Action_Property_File); 309 | adapter.getItem(pager.getCurrentItem()).doVeryAction(intent); 310 | } 311 | 312 | private void addNewFile() { 313 | Intent intent = new Intent(); 314 | intent.setAction(FileConst.Action_Add_New_File); 315 | adapter.getItem(pager.getCurrentItem()).doVeryAction(intent); 316 | } 317 | 318 | private void refreshFileList() { 319 | Intent intent = new Intent(); 320 | intent.setAction(FileConst.Action_Refresh_FileList); 321 | adapter.getItem(pager.getCurrentItem()).doVeryAction(intent); 322 | } 323 | 324 | private void searchFile(String query) { 325 | Intent intent = new Intent(); 326 | intent.setAction(FileConst.Action_Search_File); 327 | intent.putExtra(FileConst.Key_Search_File_Query, query); 328 | adapter.getItem(pager.getCurrentItem()).doVeryAction(intent); 329 | } 330 | 331 | private void unzipFile() { 332 | Intent intent = new Intent(); 333 | intent.setAction(FileConst.Action_Unzip_File); 334 | adapter.getItem(pager.getCurrentItem()).doVeryAction(intent); 335 | } 336 | 337 | private void favorFile() { 338 | Intent intent = new Intent(); 339 | intent.setAction(FileConst.Action_Favor_File); 340 | adapter.getItem(pager.getCurrentItem()).doVeryAction(intent); 341 | } 342 | 343 | private void quitSearchFile() { 344 | MenuItem searchItem = mMenu.findItem(R.id.action_search); 345 | if (searchItem != null) { 346 | searchItem.collapseActionView(); 347 | } 348 | } 349 | 350 | @Override 351 | public boolean onKeyUp(int keyCode, KeyEvent event) { 352 | if (keyCode == KeyEvent.KEYCODE_BACK) { 353 | if (doBackAction()) { 354 | return true; 355 | } 356 | } 357 | return super.onKeyUp(keyCode, event); 358 | } 359 | 360 | private boolean doBackAction() { 361 | return adapter.getItem(pager.getCurrentItem()).doBackAction(); 362 | } 363 | 364 | @Override 365 | protected void onDestroy() { 366 | unregisterReceiver(fileBroadcastReceiver); 367 | super.onDestroy(); 368 | } 369 | 370 | private OnActionExpandListener onActionExpandListener = new OnActionExpandListener() { 371 | 372 | @Override 373 | public boolean onMenuItemActionExpand(MenuItem item) { 374 | pager.setScrollEnabled(false); 375 | return true; 376 | } 377 | 378 | @Override 379 | public boolean onMenuItemActionCollapse(MenuItem item) { 380 | pager.setScrollEnabled(true); 381 | searchFile(""); 382 | return true; 383 | } 384 | }; 385 | 386 | private OnQueryTextListener onQueryTextListener = new OnQueryTextListener() { 387 | private int delayMillis = 300; 388 | private String query = ""; 389 | private Timer timer; 390 | Runnable runnable = new Runnable() { 391 | 392 | @Override 393 | public void run() { 394 | searchFile(query); 395 | } 396 | }; 397 | 398 | @Override 399 | public boolean onQueryTextSubmit(String query) { 400 | ToastUtil.showToast(getApplicationContext(), query); 401 | return false; 402 | } 403 | 404 | @Override 405 | public boolean onQueryTextChange(String newText) { 406 | TimerUtil.clearTimeOut(timer); 407 | query = newText; 408 | timer = TimerUtil.setTimeOut(runnable, delayMillis); 409 | return false; 410 | } 411 | }; 412 | 413 | class FileBroadcastReceiver extends BroadcastReceiver { 414 | 415 | @Override 416 | public void onReceive(Context context, Intent intent) { 417 | String action = intent.getAction(); 418 | int mask = intent.getIntExtra(FileConst.Extra_Menu_Mask, 0); 419 | if (FileConst.Action_Set_File_Operation_ActionBar.equals(action)) { 420 | showFileOperationMenu(mask); 421 | return; 422 | } else if (FileConst.Action_Set_File_View_ActionBar.equals(action)) { 423 | showFileListMenu(mask); 424 | return; 425 | } else if (FileConst.Action_Quit_Search.equals(action)) { 426 | quitSearchFile(); 427 | return; 428 | } 429 | adapter.getItem(pager.getCurrentItem()).doVeryAction(intent); 430 | } 431 | } 432 | 433 | } 434 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/PickerActivity.java: -------------------------------------------------------------------------------- 1 | package net.nashlegend.legendexplorer; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | 6 | import net.nashlegend.legendexplorer.R; 7 | import net.nashlegend.legendexplorer.R.color; 8 | import net.nashlegend.legendutils.BuildIn.FileDialogView; 9 | import net.nashlegend.legendutils.Dialogs.FileDialog; 10 | 11 | import android.app.Activity; 12 | import android.content.Context; 13 | import android.content.Intent; 14 | import android.net.Uri; 15 | import android.os.Bundle; 16 | import android.os.Environment; 17 | import android.util.Log; 18 | import android.view.Menu; 19 | import android.view.MenuItem; 20 | import android.view.View; 21 | import android.view.View.OnClickListener; 22 | import android.view.Window; 23 | import android.widget.Button; 24 | 25 | /** 26 | * 全局文件选择器,Intent.ACTION_GET_CONTENT用来发出要选择项的Action。setType决定要选择的类型 27 | * 28 | * Intent intent = new Intent(Intent.ACTION_GET_CONTENT);_______________________ 29 | * intent.setType("*\/*"); _____________________________________________________ 30 | * Intent wrapperIntent = Intent.createChooser(intent, null);___________________ 31 | * startActivityForResult(wrapperIntent, 1001);_________________________________ 32 | * 33 | * @author NashLegend 34 | * 35 | */ 36 | public class PickerActivity extends Activity { 37 | 38 | private FileDialogView pickerView; 39 | private Button cancelButton; 40 | private Button okButton; 41 | 42 | @Override 43 | protected void onCreate(Bundle savedInstanceState) { 44 | super.onCreate(savedInstanceState); 45 | setContentView(R.layout.activity_picker); 46 | setTitle("选择文件"); 47 | Intent intent = getIntent(); 48 | if (intent != null 49 | && Intent.ACTION_GET_CONTENT.equals(intent.getAction())) { 50 | pickerView = (FileDialogView) findViewById(R.id.picker); 51 | pickerView.setFileMode(FileDialog.FILE_MODE_OPEN_FILE_SINGLE); 52 | pickerView.setInitialPath(Environment.getExternalStorageDirectory() 53 | .getAbsolutePath()); 54 | pickerView.openFolder(); 55 | cancelButton = (Button) pickerView 56 | .findViewById(net.nashlegend.legendutils.R.id.button_dialog_file_cancel); 57 | okButton = (Button) pickerView 58 | .findViewById(net.nashlegend.legendutils.R.id.button_dialog_file_ok); 59 | cancelButton.setOnClickListener(new OnClickListener() { 60 | 61 | @Override 62 | public void onClick(View v) { 63 | setResult(RESULT_CANCELED); 64 | finish(); 65 | } 66 | }); 67 | okButton.setOnClickListener(new OnClickListener() { 68 | 69 | @Override 70 | public void onClick(View v) { 71 | ArrayList files = pickerView.getSelectedFiles(); 72 | if (files != null && files.size() > 0) { 73 | File file = files.get(0); 74 | Intent intent = new Intent(); 75 | Uri uri = Uri.fromFile(file); 76 | intent.setData(uri); 77 | setResult(RESULT_OK, intent); 78 | finish(); 79 | } 80 | } 81 | }); 82 | } 83 | 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/SearchActivity.java: -------------------------------------------------------------------------------- 1 | package net.nashlegend.legendexplorer; 2 | 3 | import net.nashlegend.legendexplorer.R; 4 | 5 | import android.app.Activity; 6 | import android.os.Bundle; 7 | import android.view.Menu; 8 | import android.view.MenuItem; 9 | 10 | public class SearchActivity extends Activity { 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_search); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/adapter/FileListAdapter.java: -------------------------------------------------------------------------------- 1 | package net.nashlegend.legendexplorer.adapter; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | import java.util.Collections; 6 | import java.util.Comparator; 7 | import java.util.Iterator; 8 | 9 | import net.nashlegend.legendexplorer.consts.FileConst; 10 | import net.nashlegend.legendexplorer.db.BookmarkHelper; 11 | import net.nashlegend.legendexplorer.fragment.CategoriedFragment.FileCategory; 12 | import net.nashlegend.legendexplorer.model.FileItem; 13 | import net.nashlegend.legendexplorer.utils.FileCategoryHelper; 14 | import net.nashlegend.legendexplorer.utils.SharePreferencesUtil; 15 | import net.nashlegend.legendexplorer.view.FileGridItemView; 16 | import net.nashlegend.legendexplorer.view.FileItemView; 17 | 18 | import net.nashlegend.legendutils.Tools.FileUtil; 19 | import net.nashlegend.legendutils.Tools.SystemUtil; 20 | 21 | import android.annotation.SuppressLint; 22 | import android.content.Context; 23 | import android.database.Cursor; 24 | import android.media.MediaScannerConnection; 25 | import android.os.AsyncTask; 26 | import android.util.Log; 27 | import android.view.View; 28 | import android.view.ViewGroup; 29 | import android.widget.BaseAdapter; 30 | import android.widget.Filter; 31 | import android.widget.Filterable; 32 | 33 | @SuppressLint("DefaultLocale") 34 | public class FileListAdapter extends BaseAdapter implements Filterable { 35 | private ArrayList list = new ArrayList(); 36 | private Context mContext; 37 | private File currentDirectory; 38 | private boolean displayModeGrid = false; 39 | private Cursor mCursor; 40 | private FileCategory fc; 41 | 42 | public FileListAdapter(Context Context) { 43 | mContext = Context; 44 | } 45 | 46 | @Override 47 | public int getCount() { 48 | return list.size(); 49 | } 50 | 51 | @Override 52 | public Object getItem(int position) { 53 | return list.get(position); 54 | } 55 | 56 | @Override 57 | public long getItemId(int position) { 58 | return position; 59 | } 60 | 61 | @Override 62 | public int getViewTypeCount() { 63 | return 2; 64 | } 65 | 66 | @Override 67 | public int getItemViewType(int position) { 68 | return displayModeGrid ? 1 : 0; 69 | } 70 | 71 | @Override 72 | public View getView(int position, View convertView, ViewGroup parent) { 73 | ViewHolder holder = null; 74 | if (displayModeGrid) { 75 | if (convertView == null) { 76 | holder = new ViewHolder(); 77 | convertView = new FileGridItemView(mContext); 78 | holder.fileGridItemView = (FileGridItemView) convertView; 79 | convertView.setTag(holder); 80 | } else { 81 | holder = (ViewHolder) convertView.getTag(); 82 | } 83 | holder.fileGridItemView.setFileItem(list.get(position), this); 84 | } else { 85 | if (convertView == null) { 86 | holder = new ViewHolder(); 87 | convertView = new FileItemView(mContext); 88 | holder.fileItemView = (FileItemView) convertView; 89 | convertView.setTag(holder); 90 | } else { 91 | holder = (ViewHolder) convertView.getTag(); 92 | } 93 | holder.fileItemView.setFileItem(list.get(position), this); 94 | } 95 | return convertView; 96 | } 97 | 98 | class ViewHolder { 99 | FileItemView fileItemView; 100 | FileGridItemView fileGridItemView; 101 | } 102 | 103 | public ArrayList getList() { 104 | return list; 105 | } 106 | 107 | public void setList(ArrayList list) { 108 | this.list = list; 109 | } 110 | 111 | /** 112 | * 打开文件夹,更新文件列表 113 | * 114 | * @param file 115 | */ 116 | public void openFolder(File file) { 117 | OpenFolderTask task = new OpenFolderTask(); 118 | task.execute(file); 119 | } 120 | 121 | class OpenFolderTask extends AsyncTask> { 122 | 123 | @Override 124 | protected ArrayList doInBackground(File... params) { 125 | ArrayList flist = new ArrayList(); 126 | boolean showhidden = SharePreferencesUtil.readBoolean( 127 | FileConst.Key_Show_Hiddle_Files, false); 128 | File file = params[0]; 129 | currentDirectory = file; 130 | if (file != null 131 | && (file.isDirectory() || file.equals(new File( 132 | FileConst.Value_File_Path_Never_Existed)))) { 133 | flist.clear(); 134 | if (file.equals(new File( 135 | FileConst.Value_File_Path_Never_Existed))) { 136 | BookmarkHelper helper = new BookmarkHelper(mContext); 137 | helper.open(); 138 | flist = helper.getBookmarks(); 139 | helper.close(); 140 | } else { 141 | File[] files = null; 142 | if (file.canRead()) { 143 | files = file.listFiles(); 144 | } else { 145 | if (SystemUtil.isRooted()) { 146 | files = FileUtil.ListFilesWithRoot(file 147 | .getAbsolutePath()); 148 | } 149 | } 150 | if (files != null) { 151 | for (int i = 0; i < files.length; i++) { 152 | if (showhidden) { 153 | flist.add(new FileItem(files[i])); 154 | } else { 155 | if (!files[i].isHidden()) { 156 | flist.add(new FileItem(files[i])); 157 | } 158 | } 159 | 160 | } 161 | } 162 | files = null; 163 | sortList(flist); 164 | } 165 | } 166 | return flist; 167 | } 168 | 169 | @Override 170 | protected void onPostExecute(ArrayList result) { 171 | setList(result); 172 | notifyDataSetChanged(); 173 | } 174 | 175 | } 176 | 177 | public void openCursor(FileCategory f) { 178 | OpenCursorTask task = new OpenCursorTask(); 179 | this.fc = f; 180 | task.execute(fc); 181 | } 182 | 183 | public void closeCursor() { 184 | if (mCursor != null && !mCursor.isClosed()) { 185 | mCursor.close(); 186 | } 187 | } 188 | 189 | class OpenCursorTask extends 190 | AsyncTask> { 191 | 192 | @Override 193 | protected ArrayList doInBackground(FileCategory... params) { 194 | ArrayList flist = new ArrayList(); 195 | currentDirectory = null; 196 | fc = params[0]; 197 | Cursor cursor = FileCategoryHelper.query(fc, mContext); 198 | mCursor = cursor; 199 | flist.clear(); 200 | ArrayList fileNos = new ArrayList(); 201 | if (cursor != null && cursor.getCount() > 0) { 202 | cursor.moveToPosition(-1); 203 | while (cursor.moveToNext()) { 204 | FileItem item = new FileItem(cursor.getString(1)); 205 | if (item.exists()) { 206 | flist.add(item); 207 | } else { 208 | fileNos.add(item); 209 | } 210 | } 211 | } 212 | if (fileNos.size() > 0) { 213 | String[] files = new String[fileNos.size()]; 214 | for (int i = 0; i < fileNos.size(); i++) { 215 | files[i] = fileNos.get(i).getAbsolutePath(); 216 | } 217 | if (mContext != null) { 218 | MediaScannerConnection 219 | .scanFile(mContext, files, null, null); 220 | } 221 | } 222 | sortList(flist); 223 | return flist; 224 | } 225 | 226 | @Override 227 | protected void onPostExecute(ArrayList result) { 228 | setList(result); 229 | notifyDataSetChanged(); 230 | } 231 | 232 | } 233 | 234 | /** 235 | * 选择当前目录下所有文件 236 | */ 237 | public void selectAll() { 238 | for (Iterator iterator = list.iterator(); iterator.hasNext();) { 239 | FileItem fileItem = (FileItem) iterator.next(); 240 | fileItem.setSelected(true); 241 | } 242 | notifyDataSetChanged(); 243 | } 244 | 245 | /** 246 | * 取消所有文件的选中状态 247 | */ 248 | public void unselectAll() { 249 | for (Iterator iterator = list.iterator(); iterator.hasNext();) { 250 | FileItem fileItem = (FileItem) iterator.next(); 251 | fileItem.setSelected(false); 252 | } 253 | notifyDataSetChanged(); 254 | } 255 | 256 | public void change2SelectMode() { 257 | for (Iterator iterator = list.iterator(); iterator.hasNext();) { 258 | FileItem fileItem = (FileItem) iterator.next(); 259 | fileItem.setInSelectMode(true); 260 | } 261 | notifyDataSetChanged(); 262 | } 263 | 264 | public void exitSelectMode() { 265 | for (Iterator iterator = list.iterator(); iterator.hasNext();) { 266 | FileItem fileItem = (FileItem) iterator.next(); 267 | fileItem.setInSelectMode(false); 268 | fileItem.setSelected(false); 269 | } 270 | notifyDataSetChanged(); 271 | } 272 | 273 | public void sortList() { 274 | FileItemComparator comparator = new FileItemComparator(); 275 | Collections.sort(list, comparator); 276 | } 277 | 278 | public void sortList(ArrayList lis) { 279 | FileItemComparator comparator = new FileItemComparator(); 280 | Collections.sort(lis, comparator); 281 | } 282 | 283 | /** 284 | * @return 选中的文件列表 285 | */ 286 | public ArrayList getSelectedFiles() { 287 | ArrayList selectedFiles = new ArrayList(); 288 | for (Iterator iterator = list.iterator(); iterator.hasNext();) { 289 | FileItem file = iterator.next();// 强制转换为File 290 | if (file.isSelected()) { 291 | selectedFiles.add(file); 292 | } 293 | } 294 | return selectedFiles; 295 | } 296 | 297 | public class FileItemComparator implements Comparator { 298 | 299 | @Override 300 | public int compare(FileItem lhs, FileItem rhs) { 301 | if (lhs.isDirectory() != rhs.isDirectory()) { 302 | // 如果一个是文件,一个是文件夹,优先按照类型排序 303 | if (lhs.isDirectory()) { 304 | return -1; 305 | } else { 306 | return 1; 307 | } 308 | } else { 309 | // 如果同是文件夹或者文件,则按名称排序 310 | return lhs.getName().toLowerCase() 311 | .compareTo(rhs.getName().toLowerCase()); 312 | } 313 | } 314 | } 315 | 316 | public File getCurrentDirectory() { 317 | return currentDirectory; 318 | } 319 | 320 | public boolean isDisplayModeGrid() { 321 | return displayModeGrid; 322 | } 323 | 324 | public void setDisplayModeGrid(boolean displayModeGrid) { 325 | this.displayModeGrid = displayModeGrid; 326 | } 327 | 328 | @Override 329 | public Filter getFilter() { 330 | return filter; 331 | } 332 | 333 | private Filter filter = new Filter() { 334 | 335 | @SuppressWarnings("unchecked") 336 | @Override 337 | protected void publishResults(CharSequence constraint, 338 | FilterResults results) { 339 | setList((ArrayList) results.values); 340 | if (results.count > 0) { 341 | notifyDataSetChanged(); 342 | } else { 343 | notifyDataSetInvalidated(); 344 | } 345 | } 346 | 347 | @Override 348 | protected FilterResults performFiltering(CharSequence constraint) { 349 | boolean showhidden = SharePreferencesUtil.readBoolean( 350 | FileConst.Key_Show_Hiddle_Files, false); 351 | String mat = constraint.toString().toLowerCase(); 352 | FilterResults results = new FilterResults(); 353 | ArrayList tmpList = new ArrayList(); 354 | File file = currentDirectory; 355 | if (file != null) { 356 | if (currentDirectory.equals(new File( 357 | FileConst.Value_File_Path_Never_Existed))) { 358 | BookmarkHelper helper = new BookmarkHelper(mContext); 359 | helper.open(); 360 | tmpList = helper.getBookmarks(); 361 | helper.close(); 362 | } else if (file.isDirectory()) { 363 | File[] files = file.listFiles(); 364 | if (files != null) { 365 | for (int i = 0; i < files.length; i++) { 366 | File tmpFile = files[i]; 367 | if (tmpFile.getName().toLowerCase().contains(mat)) { 368 | if (showhidden) { 369 | tmpList.add(new FileItem(tmpFile)); 370 | } else { 371 | if (!files[i].isHidden()) { 372 | tmpList.add(new FileItem(tmpFile)); 373 | } 374 | } 375 | } 376 | 377 | } 378 | } 379 | files = null; 380 | } 381 | } else if (mCursor != null) { 382 | if (mCursor.getCount() > 0) { 383 | mCursor.moveToPosition(-1); 384 | while (mCursor.moveToNext()) { 385 | FileItem item = new FileItem(mCursor.getString(1)); 386 | if (item.getName().toLowerCase().contains(mat)) { 387 | tmpList.add(item); 388 | } 389 | } 390 | } 391 | } 392 | sortList(tmpList); 393 | results.values = tmpList; 394 | results.count = tmpList.size(); 395 | return results; 396 | } 397 | }; 398 | 399 | } 400 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/adapter/FilePagerAdapter.java: -------------------------------------------------------------------------------- 1 | 2 | package net.nashlegend.legendexplorer.adapter; 3 | 4 | import java.util.ArrayList; 5 | 6 | import net.nashlegend.legendexplorer.fragment.BaseFragment; 7 | 8 | 9 | import android.app.FragmentManager; 10 | import android.support.v13.app.FragmentPagerAdapter; 11 | 12 | public class FilePagerAdapter extends FragmentPagerAdapter { 13 | 14 | ArrayList list = new ArrayList(); 15 | 16 | public FilePagerAdapter(FragmentManager fm) { 17 | super(fm); 18 | } 19 | 20 | @Override 21 | public BaseFragment getItem(int arg0) { 22 | return list.get(arg0); 23 | } 24 | 25 | @Override 26 | public int getCount() { 27 | return list.size(); 28 | } 29 | 30 | public ArrayList getList() { 31 | return list; 32 | } 33 | 34 | public void setList(ArrayList list) { 35 | this.list = list; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/adapter/ImagePagerAdapter.java: -------------------------------------------------------------------------------- 1 | 2 | package net.nashlegend.legendexplorer.adapter; 3 | 4 | import java.io.File; 5 | import java.util.ArrayList; 6 | 7 | import net.nashlegend.legendexplorer.view.ZoomImageView; 8 | 9 | import net.nashlegend.legendutils.Tools.FileUtil; 10 | 11 | import android.content.Context; 12 | import android.support.v4.view.PagerAdapter; 13 | import android.view.View; 14 | import android.view.ViewGroup; 15 | 16 | public class ImagePagerAdapter extends PagerAdapter { 17 | String mPath; 18 | Context mContext; 19 | ArrayList files = new ArrayList(); 20 | private int imageIndex; 21 | 22 | public ImagePagerAdapter(Context context, String path) { 23 | mPath = path; 24 | mContext = context; 25 | 26 | File FirstImage = new File(path); 27 | 28 | if (isImageFile(FirstImage)) { 29 | File parentFile = FirstImage.getParentFile(); 30 | File[] listFiles = parentFile.listFiles(); 31 | if (listFiles != null) { 32 | for (int i = 0; i < parentFile.listFiles().length; i++) { 33 | File file = listFiles[i]; 34 | if (isImageFile(file)) { 35 | files.add(file); 36 | if (file.equals(FirstImage)) { 37 | imageIndex = files.size() - 1; 38 | } 39 | } 40 | } 41 | } 42 | } 43 | } 44 | 45 | public int getImageIndex() { 46 | return imageIndex; 47 | } 48 | 49 | public boolean isImageFile(File file) { 50 | return FileUtil.getFileType(file) == FileUtil.FILE_TYPE_IMAGE; 51 | } 52 | 53 | @Override 54 | public int getCount() { 55 | // TODO Auto-generated method stub 56 | return files.size(); 57 | } 58 | 59 | @Override 60 | public Object instantiateItem(ViewGroup container, int position) { 61 | // TODO 62 | ZoomImageView imageView = new ZoomImageView(mContext); 63 | imageView.setImageFile(files.get(position).getAbsolutePath()); 64 | container.addView(imageView); 65 | return imageView; 66 | } 67 | 68 | @Override 69 | public void destroyItem(ViewGroup container, int position, Object object) { 70 | try { 71 | container.removeView((View) object); 72 | } catch (Exception e) { 73 | 74 | } 75 | } 76 | 77 | @Override 78 | public boolean isViewFromObject(View arg0, Object arg1) { 79 | return arg0 == arg1; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/application/ExplorerApplication.java: -------------------------------------------------------------------------------- 1 | package net.nashlegend.legendexplorer.application; 2 | 3 | import net.nashlegend.legendexplorer.consts.FileConst; 4 | import net.nashlegend.legendexplorer.db.BookmarkHelper; 5 | import net.nashlegend.legendexplorer.utils.SharePreferencesUtil; 6 | 7 | 8 | import android.app.Application; 9 | import android.content.Context; 10 | import android.media.MediaScannerConnection; 11 | import android.os.Environment; 12 | import android.widget.ImageView; 13 | 14 | public class ExplorerApplication extends Application { 15 | 16 | public static Context GlobalContext; 17 | 18 | public ExplorerApplication() { 19 | 20 | } 21 | 22 | @Override 23 | public void onCreate() { 24 | super.onCreate(); 25 | GlobalContext = this; 26 | // MediaScannerConnection.scanFile(getApplicationContext(), 27 | // new String[] { Environment.getExternalStorageDirectory() 28 | // .getAbsolutePath() }, null, null); 29 | if (SharePreferencesUtil.readBoolean(FileConst.Key_Is_First_Open, true)) { 30 | SharePreferencesUtil 31 | .saveBoolean(FileConst.Key_Is_First_Open, false); 32 | BookmarkHelper helper = new BookmarkHelper(this); 33 | helper.initBookmarks(); 34 | } 35 | } 36 | 37 | public static Context getGlobalContext() { 38 | return GlobalContext; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/consts/FileConst.java: -------------------------------------------------------------------------------- 1 | package net.nashlegend.legendexplorer.consts; 2 | 3 | public class FileConst { 4 | 5 | // Actions 6 | public static final String Action_FileItem_Long_Click = "legendexplorer.fileitemlongclick"; 7 | public static final String Action_FileItem_Unselect = "legendexplorer.fileitemunselect"; 8 | public static final String Action_FileItem_Select = "legendexplorer.fileitemselect"; 9 | public static final String Action_Refresh_FileList = "legendexplorer.refreshfilelist"; 10 | public static final String Action_Toggle_View_Mode = "legendexplorer.toggleviewmode"; 11 | public static final String Action_Property_File = "legendexplorer.propertyfile"; 12 | public static final String Action_Add_New_File = "legendexplorer.addnewfile"; 13 | public static final String Action_Search_File = "legendexplorer.searchfile"; 14 | public static final String Action_Quit_Search = "legendexplorer.quitsearch"; 15 | public static final String Action_Open_Folder = "legendexplorer.openfolder"; 16 | public static final String Action_Copy_File = "legendexplorer.copyfile"; 17 | public static final String Action_Move_File = "legendexplorer.movefile"; 18 | public static final String Action_Zip_File = "legendexplorer.zipfile"; 19 | public static final String Action_Unzip_File = "legendexplorer.unzipfile"; 20 | public static final String Action_Favor_File = "legendexplorer.favorfile"; 21 | public static final String Action_Rename_File = "legendexplorer.renamefile"; 22 | public static final String Action_Delete_File = "legendexplorer.deletefile"; 23 | public static final String Action_Toggle_Show_Hidden = "legendexplorer.toggleshowhidden"; 24 | public static final String Action_File_Operation_Done = "legendexplorer.fileoperationdone"; 25 | public static final String Action_Enable_Pager_Scroll = "legendexplorer.enablepagerscroll"; 26 | public static final String Action_Disable_Pager_Scroll = "legendexplorer.disablepagerscroll"; 27 | public static final String Action_Set_File_View_ActionBar = "legendexplorer.setfileviewactionbar"; 28 | public static final String Action_Set_File_Operation_ActionBar = "legendexplorer.setfileoperationactionbar"; 29 | 30 | // Extras 31 | public static final String Extra_File_Path = "filepath"; 32 | public static final String Extra_Item_Type = "itemtype"; 33 | public static final String Extra_Explore_Type = "exploretype"; 34 | public static final String Extra_Category_Type = "categorytype"; 35 | public static final String Extra_Path_Preffix = "pathpreffix"; 36 | public static final String Extra_Menu_Mask = "menumask"; 37 | 38 | // Keys 39 | public static final String Key_Is_First_Open = "isfirstopen"; 40 | public static final String Key_Files_Display_Mode = "filesdisplaymode"; 41 | public static final String Key_Show_Hiddle_Files = "showhiddenfiles"; 42 | public static final String Key_Search_File_Query = "searchfilequery"; 43 | public static final String Key_Show_More_On_Icon = "showmoreonicon"; 44 | 45 | // Values 46 | public static final String Value_Bookmark_Path = "Bookmark://"; 47 | public static final String Value_File_Path_Never_Existed = "/filepathneverexisted/"; 48 | public static final int Value_Files_Display_List = 0; 49 | public static final int Value_Files_Display_Grid = 1; 50 | 51 | public static final int Value_Explore_Type_Files = 0; 52 | public static final int Value_Explore_Type_Bookmarks = 1; 53 | public static final int Value_Explore_Type_Categories = 2; 54 | 55 | public static final int Value_Item_Type_File_Or_Folder = 0; 56 | public static final int Value_Item_Type_Bookmark = 1; 57 | public static final int Value_Item_Type_Category = 2; 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/db/BookmarkColumn.java: -------------------------------------------------------------------------------- 1 | 2 | package net.nashlegend.legendexplorer.db; 3 | 4 | import android.provider.BaseColumns; 5 | 6 | public class BookmarkColumn implements BaseColumns { 7 | 8 | public BookmarkColumn() { 9 | 10 | } 11 | 12 | // 列名 13 | public static final String FILE_NAME = "FILE_NAME"; // 文件名 14 | public static final String FILE_PATH = "FILE_PATH"; // 文件路径 15 | 16 | // 索引值 17 | public static final int FILE_NAME_COLUMN = 0; 18 | public static final int FILE_PATH_COLUMN = FILE_NAME_COLUMN + 1; 19 | // 查询结果集 20 | public static final String[] PROJECTION = 21 | { 22 | FILE_NAME, 23 | FILE_PATH 24 | }; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/db/BookmarkHelper.java: -------------------------------------------------------------------------------- 1 | package net.nashlegend.legendexplorer.db; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | 6 | import net.nashlegend.legendexplorer.consts.FileConst; 7 | import net.nashlegend.legendexplorer.model.FileItem; 8 | 9 | 10 | import android.content.ContentValues; 11 | import android.content.Context; 12 | import android.database.Cursor; 13 | import android.database.sqlite.SQLiteDatabase; 14 | import android.os.Environment; 15 | import android.util.Log; 16 | 17 | public class BookmarkHelper { 18 | 19 | private final Context mContext; 20 | private FileDataBase mDbHelper; 21 | private SQLiteDatabase mSqlDB; 22 | 23 | public BookmarkHelper(Context context) { 24 | this.mContext = context; 25 | } 26 | 27 | public BookmarkHelper open() { 28 | mDbHelper = FileDataBase.getInstance(mContext); 29 | mSqlDB = mDbHelper.getWritableDatabase(); 30 | return this; 31 | } 32 | 33 | public void close() { 34 | if (mDbHelper != null) { 35 | mDbHelper.close(); 36 | } 37 | } 38 | 39 | public boolean deleteBookmark(ArrayList list) { 40 | try { 41 | mSqlDB.beginTransaction(); 42 | for (File item : list) { 43 | deleteBookmark(item); 44 | } 45 | mSqlDB.setTransactionSuccessful(); 46 | } finally { 47 | mSqlDB.endTransaction(); 48 | } 49 | return true; 50 | } 51 | 52 | public boolean deleteBookmark(File item) { 53 | String whereClause = BookmarkColumn.FILE_PATH + "='" 54 | + item.getAbsolutePath() + "'"; 55 | Log.i("where", whereClause); 56 | int num = mSqlDB.delete(FileDataBase.TableBookmark, whereClause, null); 57 | return num > 0; 58 | } 59 | 60 | public boolean insertBookmark(ArrayList list) { 61 | try { 62 | mSqlDB.beginTransaction(); 63 | for (FileItem item : list) { 64 | insert(item); 65 | } 66 | mSqlDB.setTransactionSuccessful(); 67 | } finally { 68 | mSqlDB.endTransaction(); 69 | } 70 | return true; 71 | } 72 | 73 | public boolean insert(FileItem item) { 74 | long ret = -1; 75 | ContentValues values = new ContentValues(); 76 | values.put(BookmarkColumn.FILE_NAME, item.getName()); 77 | values.put(BookmarkColumn.FILE_PATH, item.getAbsolutePath()); 78 | ret = mSqlDB.insert(FileDataBase.TableBookmark, null, values); 79 | return ret != -1; 80 | } 81 | 82 | public ArrayList getBookmarks() { 83 | 84 | ArrayList list = new ArrayList(); 85 | 86 | Cursor cursor = mSqlDB.query(FileDataBase.TableBookmark, 87 | BookmarkColumn.PROJECTION, null, null, null, null, null, 88 | String.valueOf(10)); 89 | 90 | if (cursor.getCount() > 0) { 91 | for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor 92 | .moveToNext()) { 93 | String filePath = cursor 94 | .getString(BookmarkColumn.FILE_PATH_COLUMN); 95 | FileItem item = new FileItem(filePath); 96 | item.setItemType(FileConst.Value_Item_Type_Bookmark); 97 | list.add(item); 98 | } 99 | } 100 | 101 | cursor.close(); 102 | cursor = null; 103 | 104 | return list; 105 | 106 | } 107 | 108 | public boolean truncate() { 109 | int ret = mSqlDB.delete(FileDataBase.TableBookmark, null, null); 110 | return ret > 0 ? true : false; 111 | } 112 | 113 | public void initBookmarks() { 114 | // 当文件不存在时isDirectory和isFile都返回false 115 | ArrayList fileItems = new ArrayList(); 116 | FileItem item_download = new FileItem( 117 | Environment 118 | .getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)); 119 | if (item_download.exists() && item_download.isDirectory()) { 120 | fileItems.add(item_download); 121 | } 122 | FileItem item_camera = new FileItem( 123 | Environment 124 | .getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)); 125 | if (item_camera.exists() && item_camera.isDirectory()) { 126 | fileItems.add(item_camera); 127 | } 128 | FileItem item_movie = new FileItem( 129 | Environment 130 | .getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES)); 131 | if (item_movie.exists() && item_movie.isDirectory()) { 132 | fileItems.add(item_movie); 133 | } 134 | FileItem item_music = new FileItem( 135 | Environment 136 | .getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC)); 137 | if (item_music.exists() && item_music.isDirectory()) { 138 | fileItems.add(item_music); 139 | } 140 | FileItem item_picture = new FileItem( 141 | Environment 142 | .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)); 143 | if (item_picture.exists() && item_picture.isDirectory()) { 144 | fileItems.add(item_picture); 145 | } 146 | 147 | open(); 148 | truncate(); 149 | insertBookmark(fileItems); 150 | close(); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/db/FileDataBase.java: -------------------------------------------------------------------------------- 1 | 2 | package net.nashlegend.legendexplorer.db; 3 | 4 | import android.content.Context; 5 | import android.database.SQLException; 6 | import android.database.sqlite.SQLiteDatabase; 7 | import android.database.sqlite.SQLiteOpenHelper; 8 | 9 | public class FileDataBase extends SQLiteOpenHelper { 10 | 11 | public static final String DatabaseName = "explorer.db"; 12 | public static final String TableBookmark = "bookmark"; 13 | public static final int DBVersion = 1; 14 | public static FileDataBase mFileDataBase = null; 15 | 16 | public FileDataBase(Context context) { 17 | super(context, DatabaseName, null, DBVersion); 18 | } 19 | 20 | public static FileDataBase getInstance(Context context) { 21 | if (mFileDataBase == null) { 22 | mFileDataBase = new FileDataBase(context); 23 | } 24 | return mFileDataBase; 25 | } 26 | 27 | @Override 28 | public void onCreate(SQLiteDatabase db) { 29 | createBookmarkDB(db); 30 | } 31 | 32 | private void createBookmarkDB(SQLiteDatabase db) { 33 | String section_sql = "create table IF NOT EXISTS " + TableBookmark + "(" 34 | + BookmarkColumn._ID + " integer primary key AUTOINCREMENT," 35 | + BookmarkColumn.FILE_NAME + " varchar default ''," 36 | + BookmarkColumn.FILE_PATH + " varchar default '' )"; 37 | try { 38 | db.execSQL(section_sql); 39 | } catch (SQLException e) { 40 | } finally { 41 | } 42 | } 43 | 44 | @Override 45 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 46 | 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/dialog/FilePropertyDialog.java: -------------------------------------------------------------------------------- 1 | package net.nashlegend.legendexplorer.dialog; 2 | 3 | import java.io.File; 4 | 5 | import net.nashlegend.legendexplorer.view.FilePropertyView; 6 | 7 | import net.nashlegend.legendutils.BuildIn.FileDialogView; 8 | 9 | import android.app.Dialog; 10 | import android.content.Context; 11 | import android.content.DialogInterface; 12 | import android.view.ViewGroup.LayoutParams; 13 | 14 | public class FilePropertyDialog extends Dialog { 15 | 16 | public FilePropertyDialog(Context context) { 17 | super(context); 18 | } 19 | 20 | public FilePropertyDialog(Context context, int theme) { 21 | super(context, theme); 22 | } 23 | 24 | public FilePropertyDialog(Context context, boolean cancelable, 25 | OnCancelListener cancelListener) { 26 | super(context, cancelable, cancelListener); 27 | } 28 | 29 | public static class Builder { 30 | private boolean allInOneFolder = true; 31 | private String title = "属性"; 32 | private File[] files; 33 | private Context mContext; 34 | 35 | public Builder(Context context) { 36 | mContext = context; 37 | } 38 | 39 | public Builder setAllInOneFolder(boolean in) { 40 | allInOneFolder = in; 41 | return this; 42 | } 43 | 44 | public Builder setTitle(String tit) { 45 | title = tit; 46 | return this; 47 | } 48 | 49 | public Builder setFiles(File[] fs) { 50 | files = fs; 51 | return this; 52 | } 53 | 54 | public FilePropertyDialog create() { 55 | FilePropertyDialog dialog = new FilePropertyDialog(mContext); 56 | final FilePropertyView view = new FilePropertyView(mContext); 57 | view.setFiles(files, allInOneFolder); 58 | 59 | LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, 60 | LayoutParams.WRAP_CONTENT); 61 | dialog.setTitle(title); 62 | dialog.addContentView(view, params); 63 | dialog.setOnCancelListener(new OnCancelListener() { 64 | 65 | @Override 66 | public void onCancel(DialogInterface dialog) { 67 | view.cancel(); 68 | } 69 | }); 70 | return dialog; 71 | } 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/fragment/BaseFragment.java: -------------------------------------------------------------------------------- 1 | 2 | package net.nashlegend.legendexplorer.fragment; 3 | 4 | import android.app.Fragment; 5 | import android.content.Intent; 6 | 7 | public abstract class BaseFragment extends Fragment { 8 | 9 | public BaseFragment() { 10 | 11 | } 12 | 13 | public abstract boolean doBackAction(); 14 | 15 | public abstract boolean doVeryAction(Intent intent); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/fragment/BookMarksFragment.java: -------------------------------------------------------------------------------- 1 | package net.nashlegend.legendexplorer.fragment; 2 | 3 | import java.io.File; 4 | 5 | import net.nashlegend.legendexplorer.MainActivity; 6 | import net.nashlegend.legendexplorer.consts.FileConst; 7 | import net.nashlegend.legendexplorer.model.FileItem; 8 | import net.nashlegend.legendexplorer.view.DropDownAncestorList; 9 | import net.nashlegend.legendexplorer.view.DropDownAncestorList.OnAncestorClickListener; 10 | 11 | import net.nashlegend.legendexplorer.R; 12 | 13 | import android.app.FragmentTransaction; 14 | import android.content.Intent; 15 | import android.graphics.Bitmap; 16 | import android.graphics.drawable.BitmapDrawable; 17 | import android.os.Bundle; 18 | import android.util.Log; 19 | import android.view.LayoutInflater; 20 | import android.view.View; 21 | import android.view.ViewGroup; 22 | import android.view.ViewGroup.LayoutParams; 23 | import android.widget.CheckBox; 24 | import android.widget.EditText; 25 | import android.widget.ImageButton; 26 | import android.widget.PopupWindow; 27 | 28 | /** 29 | * 书签视图 30 | * 31 | * @author NashLegend 32 | */ 33 | public class BookMarksFragment extends FilesFragment { 34 | private String pathPreffix = FileConst.Value_File_Path_Never_Existed; 35 | 36 | public BookMarksFragment() { 37 | 38 | } 39 | 40 | @Override 41 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 42 | Bundle savedInstanceState) { 43 | if (view == null) { 44 | view = inflater.inflate(R.layout.layout_file_bookmark, container, 45 | false); 46 | pathText = (EditText) view.findViewById(R.id.edittext_file_path); 47 | 48 | backButton = (ImageButton) view 49 | .findViewById(R.id.imagebutton_file_back); 50 | selectAllButton = (CheckBox) view 51 | .findViewById(R.id.checkbox_file_all); 52 | backButton.setOnClickListener(this); 53 | selectAllButton.setOnCheckedChangeListener(this); 54 | selectAllButton.setVisibility(View.GONE); 55 | pathText.setKeyListener(null); 56 | pathText.setOnClickListener(this); 57 | 58 | ancestorList = new DropDownAncestorList(getActivity()); 59 | ancestorList 60 | .setOnAncestorClickListener(new OnAncestorClickListener() { 61 | 62 | @Override 63 | public void onClick(String path) { 64 | openAncestorFolder(new File(path)); 65 | popupWindow.dismiss(); 66 | } 67 | }); 68 | 69 | popupWindow = new PopupWindow(ancestorList, 70 | LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, true); 71 | popupWindow.setTouchable(true); 72 | popupWindow.setOutsideTouchable(true); 73 | popupWindow.setBackgroundDrawable(new BitmapDrawable( 74 | getResources(), (Bitmap) null)); 75 | 76 | openBookMarks(); 77 | } else { 78 | if (view.getParent() != null) { 79 | ((ViewGroup) view.getParent()).removeView(view); 80 | } 81 | } 82 | 83 | return view; 84 | } 85 | 86 | @Override 87 | protected void change2SelectMode() { 88 | if (fakeBackStack.size() > 0) { 89 | selectAllButton.setVisibility(View.VISIBLE); 90 | inSelectMode = true; 91 | fakeBackStack.get(fakeBackStack.size() - 1).change2SelectMode(); 92 | 93 | Intent intent = new Intent(); 94 | int mask = 0; 95 | if (fakeBackStack.size() > 0) { 96 | FileListFragment fragment = fakeBackStack.get(fakeBackStack 97 | .size() - 1); 98 | if (fragment.getFilePath() 99 | .equals(FileConst.Value_Bookmark_Path)) { 100 | mask = MainActivity.FlagFavorItem 101 | | MainActivity.FlagUnzipFileItem 102 | | MainActivity.FlagZipFileItem 103 | | MainActivity.FlagRenameFileItem 104 | | MainActivity.FlagCutFileItem; 105 | } else { 106 | mask = 0; 107 | } 108 | } 109 | intent.putExtra(FileConst.Extra_Menu_Mask, mask); 110 | intent.setAction(FileConst.Action_Set_File_Operation_ActionBar); 111 | getActivity().sendBroadcast(intent); 112 | } 113 | } 114 | 115 | @Override 116 | protected void exitSelectMode() { 117 | if (fakeBackStack.size() > 0) { 118 | selectAllButton.setVisibility(View.GONE); 119 | selectAllButton.setChecked(false); 120 | inSelectMode = false; 121 | fakeBackStack.get(fakeBackStack.size() - 1).exitSelectMode(); 122 | 123 | Intent intent = new Intent(); 124 | int mask = 0; 125 | // 事实上fakeBackStack.size==1的时候就足以判断了 126 | if (fakeBackStack.size() > 0) { 127 | FileListFragment fragment = fakeBackStack.get(fakeBackStack 128 | .size() - 1); 129 | if (fragment.getFilePath() 130 | .equals(FileConst.Value_Bookmark_Path)) { 131 | mask = MainActivity.FlagAddFileItem; 132 | } else { 133 | mask = 0; 134 | } 135 | } 136 | intent.putExtra(FileConst.Extra_Menu_Mask, mask); 137 | intent.setAction(FileConst.Action_Set_File_View_ActionBar); 138 | getActivity().sendBroadcast(intent); 139 | } 140 | } 141 | 142 | /* 143 | * @see 144 | * net.nashlegend.legendexplorer.fragment.FilesFragment#openFolder(java.io. 145 | * File, int) 146 | */ 147 | public void openFolder(File file, int animation) { 148 | 149 | if (file == null || !file.isDirectory()) { 150 | throw new NullPointerException( 151 | "openFolder://file.exists() && file.isDirectory()"); 152 | } 153 | 154 | if (file.equals(new File(pathPreffix))) { 155 | openBookMarks(); 156 | return; 157 | } 158 | 159 | FileListFragment fragment = new FileListFragment(); 160 | Bundle bundle = new Bundle(); 161 | bundle.putString(FileConst.Extra_File_Path, file.getAbsolutePath()); 162 | bundle.putString(FileConst.Extra_Path_Preffix, pathPreffix); 163 | bundle.putInt(FileConst.Extra_Item_Type, 164 | FileConst.Value_Item_Type_File_Or_Folder); 165 | bundle.putInt(FileConst.Extra_Explore_Type, 166 | FileConst.Value_Explore_Type_Bookmarks); 167 | fragment.setArguments(bundle); 168 | 169 | FragmentTransaction transaction = getFragmentManager() 170 | .beginTransaction(); 171 | transaction.setTransition(animation); 172 | transaction.replace(R.id.content_bookmark, fragment, 173 | file.getAbsolutePath()); 174 | transaction.commit(); 175 | fakeBackStack.add(fragment); 176 | pathText.setText(fragment.getDisplayedFilePath()); 177 | 178 | Intent intent = new Intent(); 179 | int mask = 0; 180 | intent.putExtra(FileConst.Extra_Menu_Mask, mask); 181 | intent.setAction(FileConst.Action_Set_File_View_ActionBar); 182 | getActivity().sendBroadcast(intent); 183 | } 184 | 185 | /** 186 | * 打开目录 187 | * 188 | * @param file 189 | * 要打开的文件夹 190 | */ 191 | public void openFolder(File file) { 192 | openFolder(file, FragmentTransaction.TRANSIT_FRAGMENT_OPEN); 193 | } 194 | 195 | /** 196 | * 打开收藏夹目录 197 | */ 198 | public void openBookMarks() { 199 | pathPreffix = FileConst.Value_File_Path_Never_Existed; 200 | fakeBackStack.clear(); 201 | FileListFragment fragment = new FileListFragment(); 202 | Bundle bundle = new Bundle(); 203 | bundle.putString(FileConst.Extra_File_Path, 204 | FileConst.Value_Bookmark_Path); 205 | bundle.putString(FileConst.Extra_Path_Preffix, pathPreffix); 206 | bundle.putInt(FileConst.Extra_Item_Type, 207 | FileConst.Value_Item_Type_Bookmark); 208 | bundle.putInt(FileConst.Extra_Explore_Type, 209 | FileConst.Value_Explore_Type_Bookmarks); 210 | fragment.setArguments(bundle); 211 | FragmentTransaction transaction = getFragmentManager() 212 | .beginTransaction(); 213 | transaction.replace(R.id.content_bookmark, fragment, 214 | FileConst.Value_Bookmark_Path); 215 | transaction.commit(); 216 | fakeBackStack.add(fragment); 217 | pathText.setText(fragment.getDisplayedFilePath()); 218 | 219 | Intent intent = new Intent(); 220 | int mask = MainActivity.FlagAddFileItem; 221 | intent.putExtra(FileConst.Extra_Menu_Mask, mask); 222 | intent.setAction(FileConst.Action_Set_File_View_ActionBar); 223 | getActivity().sendBroadcast(intent); 224 | } 225 | 226 | /** 227 | * 回退 228 | */ 229 | protected void backStack() { 230 | if (fakeBackStack.size() > 1) { 231 | fakeBackStack.remove(fakeBackStack.size() - 1); 232 | FileListFragment fragment = fakeBackStack 233 | .get(fakeBackStack.size() - 1); 234 | FragmentTransaction transaction = getFragmentManager() 235 | .beginTransaction(); 236 | transaction 237 | .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE); 238 | transaction.replace(R.id.content_bookmark, fragment, 239 | fragment.getDisplayedFilePath()); 240 | transaction.commit(); 241 | pathText.setText(fragment.getDisplayedFilePath()); 242 | 243 | Intent intent = new Intent(); 244 | int mask = 0; 245 | if (fragment.getFilePath().equals(FileConst.Value_Bookmark_Path)) { 246 | mask = MainActivity.FlagAddFileItem; 247 | } else { 248 | mask = 0; 249 | } 250 | intent.putExtra(FileConst.Extra_Menu_Mask, mask); 251 | intent.setAction(FileConst.Action_Set_File_View_ActionBar); 252 | getActivity().sendBroadcast(intent); 253 | 254 | } else { 255 | // do nothing 256 | } 257 | } 258 | 259 | @Override 260 | protected void back2ParentLevel() { 261 | backStack(); 262 | } 263 | 264 | @Override 265 | protected void invokeAncestorList() { 266 | String path = fakeBackStack.get(fakeBackStack.size() - 1).getFilePath(); 267 | if (FileConst.Value_Bookmark_Path.equals(path)) { 268 | return; 269 | } 270 | File file = new File(path); 271 | ancestorList 272 | .setupList(file, pathPreffix, FileConst.Value_Bookmark_Path); 273 | popupWindow.showAsDropDown(pathText); 274 | } 275 | 276 | @Override 277 | protected void doOpenFolderAction(Intent intent) { 278 | int tp = intent.getIntExtra(FileConst.Extra_Item_Type, 279 | FileConst.Value_Item_Type_File_Or_Folder); 280 | String path = intent.getStringExtra(FileConst.Extra_File_Path); 281 | File file = new File(path); 282 | if (file.getParentFile() != null) { 283 | if (tp == FileConst.Value_Item_Type_Bookmark) { 284 | if (pathPreffix == FileConst.Value_File_Path_Never_Existed) { 285 | pathPreffix = new File(path).getParent(); 286 | if (pathPreffix.lastIndexOf("/") != pathPreffix.length() - 1) { 287 | pathPreffix += "/"; 288 | } 289 | } 290 | } 291 | } 292 | openFolder(file); 293 | } 294 | 295 | @Override 296 | public void setUserVisibleHint(boolean isVisibleToUser) { 297 | super.setUserVisibleHint(isVisibleToUser); 298 | if (isVisibleToUser) { 299 | Intent intent = new Intent(); 300 | int mask = MainActivity.FlagAddFileItem; 301 | if (fakeBackStack.size() > 0) { 302 | FileListFragment fragment = fakeBackStack.get(fakeBackStack 303 | .size() - 1); 304 | if (fragment.getFilePath() 305 | .equals(FileConst.Value_Bookmark_Path)) { 306 | mask = MainActivity.FlagAddFileItem; 307 | } else { 308 | mask = 0; 309 | } 310 | } 311 | intent.putExtra(FileConst.Extra_Menu_Mask, mask); 312 | intent.setAction(FileConst.Action_Set_File_View_ActionBar); 313 | getActivity().sendBroadcast(intent); 314 | } 315 | } 316 | 317 | } 318 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/fragment/Explorable.java: -------------------------------------------------------------------------------- 1 | package net.nashlegend.legendexplorer.fragment; 2 | 3 | public interface Explorable { 4 | public void toggleViewMode(); 5 | 6 | public void copyFile(); 7 | 8 | public void moveFile(); 9 | 10 | public void deleteFile(); 11 | 12 | public void addNewFile(); 13 | 14 | public void refreshFileList(); 15 | 16 | public void searchFile(String query); 17 | 18 | public void toggleShowHidden(); 19 | 20 | public void renameFile(); 21 | 22 | public void zipFile(); 23 | 24 | public void propertyFile(); 25 | } 26 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/fragment/FilesFragment.java: -------------------------------------------------------------------------------- 1 | 2 | package net.nashlegend.legendexplorer.fragment; 3 | 4 | import java.io.File; 5 | import java.util.ArrayList; 6 | import java.util.Iterator; 7 | 8 | import net.nashlegend.legendexplorer.MainActivity; 9 | import net.nashlegend.legendexplorer.consts.FileConst; 10 | import net.nashlegend.legendexplorer.view.DropDownAncestorList; 11 | import net.nashlegend.legendexplorer.view.DropDownAncestorList.OnAncestorClickListener; 12 | 13 | import net.nashlegend.legendexplorer.R; 14 | import net.nashlegend.legendutils.Tools.FileUtil; 15 | 16 | import android.app.FragmentTransaction; 17 | import android.content.Intent; 18 | import android.graphics.Bitmap; 19 | import android.graphics.drawable.BitmapDrawable; 20 | import android.os.Bundle; 21 | import android.os.Environment; 22 | import android.util.Log; 23 | import android.view.Gravity; 24 | import android.view.LayoutInflater; 25 | import android.view.View; 26 | import android.view.ViewGroup; 27 | import android.view.View.OnClickListener; 28 | import android.view.ViewGroup.LayoutParams; 29 | import android.widget.CheckBox; 30 | import android.widget.CompoundButton; 31 | import android.widget.EditText; 32 | import android.widget.ImageButton; 33 | import android.widget.CompoundButton.OnCheckedChangeListener; 34 | import android.widget.PopupWindow; 35 | 36 | /** 37 | * 普通视图,事实上if (fakeBackStack.size() > 0) 都是不需要的 38 | * 39 | * @author NashLegend 40 | */ 41 | public class FilesFragment extends BaseFragment implements OnClickListener, 42 | OnCheckedChangeListener, Explorable { 43 | protected View view; 44 | protected EditText pathText; 45 | protected ImageButton backButton; 46 | protected CheckBox selectAllButton; 47 | protected boolean inSelectMode = false; 48 | protected DropDownAncestorList ancestorList; 49 | protected ArrayList fakeBackStack = new ArrayList(); 50 | protected String initialPath = Environment.getExternalStorageDirectory() 51 | .getPath(); 52 | protected PopupWindow popupWindow; 53 | 54 | public FilesFragment() { 55 | 56 | } 57 | 58 | @Override 59 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 60 | Bundle savedInstanceState) { 61 | if (view == null) { 62 | view = inflater.inflate(R.layout.layout_file_explorer, container, 63 | false); 64 | pathText = (EditText) view.findViewById(R.id.edittext_file_path); 65 | backButton = (ImageButton) view 66 | .findViewById(R.id.imagebutton_file_back); 67 | selectAllButton = (CheckBox) view 68 | .findViewById(R.id.checkbox_file_all); 69 | 70 | backButton.setOnClickListener(this); 71 | selectAllButton.setOnCheckedChangeListener(this); 72 | selectAllButton.setVisibility(View.GONE); 73 | pathText.setKeyListener(null); 74 | pathText.setOnClickListener(this); 75 | 76 | ancestorList = new DropDownAncestorList(getActivity()); 77 | ancestorList 78 | .setOnAncestorClickListener(new OnAncestorClickListener() { 79 | 80 | @Override 81 | public void onClick(String path) { 82 | openAncestorFolder(new File(path)); 83 | popupWindow.dismiss(); 84 | } 85 | }); 86 | 87 | popupWindow = new PopupWindow(ancestorList, 88 | LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, true); 89 | popupWindow.setTouchable(true); 90 | popupWindow.setOutsideTouchable(true); 91 | popupWindow.setBackgroundDrawable(new BitmapDrawable( 92 | getResources(), (Bitmap) null)); 93 | 94 | openFolder(); 95 | } else { 96 | if (view.getParent() != null) { 97 | ((ViewGroup) view.getParent()).removeView(view); 98 | } 99 | } 100 | 101 | return view; 102 | } 103 | 104 | protected void invokeAncestorList() { 105 | String path = pathText.getText().toString(); 106 | if ("/".equals(path)) { 107 | return; 108 | } else { 109 | File file = new File(path); 110 | ancestorList.setupList(file); 111 | } 112 | popupWindow.showAsDropDown(pathText); 113 | } 114 | 115 | protected void openAncestorFolder(File file) { 116 | // file.isDirectory()为true则说明文件存在 117 | if (file != null && file.isDirectory()) { 118 | for (int i = fakeBackStack.size() - 1; i >= 0; i--) { 119 | FileListFragment subFragment = fakeBackStack.get(i); 120 | File file2 = new File(subFragment.getFilePath()); 121 | if (FileUtil.isAncestorOf(file, file2) || file.equals(file2)) { 122 | fakeBackStack.remove(i); 123 | } 124 | } 125 | } 126 | 127 | openFolder(file, FragmentTransaction.TRANSIT_FRAGMENT_CLOSE); 128 | } 129 | 130 | /** 131 | * @param file 132 | * @param animation 133 | */ 134 | public void openFolder(File file, int animation) { 135 | // file.isDirectory()不为true则说明文件要么不存在要么是文件 136 | if (file == null || !file.isDirectory()) { 137 | // 若不存在此目录,则打开根文件夹 138 | return; 139 | } 140 | 141 | FileListFragment fragment = new FileListFragment(); 142 | Bundle bundle = new Bundle(); 143 | bundle.putString(FileConst.Extra_File_Path, file.getAbsolutePath()); 144 | fragment.setArguments(bundle); 145 | 146 | FragmentTransaction transaction = getFragmentManager() 147 | .beginTransaction(); 148 | transaction.setTransition(animation); 149 | transaction.replace(R.id.content_explorer, fragment, 150 | file.getAbsolutePath()); 151 | transaction.commit(); 152 | fakeBackStack.add(fragment); 153 | pathText.setText(fragment.getDisplayedFilePath()); 154 | } 155 | 156 | /** 157 | * 打开目录 158 | * 159 | * @param file 要打开的文件夹 160 | */ 161 | public void openFolder(File file) { 162 | openFolder(file, FragmentTransaction.TRANSIT_FRAGMENT_OPEN); 163 | } 164 | 165 | /** 166 | * 打开目录 167 | * 168 | * @param path 要打开的文件夹路径 169 | */ 170 | public void openFolder(String path) { 171 | openFolder(new File(path)); 172 | } 173 | 174 | /** 175 | * 打开初始目录 176 | */ 177 | public void openFolder() { 178 | openFolder(initialPath); 179 | } 180 | 181 | /** 182 | * 返回一层fragment,不一定是上一级目录 183 | */ 184 | protected void backStack() { 185 | if (fakeBackStack.size() > 1) { 186 | fakeBackStack.remove(fakeBackStack.size() - 1); 187 | FileListFragment fragment = fakeBackStack 188 | .get(fakeBackStack.size() - 1); 189 | FragmentTransaction transaction = getFragmentManager() 190 | .beginTransaction(); 191 | transaction 192 | .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE); 193 | transaction.replace(R.id.content_explorer, fragment, 194 | fragment.getDisplayedFilePath()); 195 | transaction.commit(); 196 | pathText.setText(fragment.getDisplayedFilePath()); 197 | } else { 198 | // do nothing 199 | } 200 | } 201 | 202 | /** 203 | * 返回上一级目录 204 | */ 205 | protected void back2ParentLevel() { 206 | // 相当于BackStack,但是当pFile上sd卡的父级或以上时,相当于打开父文件夹并清除其子文件夹 207 | if (fakeBackStack.size() > 0) { 208 | FileListFragment fragment = fakeBackStack 209 | .get(fakeBackStack.size() - 1); 210 | File file = new File(fragment.getDisplayedFilePath()); 211 | File pFile = file.getParentFile(); 212 | for (int i = fakeBackStack.size() - 1; i >= 0; i--) { 213 | FileListFragment subFragment = fakeBackStack.get(i); 214 | File file2 = new File(subFragment.getFilePath()); 215 | if (FileUtil.isAncestorOf(pFile, file2) || pFile.equals(file2)) { 216 | fakeBackStack.remove(i); 217 | } 218 | } 219 | if (pFile != null && pFile.exists() && pFile.isDirectory()) { 220 | openFolder(pFile, FragmentTransaction.TRANSIT_FRAGMENT_CLOSE); 221 | } 222 | } 223 | } 224 | 225 | /** 226 | * 选中当前目录所有文件 227 | */ 228 | protected void selectAll() { 229 | if (fakeBackStack.size() > 0) { 230 | fakeBackStack.get(fakeBackStack.size() - 1).selectAll(); 231 | } 232 | } 233 | 234 | /** 235 | * 取消选中当前目录所有文件 236 | */ 237 | protected void unselectAll() { 238 | if (fakeBackStack.size() > 0) { 239 | fakeBackStack.get(fakeBackStack.size() - 1).unselectAll(); 240 | } 241 | } 242 | 243 | public void unselectCheckBox() { 244 | selectAllButton.setOnCheckedChangeListener(null); 245 | selectAllButton.setChecked(false); 246 | selectAllButton.setOnCheckedChangeListener(this); 247 | } 248 | 249 | /** 250 | * @return 返回选中的文件列表 251 | */ 252 | public File[] getSelectedFiles() { 253 | if (fakeBackStack.size() > 0) { 254 | return fakeBackStack.get(fakeBackStack.size() - 1) 255 | .getSelectedFiles(); 256 | } 257 | return null; 258 | } 259 | 260 | @Override 261 | public void onClick(View v) { 262 | if (inSelectMode) { 263 | exitSelectMode(); 264 | } 265 | int id = v.getId(); 266 | switch (id) { 267 | case R.id.imagebutton_file_back: 268 | back2ParentLevel(); 269 | break; 270 | case R.id.edittext_file_path: 271 | invokeAncestorList(); 272 | break; 273 | default: 274 | break; 275 | } 276 | } 277 | 278 | public EditText getPathText() { 279 | return pathText; 280 | } 281 | 282 | public String getInitialPath() { 283 | return initialPath; 284 | } 285 | 286 | public void setInitialPath(String initialPath) { 287 | this.initialPath = initialPath; 288 | } 289 | 290 | @Override 291 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 292 | if (selectAllButton.isChecked()) { 293 | selectAll(); 294 | } else { 295 | unselectAll(); 296 | } 297 | } 298 | 299 | public CheckBox getSelectAllButton() { 300 | return selectAllButton; 301 | } 302 | 303 | protected void change2SelectMode() { 304 | if (fakeBackStack.size() > 0) { 305 | selectAllButton.setVisibility(View.VISIBLE); 306 | inSelectMode = true; 307 | fakeBackStack.get(fakeBackStack.size() - 1).change2SelectMode(); 308 | 309 | Intent intent = new Intent(); 310 | intent.setAction(FileConst.Action_Set_File_Operation_ActionBar); 311 | getActivity().sendBroadcast(intent); 312 | } 313 | } 314 | 315 | protected void exitSelectMode() { 316 | if (fakeBackStack.size() > 0) { 317 | selectAllButton.setVisibility(View.GONE); 318 | selectAllButton.setChecked(false); 319 | inSelectMode = false; 320 | fakeBackStack.get(fakeBackStack.size() - 1).exitSelectMode(); 321 | 322 | Intent intent = new Intent(); 323 | intent.setAction(FileConst.Action_Set_File_View_ActionBar); 324 | getActivity().sendBroadcast(intent); 325 | } 326 | } 327 | 328 | protected void doOpenFolderAction(Intent intent) { 329 | openFolder(intent.getStringExtra(FileConst.Extra_File_Path)); 330 | } 331 | 332 | private boolean isInSearchingMode() { 333 | if (fakeBackStack.size() > 0) { 334 | return fakeBackStack.get(fakeBackStack.size() - 1) 335 | .isInSearchingMode(); 336 | } 337 | return false; 338 | } 339 | 340 | private void quitSearchFile() { 341 | Intent intent = new Intent(); 342 | intent.setAction(FileConst.Action_Quit_Search); 343 | getActivity().sendBroadcast(intent); 344 | } 345 | 346 | @Override 347 | public boolean doBackAction() { 348 | if (inSelectMode) { 349 | exitSelectMode(); 350 | return true; 351 | } 352 | if (isInSearchingMode()) { 353 | quitSearchFile(); 354 | return true; 355 | } 356 | if (fakeBackStack.size() > 1) { 357 | backStack(); 358 | return true; 359 | } 360 | return false; 361 | } 362 | 363 | @Override 364 | public boolean doVeryAction(Intent intent) { 365 | String action = intent.getAction(); 366 | if (FileConst.Action_Open_Folder.equals(action)) { 367 | doOpenFolderAction(intent); 368 | } else if (FileConst.Action_FileItem_Long_Click.equals(action)) { 369 | change2SelectMode(); 370 | getItemSelect(); 371 | } else if (FileConst.Action_FileItem_Unselect.equals(action)) { 372 | getItemUnselect(); 373 | } else if (FileConst.Action_FileItem_Select.equals(action)) { 374 | getItemSelect(); 375 | } else if (FileConst.Action_File_Operation_Done.equals(action)) { 376 | exitSelectMode(); 377 | } else if (FileConst.Action_Add_New_File.equals(action)) { 378 | addNewFile(); 379 | } else if (FileConst.Action_Search_File.equals(action)) { 380 | String query = intent 381 | .getStringExtra(FileConst.Key_Search_File_Query); 382 | searchFile(query); 383 | } else if (FileConst.Action_Quit_Search.equals(action)) { 384 | searchFile(""); 385 | } else if (FileConst.Action_Toggle_View_Mode.equals(action)) { 386 | toggleViewMode(); 387 | } else if (FileConst.Action_Refresh_FileList.equals(action)) { 388 | refreshFileList(); 389 | } else if (FileConst.Action_Copy_File.equals(action)) { 390 | copyFile(); 391 | } else if (FileConst.Action_Move_File.equals(action)) { 392 | moveFile(); 393 | } else if (FileConst.Action_Delete_File.equals(action)) { 394 | deleteFile(); 395 | } else if (FileConst.Action_Toggle_Show_Hidden.equals(action)) { 396 | toggleShowHidden(); 397 | } else if (FileConst.Action_Zip_File.equals(action)) { 398 | zipFile(); 399 | } else if (FileConst.Action_Rename_File.equals(action)) { 400 | renameFile(); 401 | } else if (FileConst.Action_Property_File.equals(action)) { 402 | propertyFile(); 403 | } else if (FileConst.Action_Unzip_File.equals(action)) { 404 | unzipFile(); 405 | } else if (FileConst.Action_Favor_File.equals(action)) { 406 | favorFile(); 407 | } 408 | return false; 409 | } 410 | 411 | public void getItemSelect() { 412 | if (fakeBackStack.size() > 0) { 413 | fakeBackStack.get(fakeBackStack.size() - 1).getItemSelect(); 414 | } 415 | } 416 | 417 | public void getItemUnselect() { 418 | 419 | selectAllButton.setOnCheckedChangeListener(null); 420 | selectAllButton.setChecked(false); 421 | selectAllButton.setOnCheckedChangeListener(this); 422 | 423 | if (fakeBackStack.size() > 0) { 424 | fakeBackStack.get(fakeBackStack.size() - 1).getItemUnselect(); 425 | } 426 | } 427 | 428 | @Override 429 | public void setUserVisibleHint(boolean isVisibleToUser) { 430 | super.setUserVisibleHint(isVisibleToUser); 431 | if (isVisibleToUser) { 432 | Intent intent = new Intent(); 433 | int mask = 0; 434 | intent.putExtra(FileConst.Extra_Menu_Mask, mask); 435 | intent.setAction(FileConst.Action_Set_File_View_ActionBar); 436 | getActivity().sendBroadcast(intent); 437 | } 438 | } 439 | 440 | @Override 441 | public void toggleViewMode() { 442 | if (fakeBackStack.size() > 0) { 443 | fakeBackStack.get(fakeBackStack.size() - 1).toggleViewMode(); 444 | } 445 | } 446 | 447 | @Override 448 | public void copyFile() { 449 | if (fakeBackStack.size() > 0) { 450 | fakeBackStack.get(fakeBackStack.size() - 1).copyFile(); 451 | } 452 | } 453 | 454 | @Override 455 | public void moveFile() { 456 | if (fakeBackStack.size() > 0) { 457 | fakeBackStack.get(fakeBackStack.size() - 1).moveFile(); 458 | } 459 | } 460 | 461 | @Override 462 | public void deleteFile() { 463 | if (fakeBackStack.size() > 0) { 464 | fakeBackStack.get(fakeBackStack.size() - 1).deleteFile(); 465 | } 466 | } 467 | 468 | @Override 469 | public void addNewFile() { 470 | if (fakeBackStack.size() > 0) { 471 | fakeBackStack.get(fakeBackStack.size() - 1).addNewFile(); 472 | } 473 | } 474 | 475 | @Override 476 | public void refreshFileList() { 477 | if (fakeBackStack.size() > 0) { 478 | fakeBackStack.get(fakeBackStack.size() - 1).refreshFileList(); 479 | } 480 | } 481 | 482 | @Override 483 | public void searchFile(String query) { 484 | if (fakeBackStack.size() > 0) { 485 | fakeBackStack.get(fakeBackStack.size() - 1).searchFile(query); 486 | } 487 | } 488 | 489 | @Override 490 | public void toggleShowHidden() { 491 | if (fakeBackStack.size() > 0) { 492 | fakeBackStack.get(fakeBackStack.size() - 1).toggleShowHidden(); 493 | } 494 | } 495 | 496 | @Override 497 | public void zipFile() { 498 | if (fakeBackStack.size() > 0) { 499 | fakeBackStack.get(fakeBackStack.size() - 1).zipFile(); 500 | } 501 | } 502 | 503 | @Override 504 | public void renameFile() { 505 | if (fakeBackStack.size() > 0) { 506 | fakeBackStack.get(fakeBackStack.size() - 1).renameFile(); 507 | } 508 | } 509 | 510 | @Override 511 | public void propertyFile() { 512 | if (fakeBackStack.size() > 0) { 513 | fakeBackStack.get(fakeBackStack.size() - 1).propertyFile(); 514 | } 515 | } 516 | 517 | public void unzipFile() { 518 | if (fakeBackStack.size() > 0) { 519 | fakeBackStack.get(fakeBackStack.size() - 1).unzipFile(); 520 | } 521 | } 522 | 523 | public void favorFile() { 524 | if (fakeBackStack.size() > 0) { 525 | fakeBackStack.get(fakeBackStack.size() - 1).favorFile(); 526 | } 527 | } 528 | } 529 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/model/FileItem.java: -------------------------------------------------------------------------------- 1 | package net.nashlegend.legendexplorer.model; 2 | 3 | import java.io.File; 4 | import java.net.URI; 5 | 6 | import net.nashlegend.legendexplorer.application.ExplorerApplication; 7 | 8 | import net.nashlegend.legendexplorer.R; 9 | import net.nashlegend.legendexplorer.R.drawable; 10 | 11 | /** 12 | * 文件对象,继承自File 13 | * 14 | * @author NashLegend 15 | */ 16 | public class FileItem extends File { 17 | 18 | /** 19 | * TODO。每次改动后都要修改serialVersionUID…… 20 | */ 21 | private static final long serialVersionUID = 1L; 22 | /** 23 | * 普通文件 24 | */ 25 | public static final int FILE_TYPE_NORMAL = 0; 26 | /** 27 | * 文件夹 28 | */ 29 | public static final int FILE_TYPE_FOLDER = 1; 30 | /** 31 | * 声音类型的文件 32 | */ 33 | public static final int FILE_TYPE_AUDIO = 2; 34 | /** 35 | * 图像类型的文件 36 | */ 37 | public static final int FILE_TYPE_IMAGE = 3; 38 | /** 39 | * 视频类型的文件 40 | */ 41 | public static final int FILE_TYPE_VIDEO = 4; 42 | /** 43 | * APK文件 44 | */ 45 | public static final int FILE_TYPE_APK = 5; 46 | /** 47 | * TXT文件 48 | */ 49 | public static final int FILE_TYPE_TXT = 6; 50 | /** 51 | * ZIP文件 52 | */ 53 | public static final int FILE_TYPE_ZIP = 7; 54 | /** 55 | * HTML文件 56 | */ 57 | public static final int FILE_TYPE_HTML = 8; 58 | /** 59 | * WORD文件 60 | */ 61 | public static final int FILE_TYPE_WORD = 9; 62 | /** 63 | * EXCEL文件 64 | */ 65 | public static final int FILE_TYPE_EXCEL = 10; 66 | /** 67 | * PPT文件 68 | */ 69 | public static final int FILE_TYPE_PPT = 11; 70 | /** 71 | * PDF文件 72 | */ 73 | public static final int FILE_TYPE_PDF = 12; 74 | /** 75 | * 电子书文件 76 | */ 77 | public static final int FILE_TYPE_EBOOK = 13; 78 | /** 79 | * 种子文件 80 | */ 81 | public static final int FILE_TYPE_TORRENT = 14; 82 | /** 83 | * CHM文件 84 | */ 85 | public static final int FILE_TYPE_CHM = 15; 86 | 87 | /** 88 | * 文件在文件列表中显示的icon 89 | */ 90 | private int icon = R.drawable.ic_launcher; 91 | 92 | /** 93 | * 文件是否在列表中被选中 94 | */ 95 | private boolean selected = false; 96 | 97 | /** 98 | * 文件类型,默认为FILE_TYPE_NORMAL,即普通文件。 99 | */ 100 | private int fileType = FILE_TYPE_NORMAL; 101 | 102 | /** 103 | * 文件后缀 104 | */ 105 | private String suffix = ""; 106 | 107 | /** 108 | * 是否处于可选状态(可显示选择复) 109 | */ 110 | private boolean inSelectMode = false; 111 | /** 112 | * FileItem类型,普通文件,收藏文件,或者类型文件 113 | */ 114 | private int itemType = 0; 115 | 116 | public FileItem(File file) { 117 | this(file.getAbsolutePath()); 118 | } 119 | 120 | public FileItem(String path) { 121 | super(path); 122 | setFileTypeBySuffix(); 123 | } 124 | 125 | public FileItem(URI uri) { 126 | super(uri); 127 | setFileTypeBySuffix(); 128 | } 129 | 130 | public FileItem(File dir, String name) { 131 | super(dir, name); 132 | setFileTypeBySuffix(); 133 | } 134 | 135 | public FileItem(String dirPath, String name) { 136 | super(dirPath, name); 137 | setFileTypeBySuffix(); 138 | } 139 | 140 | /** 141 | * 根据后缀取得文件类型 142 | */ 143 | private void setFileTypeBySuffix() { 144 | if (isDirectory()) { 145 | setFileType(FILE_TYPE_FOLDER); 146 | } else { 147 | String suffix = getSuffixFromName(); 148 | // 在此处设置后缀 149 | setSuffix(suffix); 150 | // 根据后缀获取文件类型 151 | if (isArrayContains( 152 | getStringArraySourceByName(R.array.TypePackage), suffix)) { 153 | setFileType(FILE_TYPE_APK); 154 | } else if (isArrayContains( 155 | getStringArraySourceByName(R.array.TypeImage), suffix)) { 156 | setFileType(FILE_TYPE_IMAGE); 157 | } else if (isArrayContains( 158 | getStringArraySourceByName(R.array.TypeAudio), suffix)) { 159 | setFileType(FILE_TYPE_AUDIO); 160 | } else if (isArrayContains( 161 | getStringArraySourceByName(R.array.TypeVideo), suffix)) { 162 | setFileType(FILE_TYPE_VIDEO); 163 | } else if (isArrayContains( 164 | getStringArraySourceByName(R.array.TypeText), suffix)) { 165 | setFileType(FILE_TYPE_TXT); 166 | } else if (isArrayContains( 167 | getStringArraySourceByName(R.array.TypeZip), suffix)) { 168 | setFileType(FILE_TYPE_ZIP); 169 | } else if (isArrayContains( 170 | getStringArraySourceByName(R.array.TypeExcel), suffix)) { 171 | setFileType(FILE_TYPE_EXCEL); 172 | } else if (isArrayContains( 173 | getStringArraySourceByName(R.array.TypeHTML), suffix)) { 174 | setFileType(FILE_TYPE_HTML); 175 | } else if (isArrayContains( 176 | getStringArraySourceByName(R.array.TypePDF), suffix)) { 177 | setFileType(FILE_TYPE_PDF); 178 | } else if (isArrayContains( 179 | getStringArraySourceByName(R.array.TypePPT), suffix)) { 180 | setFileType(FILE_TYPE_PPT); 181 | } else if (isArrayContains( 182 | getStringArraySourceByName(R.array.TypeWord), suffix)) { 183 | setFileType(FILE_TYPE_WORD); 184 | } else if (isArrayContains( 185 | getStringArraySourceByName(R.array.TypeChm), suffix)) { 186 | setFileType(FILE_TYPE_CHM); 187 | } else if (isArrayContains( 188 | getStringArraySourceByName(R.array.TypeTorrent), suffix)) { 189 | setFileType(FILE_TYPE_TORRENT); 190 | } else if (isArrayContains( 191 | getStringArraySourceByName(R.array.TypeEBook), suffix)) { 192 | setFileType(FILE_TYPE_EBOOK); 193 | } else { 194 | setFileType(FILE_TYPE_NORMAL); 195 | } 196 | } 197 | } 198 | 199 | private String[] getStringArraySourceByName(int id) { 200 | return ExplorerApplication.getGlobalContext().getResources() 201 | .getStringArray(id); 202 | } 203 | 204 | private boolean isArrayContains(String[] strs, String suffix) { 205 | if (strs == null || suffix == null) { 206 | return false; 207 | } 208 | for (int i = 0; i < strs.length; i++) { 209 | if (suffix.equals(strs[i])) { 210 | return true; 211 | } 212 | } 213 | return false; 214 | } 215 | 216 | private String getSuffixFromName() { 217 | String fileName = getName(); 218 | String suffix = ""; 219 | int offset = fileName.lastIndexOf("."); 220 | // -1则没有后缀。0,则表示是一个隐藏文件而没有后缀,offset == fileName.length() - 221 | // 1,表示"."是最后一个字符,没有后缀 222 | if (offset > 0 && offset < fileName.length() - 1) { 223 | suffix = fileName.substring(offset + 1); 224 | } 225 | return suffix.toLowerCase(); 226 | } 227 | 228 | public boolean isSelected() { 229 | return selected; 230 | } 231 | 232 | public void setSelected(boolean selected) { 233 | this.selected = selected; 234 | } 235 | 236 | public int getFileType() { 237 | return fileType; 238 | } 239 | 240 | /** 241 | * 设置fileTyle,同时修改icon 242 | * 243 | * @param fileType 244 | */ 245 | public void setFileType(int fileType) { 246 | this.fileType = fileType; 247 | switch (fileType) { 248 | case FILE_TYPE_APK: 249 | setIcon(R.drawable.format_apk); 250 | break; 251 | case FILE_TYPE_FOLDER: 252 | setIcon(R.drawable.format_folder); 253 | break; 254 | case FILE_TYPE_IMAGE: 255 | setIcon(R.drawable.format_picture); 256 | break; 257 | case FILE_TYPE_NORMAL: 258 | setIcon(R.drawable.format_unkown); 259 | break; 260 | case FILE_TYPE_AUDIO: 261 | setIcon(R.drawable.format_music); 262 | break; 263 | case FILE_TYPE_TXT: 264 | setIcon(R.drawable.format_text); 265 | break; 266 | case FILE_TYPE_VIDEO: 267 | setIcon(R.drawable.format_media); 268 | break; 269 | case FILE_TYPE_ZIP: 270 | setIcon(R.drawable.format_zip); 271 | break; 272 | case FILE_TYPE_HTML: 273 | setIcon(R.drawable.format_html); 274 | break; 275 | case FILE_TYPE_PDF: 276 | setIcon(R.drawable.format_pdf); 277 | break; 278 | case FILE_TYPE_WORD: 279 | setIcon(R.drawable.format_word); 280 | break; 281 | case FILE_TYPE_EXCEL: 282 | setIcon(R.drawable.format_excel); 283 | break; 284 | case FILE_TYPE_PPT: 285 | setIcon(R.drawable.format_ppt); 286 | break; 287 | case FILE_TYPE_TORRENT: 288 | setIcon(R.drawable.format_torrent); 289 | break; 290 | case FILE_TYPE_EBOOK: 291 | setIcon(R.drawable.format_ebook); 292 | break; 293 | case FILE_TYPE_CHM: 294 | setIcon(R.drawable.format_chm); 295 | break; 296 | default: 297 | setIcon(R.drawable.format_unkown); 298 | break; 299 | } 300 | } 301 | 302 | public int getIcon() { 303 | return icon; 304 | } 305 | 306 | public void setIcon(int icon) { 307 | this.icon = icon; 308 | } 309 | 310 | public String getSuffix() { 311 | return suffix; 312 | } 313 | 314 | public void setSuffix(String suffix) { 315 | this.suffix = suffix; 316 | } 317 | 318 | public boolean isInSelectMode() { 319 | return inSelectMode; 320 | } 321 | 322 | public void setInSelectMode(boolean inSelectMode) { 323 | this.inSelectMode = inSelectMode; 324 | } 325 | 326 | public int getItemType() { 327 | return itemType; 328 | } 329 | 330 | public void setItemType(int itemType) { 331 | this.itemType = itemType; 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/utils/FileCategoryHelper.java: -------------------------------------------------------------------------------- 1 | package net.nashlegend.legendexplorer.utils; 2 | 3 | import java.util.HashMap; 4 | import java.util.HashSet; 5 | import java.util.Iterator; 6 | 7 | import net.nashlegend.legendexplorer.fragment.CategoriedFragment.FileCategory; 8 | 9 | import android.content.Context; 10 | import android.database.Cursor; 11 | import android.net.Uri; 12 | import android.provider.MediaStore.Audio; 13 | import android.provider.MediaStore.Files; 14 | import android.provider.MediaStore.Images; 15 | import android.provider.MediaStore.Video; 16 | import android.provider.MediaStore.Files.FileColumns; 17 | import android.util.Log; 18 | 19 | public class FileCategoryHelper { 20 | 21 | public static final int COLUMN_ID = 0; 22 | 23 | public static final int COLUMN_PATH = 1; 24 | 25 | public static final int COLUMN_SIZE = 2; 26 | 27 | public static final int COLUMN_DATE = 3; 28 | 29 | private static final String LOG_TAG = "cat"; 30 | 31 | public static class CategoryInfo { 32 | public long count; 33 | 34 | public long size; 35 | } 36 | 37 | private static HashMap mCategoryInfo = new HashMap(); 38 | 39 | public static HashMap getCategoryInfos() { 40 | return mCategoryInfo; 41 | } 42 | 43 | public static FileCategory[] sCategories = new FileCategory[] { 44 | FileCategory.Music, FileCategory.Video, FileCategory.Picture, 45 | FileCategory.Doc, FileCategory.Zip, FileCategory.Apk }; 46 | 47 | public FileCategoryHelper(Context context) { 48 | 49 | } 50 | 51 | public static void delete(FileCategory fc, String path, Context context) { 52 | 53 | } 54 | 55 | public static Cursor query(FileCategory fc, Context context) { 56 | Uri uri = getContentUriByCategory(fc); 57 | String selection = buildSelectionByCategory(fc); 58 | String sortOrder = null; 59 | 60 | if (uri == null) { 61 | return null; 62 | } 63 | 64 | String[] columns = new String[] { FileColumns._ID, FileColumns.DATA, 65 | FileColumns.SIZE, FileColumns.DATE_MODIFIED }; 66 | return context.getContentResolver().query(uri, columns, selection, 67 | null, sortOrder); 68 | } 69 | 70 | private static Uri getContentUriByCategory(FileCategory cat) { 71 | Uri uri; 72 | String volumeName = "external"; 73 | switch (cat) { 74 | case Doc: 75 | case Zip: 76 | case Apk: 77 | uri = Files.getContentUri(volumeName); 78 | break; 79 | case Music: 80 | uri = Audio.Media.getContentUri(volumeName); 81 | break; 82 | case Video: 83 | uri = Video.Media.getContentUri(volumeName); 84 | break; 85 | case Picture: 86 | uri = Images.Media.getContentUri(volumeName); 87 | break; 88 | default: 89 | uri = null; 90 | } 91 | return uri; 92 | } 93 | 94 | public static void refreshCategoryInfo(Context mContext) { 95 | for (FileCategory fc : sCategories) { 96 | setCategoryInfo(fc, 0, 0); 97 | } 98 | String volumeName = "external"; 99 | Uri uri = Audio.Media.getContentUri(volumeName); 100 | refreshMediaCategory(FileCategory.Music, uri, mContext); 101 | uri = Video.Media.getContentUri(volumeName); 102 | refreshMediaCategory(FileCategory.Video, uri, mContext); 103 | uri = Images.Media.getContentUri(volumeName); 104 | refreshMediaCategory(FileCategory.Picture, uri, mContext); 105 | uri = Files.getContentUri(volumeName); 106 | refreshMediaCategory(FileCategory.Doc, uri, mContext); 107 | refreshMediaCategory(FileCategory.Zip, uri, mContext); 108 | refreshMediaCategory(FileCategory.Apk, uri, mContext); 109 | } 110 | 111 | private static void setCategoryInfo(FileCategory fc, long count, long size) { 112 | CategoryInfo info = mCategoryInfo.get(fc); 113 | if (info == null) { 114 | info = new CategoryInfo(); 115 | mCategoryInfo.put(fc, info); 116 | } 117 | info.count = count; 118 | info.size = size; 119 | } 120 | 121 | private static boolean refreshMediaCategory(FileCategory fc, Uri uri, 122 | Context mContext) { 123 | String[] columns = new String[] { "COUNT(*)", "SUM(_size)" }; 124 | Cursor c = mContext.getContentResolver().query(uri, columns, 125 | buildSelectionByCategory(fc), null, null); 126 | if (c == null) { 127 | Log.e(LOG_TAG, "fail to query uri:" + uri); 128 | return false; 129 | } 130 | 131 | if (c.moveToNext()) { 132 | setCategoryInfo(fc, c.getLong(0), c.getLong(1)); 133 | c.close(); 134 | return true; 135 | } 136 | 137 | return false; 138 | } 139 | 140 | public static HashSet sDocMimeTypesSet = new HashSet() { 141 | /** 142 | * 143 | */ 144 | private static final long serialVersionUID = 1L; 145 | 146 | { 147 | add("text/plain"); 148 | add("application/pdf"); 149 | add("application/msword"); 150 | add("application/vnd.ms-excel"); 151 | add("application/vnd.ms-excel"); 152 | } 153 | }; 154 | 155 | private static String buildDocSelection() { 156 | StringBuilder selection = new StringBuilder(); 157 | Iterator iter = sDocMimeTypesSet.iterator(); 158 | while (iter.hasNext()) { 159 | selection.append("(" + FileColumns.MIME_TYPE + "=='" + iter.next() 160 | + "') OR "); 161 | } 162 | return selection.substring(0, selection.lastIndexOf(")") + 1); 163 | } 164 | 165 | private static String buildSelectionByCategory(FileCategory cat) { 166 | String selection = null; 167 | switch (cat) { 168 | case Doc: 169 | selection = buildDocSelection(); 170 | break; 171 | case Zip: 172 | selection = "(" + FileColumns.MIME_TYPE + " == '" 173 | + "application/zip" + "')"; 174 | break; 175 | case Apk: 176 | selection = FileColumns.DATA + " LIKE '%.apk' AND " + FileColumns.SIZE + " > 12288";//大于12k 177 | break; 178 | default: 179 | selection = null; 180 | } 181 | return selection; 182 | } 183 | 184 | } 185 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/utils/IconContainer.java: -------------------------------------------------------------------------------- 1 | package net.nashlegend.legendexplorer.utils; 2 | 3 | import java.io.File; 4 | import java.lang.ref.SoftReference; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | 7 | import android.graphics.Bitmap; 8 | 9 | public class IconContainer { 10 | private final static ConcurrentHashMap> mCachedIcons = new ConcurrentHashMap>(); 11 | 12 | public static Bitmap get(String path) { 13 | Bitmap bm = null; 14 | if (mCachedIcons.containsKey(path)) { 15 | bm = mCachedIcons.get(path).get(); 16 | } 17 | return bm; 18 | } 19 | 20 | public static Bitmap get(File file) { 21 | return get(file.getAbsolutePath()); 22 | } 23 | 24 | public static void put(String path, Bitmap bm) { 25 | SoftReference soft = new SoftReference(bm); 26 | mCachedIcons.put(path, soft); 27 | } 28 | 29 | public static void put(File file, Bitmap bm) { 30 | put(file.getAbsolutePath(), bm); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/utils/SharePreferencesUtil.java: -------------------------------------------------------------------------------- 1 | 2 | package net.nashlegend.legendexplorer.utils; 3 | 4 | import net.nashlegend.legendexplorer.application.ExplorerApplication; 5 | 6 | import android.content.Context; 7 | import android.content.SharedPreferences; 8 | import android.content.SharedPreferences.Editor; 9 | 10 | public class SharePreferencesUtil { 11 | 12 | // 本地xml文件名 13 | private final static String SP_NAME = "legendexplorer"; 14 | private static SharedPreferences mSharedPreferences; 15 | private static Editor mEditor; 16 | 17 | public static SharedPreferences getSharedPreferences() 18 | { 19 | if (mSharedPreferences == null) 20 | { 21 | mSharedPreferences = ExplorerApplication.getGlobalContext().getSharedPreferences( 22 | SP_NAME, Context.MODE_PRIVATE); 23 | } 24 | 25 | return mSharedPreferences; 26 | } 27 | 28 | public static Editor getEditor() 29 | { 30 | if (mEditor == null) 31 | { 32 | mEditor = getSharedPreferences().edit(); 33 | } 34 | 35 | return mEditor; 36 | } 37 | 38 | // 读String 39 | public static String readString(String key, String defaultvalue) { 40 | String str = getSharedPreferences().getString(key, defaultvalue); 41 | return str; 42 | } 43 | 44 | // 写String 45 | public static boolean saveString(String key, String value) { 46 | getEditor().putString(key, value); 47 | return getEditor().commit(); 48 | } 49 | 50 | // 读Boolean 51 | public static boolean readBoolean(String key, boolean defaultvalue) { 52 | return getSharedPreferences().getBoolean(key, defaultvalue); 53 | } 54 | 55 | // 写Boolean 56 | public static boolean saveBoolean(String key, boolean value) { 57 | getEditor().putBoolean(key, value); 58 | return getEditor().commit(); 59 | } 60 | 61 | // 读Int 62 | public static int readInt(String key, int defValue) { 63 | int n = getSharedPreferences().getInt(key, defValue); 64 | return n; 65 | } 66 | 67 | // 写Int 68 | public static boolean saveInt(String key, int value) { 69 | getEditor().putInt(key, value); 70 | return getEditor().commit(); 71 | } 72 | 73 | // 布尔值读 74 | public static boolean isActive(String key, boolean defaultvalue) { 75 | return getSharedPreferences().getBoolean(key, defaultvalue); 76 | } 77 | 78 | // 布尔值写 79 | public static boolean saveActive(String key, boolean value) { 80 | getEditor().putBoolean(key, value); 81 | return getEditor().commit(); 82 | } 83 | 84 | // 删除一项 85 | public static boolean remove(String key) { 86 | getEditor().remove(key); 87 | return getEditor().commit(); 88 | } 89 | 90 | // 全清空 91 | public static boolean clear() { 92 | getEditor().clear(); 93 | return getEditor().commit(); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/utils/StorageObserver.java: -------------------------------------------------------------------------------- 1 | package net.nashlegend.legendexplorer.utils; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import java.util.Stack; 7 | 8 | import android.os.FileObserver; 9 | import android.util.Log; 10 | 11 | public class StorageObserver extends FileObserver { 12 | 13 | public static int CHANGES_ONLY = CLOSE_WRITE | MOVE_SELF | MOVED_FROM; 14 | 15 | List mObservers; 16 | String mPath; 17 | int mMask; 18 | 19 | public StorageObserver(String path) { 20 | this(path, ALL_EVENTS); 21 | } 22 | 23 | public StorageObserver(String path, int mask) { 24 | super(path, mask); 25 | mPath = path; 26 | mMask = mask; 27 | } 28 | 29 | @Override 30 | public void startWatching() { 31 | if (mObservers != null) 32 | return; 33 | mObservers = new ArrayList(); 34 | Stack stack = new Stack(); 35 | stack.push(mPath); 36 | 37 | while (!stack.empty()) { 38 | String parent = stack.pop(); 39 | mObservers.add(new SingleFileObserver(parent, mMask)); 40 | File path = new File(parent); 41 | File[] files = path.listFiles(); 42 | if (files == null) 43 | continue; 44 | for (int i = 0; i < files.length; ++i) { 45 | if (files[i].isDirectory() && !files[i].getName().equals(".") 46 | && !files[i].getName().equals("..")) { 47 | stack.push(files[i].getPath()); 48 | } 49 | } 50 | } 51 | for (int i = 0; i < mObservers.size(); i++) 52 | mObservers.get(i).startWatching(); 53 | } 54 | 55 | @Override 56 | public void stopWatching() { 57 | if (mObservers == null) 58 | return; 59 | 60 | for (int i = 0; i < mObservers.size(); ++i) 61 | mObservers.get(i).stopWatching(); 62 | 63 | mObservers.clear(); 64 | mObservers = null; 65 | } 66 | 67 | @Override 68 | public void onEvent(int event, String path) { 69 | event &= FileObserver.ALL_EVENTS; 70 | switch (event) { 71 | case FileObserver.DELETE: 72 | Log.i("obs", "DELETE" + "__" + path); 73 | break; 74 | case FileObserver.CREATE: 75 | Log.i("obs", "CREATE" + "__" + path); 76 | File createdFile = new File(path); 77 | if (createdFile.isDirectory()) { 78 | SingleFileObserver ob = new SingleFileObserver(path, mMask); 79 | ob.startWatching(); 80 | mObservers.add(ob); 81 | } 82 | break; 83 | case FileObserver.MOVED_TO: 84 | Log.i("obs", "MOVED_TO" + "__" + path); 85 | break; 86 | case FileObserver.MOVED_FROM: 87 | Log.i("obs", "MOVED_FROM" + "__" + path); 88 | break; 89 | default: 90 | break; 91 | } 92 | } 93 | 94 | private class SingleFileObserver extends FileObserver { 95 | private String mPath; 96 | 97 | public SingleFileObserver(String path, int mask) { 98 | super(path, mask); 99 | mPath = path; 100 | } 101 | 102 | @Override 103 | public void onEvent(int event, String path) { 104 | String newPath = mPath + "/" + path; 105 | StorageObserver.this.onEvent(event, newPath); 106 | } 107 | 108 | } 109 | } -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/view/DropDownAncestorList.java: -------------------------------------------------------------------------------- 1 | package net.nashlegend.legendexplorer.view; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | import java.util.Collections; 6 | import java.util.Iterator; 7 | 8 | import net.nashlegend.legendexplorer.consts.FileConst; 9 | 10 | import net.nashlegend.legendexplorer.R; 11 | import net.nashlegend.legendutils.Tools.DisplayUtil; 12 | 13 | import android.content.Context; 14 | import android.text.TextUtils; 15 | import android.util.AttributeSet; 16 | import android.util.Log; 17 | import android.view.LayoutInflater; 18 | import android.view.View; 19 | import android.view.View.OnClickListener; 20 | import android.view.ViewGroup; 21 | import android.widget.LinearLayout; 22 | import android.widget.TextView; 23 | 24 | public class DropDownAncestorList extends LinearLayout implements 25 | OnClickListener { 26 | 27 | private File mFile = new File("/"); 28 | private ArrayList views = new ArrayList(); 29 | private int padStep = DisplayUtil.dip2px(10, getContext()); 30 | private String prefix = "//////////////"; 31 | private String replacer = FileConst.Value_Bookmark_Path; 32 | private OnAncestorClickListener onAncestorClickListener; 33 | private boolean hasPreffix = false; 34 | private File rootFile; 35 | 36 | public DropDownAncestorList(Context context) { 37 | super(context); 38 | setOrientation(LinearLayout.VERTICAL); 39 | setClickable(true); 40 | } 41 | 42 | public DropDownAncestorList(Context context, AttributeSet attrs) { 43 | super(context, attrs); 44 | setOrientation(LinearLayout.VERTICAL); 45 | setClickable(true); 46 | } 47 | 48 | public DropDownAncestorList(Context context, AttributeSet attrs, 49 | int defStyle) { 50 | super(context, attrs, defStyle); 51 | setOrientation(LinearLayout.VERTICAL); 52 | setClickable(true); 53 | } 54 | 55 | public OnAncestorClickListener getOnAncestorClickListener() { 56 | return onAncestorClickListener; 57 | } 58 | 59 | public void setOnAncestorClickListener( 60 | OnAncestorClickListener onAncestorClickListener) { 61 | this.onAncestorClickListener = onAncestorClickListener; 62 | } 63 | 64 | public void setupList(File currentFolder, String prefix, String replacer) { 65 | hasPreffix = true; 66 | this.prefix = prefix; 67 | this.replacer = replacer; 68 | this.rootFile = new File(prefix); 69 | setupList(currentFolder); 70 | } 71 | 72 | public void setupList(File file) { 73 | if (file != null && !file.equals(mFile)) { 74 | clearAllViews(); 75 | mFile = file; 76 | buildViewList(); 77 | } 78 | } 79 | 80 | private boolean isNotFileRoot(File file) { 81 | if (hasPreffix) { 82 | return !file.equals(rootFile); 83 | } else { 84 | return file.getParentFile() != null; 85 | } 86 | } 87 | 88 | private void buildViewList() { 89 | 90 | File tmpFile = mFile; 91 | 92 | while (isNotFileRoot(tmpFile)) { 93 | File file = tmpFile.getParentFile(); 94 | View view = LayoutInflater.from(getContext()).inflate( 95 | R.layout.view_ancestor_item, null); 96 | 97 | view.setTag(file.getAbsolutePath()); 98 | TextView tv = (TextView) view 99 | .findViewById(R.id.text_ancestor_file_title); 100 | String displatPath = file.getAbsolutePath(); 101 | if (displatPath.lastIndexOf("/") != displatPath.length() - 1) { 102 | displatPath += "/"; 103 | } 104 | 105 | if (hasPreffix) { 106 | displatPath = displatPath.replace(prefix, replacer); 107 | } 108 | 109 | tv.setText(displatPath); 110 | view.setOnClickListener(this); 111 | views.add(view); 112 | tmpFile = file; 113 | } 114 | 115 | Collections.reverse(views); 116 | int st = 0; 117 | for (Iterator iterator = views.iterator(); iterator.hasNext();) { 118 | View view = (View) iterator.next(); 119 | LinearLayout root = (LinearLayout) view 120 | .findViewById(R.id.rootFolderItemView); 121 | root.setPadding(padStep * st, 0, 0, 0); 122 | addView(view); 123 | st++; 124 | } 125 | } 126 | 127 | public void clearAllViews() { 128 | for (Iterator iterator = views.iterator(); iterator.hasNext();) { 129 | View view = (View) iterator.next(); 130 | if (view.getParent() != null) { 131 | ((ViewGroup) view.getParent()).removeView(view); 132 | } 133 | } 134 | views.clear(); 135 | } 136 | 137 | @Override 138 | public void onClick(View v) { 139 | if (v.getTag() != null) { 140 | String path = v.getTag().toString(); 141 | if (TextUtils.isEmpty(path)) { 142 | return; 143 | } 144 | if (onAncestorClickListener != null) { 145 | onAncestorClickListener.onClick(path); 146 | } 147 | } 148 | } 149 | 150 | public static interface OnAncestorClickListener { 151 | void onClick(String path); 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/view/FileGridItemView.java: -------------------------------------------------------------------------------- 1 | package net.nashlegend.legendexplorer.view; 2 | 3 | import java.io.IOException; 4 | 5 | import net.nashlegend.legendexplorer.adapter.FileListAdapter; 6 | import net.nashlegend.legendexplorer.consts.FileConst; 7 | import net.nashlegend.legendexplorer.model.FileItem; 8 | import net.nashlegend.legendexplorer.utils.IconContainer; 9 | 10 | import net.nashlegend.legendexplorer.R; 11 | import net.nashlegend.legendutils.Tools.FileUtil; 12 | 13 | import android.app.Service; 14 | import android.content.ActivityNotFoundException; 15 | import android.content.Context; 16 | import android.content.Intent; 17 | import android.graphics.Bitmap; 18 | import android.net.Uri; 19 | import android.os.AsyncTask; 20 | import android.os.AsyncTask.Status; 21 | import android.os.Vibrator; 22 | import android.view.LayoutInflater; 23 | import android.view.View; 24 | import android.view.ViewGroup; 25 | import android.widget.CheckBox; 26 | import android.widget.CompoundButton; 27 | import android.widget.CompoundButton.OnCheckedChangeListener; 28 | import android.widget.FrameLayout; 29 | import android.widget.ImageView; 30 | import android.widget.TextView; 31 | import android.widget.Toast; 32 | import android.view.View.OnClickListener; 33 | import android.view.View.OnLongClickListener; 34 | 35 | /** 36 | * 文件列表单个item的view 37 | * 38 | * @author NashLegend 39 | */ 40 | public class FileGridItemView extends FrameLayout implements OnClickListener, 41 | OnCheckedChangeListener, OnLongClickListener { 42 | private ImageView icon; 43 | private ImageView symbolView; 44 | private TextView title; 45 | private CheckBox checkBox; 46 | private ViewGroup rootFileItemView; 47 | private FileListAdapter adapter; 48 | private FileItem fileItem; 49 | private IconLoadTask task; 50 | 51 | public FileGridItemView(Context context) { 52 | super(context); 53 | LayoutInflater inflater = (LayoutInflater) context 54 | .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 55 | inflater.inflate(R.layout.view_grid_file_item, this); 56 | icon = (ImageView) findViewById(R.id.image_file_icon); 57 | symbolView = (ImageView) findViewById(R.id.image_file_sym_icon); 58 | title = (TextView) findViewById(R.id.text_file_title); 59 | rootFileItemView = (ViewGroup) findViewById(R.id.rootFileItemView); 60 | checkBox = (CheckBox) findViewById(R.id.checkbox_file_item_select); 61 | setOnClickListener(this); 62 | setLongClickable(true); 63 | } 64 | 65 | public FileItem getFileItem() { 66 | return fileItem; 67 | } 68 | 69 | public void setFileItem(FileItem fileItem, FileListAdapter adapter) { 70 | if (!fileItem.equals(this.fileItem)) { 71 | this.fileItem = fileItem; 72 | showFileIcon(); 73 | } 74 | this.fileItem = fileItem; 75 | this.adapter = adapter; 76 | title.setText(fileItem.getName()); 77 | toggleSelectState(); 78 | if (fileItem.isInSelectMode()) { 79 | checkBox.setVisibility(View.VISIBLE); 80 | setOnLongClickListener(null); 81 | } else { 82 | checkBox.setVisibility(View.GONE); 83 | setOnLongClickListener(this); 84 | } 85 | } 86 | 87 | /** 88 | * 显示文件图标 89 | */ 90 | private void showFileIcon() { 91 | icon.setImageResource(fileItem.getIcon()); 92 | int tp = fileItem.getFileType(); 93 | if (tp == FileItem.FILE_TYPE_APK || tp == FileItem.FILE_TYPE_AUDIO 94 | || tp == FileItem.FILE_TYPE_IMAGE 95 | || tp == FileItem.FILE_TYPE_VIDEO) { 96 | if (task != null && task.getStatus() == Status.RUNNING) { 97 | task.cancel(true); 98 | } 99 | Bitmap bmp = IconContainer.get(fileItem); 100 | if (bmp == null) { 101 | task = new IconLoadTask(); 102 | task.execute(fileItem); 103 | } else { 104 | icon.setImageBitmap(bmp); 105 | } 106 | } 107 | try { 108 | if (fileItem.isDirectory() && FileUtil.isSymboliclink(fileItem)) { 109 | symbolView.setVisibility(View.VISIBLE); 110 | } else { 111 | symbolView.setVisibility(View.GONE); 112 | } 113 | } catch (IOException e) { 114 | symbolView.setVisibility(View.GONE); 115 | } 116 | } 117 | 118 | class IconLoadTask extends AsyncTask { 119 | 120 | FileItem originalFile; 121 | 122 | @Override 123 | protected Bitmap doInBackground(FileItem... params) { 124 | originalFile = params[0]; 125 | Bitmap bmp = FileUtil.extractFileThumbnail(originalFile, 126 | getContext()); 127 | if (bmp != null) { 128 | IconContainer.put(originalFile, bmp); 129 | } 130 | return bmp; 131 | } 132 | 133 | @Override 134 | protected void onPostExecute(Bitmap result) { 135 | if (fileItem.equals(this.originalFile)) { 136 | if (result != null) { 137 | icon.setImageBitmap(result); 138 | } else { 139 | icon.setImageResource(fileItem.getIcon()); 140 | } 141 | } 142 | super.onPostExecute(result); 143 | } 144 | 145 | } 146 | 147 | /** 148 | * 切换选中、未选中状态,fileItem.setSelected(boolean)先发生; 149 | */ 150 | public void toggleSelectState() { 151 | toggleSelectState(false); 152 | } 153 | 154 | private void toggleSelectState(boolean manual) { 155 | if (fileItem.isSelected()) { 156 | rootFileItemView 157 | .setBackgroundResource(R.drawable.bg_file_item_select); 158 | } else { 159 | rootFileItemView 160 | .setBackgroundResource(R.drawable.bg_file_item_normal); 161 | if (manual && fileItem.isInSelectMode()) { 162 | Intent intent = new Intent(); 163 | intent.setAction(FileConst.Action_FileItem_Unselect); 164 | getContext().sendBroadcast(intent); 165 | } 166 | } 167 | checkBox.setOnCheckedChangeListener(null); 168 | checkBox.setChecked(fileItem.isSelected()); 169 | checkBox.setOnCheckedChangeListener(this); 170 | } 171 | 172 | @Override 173 | public void onClick(View v) { 174 | if (v.getId() != R.id.checkbox_file_item_select) { 175 | if (fileItem.isInSelectMode()) { 176 | selectOne(); 177 | } else { 178 | if (fileItem.isDirectory()) { 179 | openFolder(); 180 | } else { 181 | openFile(); 182 | } 183 | } 184 | 185 | } 186 | } 187 | 188 | /** 189 | * 打开文件 190 | */ 191 | private void openFile() { 192 | Intent intent = new Intent(Intent.ACTION_VIEW); 193 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 194 | intent.addCategory(Intent.CATEGORY_DEFAULT); 195 | Uri data = Uri.fromFile(fileItem); 196 | intent.setDataAndType(data, "*/*"); 197 | switch (fileItem.getFileType()) { 198 | case FileItem.FILE_TYPE_APK: 199 | intent.setDataAndType(data, 200 | "application/vnd.android.package-archive"); 201 | break; 202 | case FileItem.FILE_TYPE_IMAGE: 203 | intent.setDataAndType(data, "image/*"); 204 | break; 205 | case FileItem.FILE_TYPE_AUDIO: 206 | intent.putExtra("oneshot", 0); 207 | intent.putExtra("configchange", 0); 208 | intent.setDataAndType(data, "audio/*"); 209 | break; 210 | case FileItem.FILE_TYPE_TXT: 211 | intent.setDataAndType(data, "text/plain"); 212 | break; 213 | case FileItem.FILE_TYPE_VIDEO: 214 | intent.putExtra("oneshot", 0); 215 | intent.putExtra("configchange", 0); 216 | intent.setDataAndType(data, "video/*"); 217 | break; 218 | case FileItem.FILE_TYPE_ZIP: 219 | intent.setDataAndType(data, "application/zip"); 220 | break; 221 | case FileItem.FILE_TYPE_WORD: 222 | intent.setDataAndType(data, "application/msword"); 223 | break; 224 | case FileItem.FILE_TYPE_PPT: 225 | intent.setDataAndType(data, "application/vnd.ms-powerpoint"); 226 | break; 227 | case FileItem.FILE_TYPE_EXCEL: 228 | intent.setDataAndType(data, "application/vnd.ms-excel"); 229 | break; 230 | case FileItem.FILE_TYPE_HTML: 231 | intent.setDataAndType(data, "text/html"); 232 | break; 233 | case FileItem.FILE_TYPE_PDF: 234 | intent.setDataAndType(data, "application/pdf"); 235 | break; 236 | case FileItem.FILE_TYPE_TORRENT: 237 | intent.setDataAndType(data, "torrent/*"); 238 | break; 239 | case FileItem.FILE_TYPE_CHM: 240 | intent.setDataAndType(data, "application/mshelp"); 241 | break; 242 | 243 | default: 244 | break; 245 | } 246 | try { 247 | getContext().startActivity(intent); 248 | } catch (ActivityNotFoundException e) { 249 | Toast.makeText(getContext(), "打不开此文件类型", 250 | Toast.LENGTH_SHORT).show(); 251 | e.printStackTrace(); 252 | } 253 | } 254 | 255 | public void selectOne() { 256 | if (fileItem.isInSelectMode()) { 257 | fileItem.setSelected(!fileItem.isSelected()); 258 | toggleSelectState(true); 259 | } 260 | } 261 | 262 | public void openFolder() { 263 | Intent intent = new Intent(); 264 | intent.setAction(FileConst.Action_Open_Folder); 265 | intent.putExtra(FileConst.Extra_File_Path, fileItem.getAbsolutePath()); 266 | intent.putExtra(FileConst.Extra_Item_Type, fileItem.getItemType()); 267 | getContext().sendBroadcast(intent); 268 | } 269 | 270 | public FileListAdapter getAdapter() { 271 | return adapter; 272 | } 273 | 274 | @Override 275 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 276 | fileItem.setSelected(isChecked); 277 | toggleSelectState(true); 278 | } 279 | 280 | @Override 281 | public boolean onLongClick(View v) { 282 | if (FileUtil.isInExternalStorage(fileItem)) { 283 | Vibrator vibrator = (Vibrator) getContext().getSystemService( 284 | Service.VIBRATOR_SERVICE); 285 | vibrator.vibrate(20); 286 | 287 | fileItem.setSelected(true); 288 | 289 | Intent intent = new Intent(); 290 | intent.setAction(FileConst.Action_FileItem_Long_Click); 291 | getContext().sendBroadcast(intent); 292 | } 293 | return false; 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/view/FileItemView.java: -------------------------------------------------------------------------------- 1 | package net.nashlegend.legendexplorer.view; 2 | 3 | import java.io.IOException; 4 | 5 | import net.nashlegend.legendexplorer.ImageBrowseActivity; 6 | import net.nashlegend.legendexplorer.adapter.FileListAdapter; 7 | import net.nashlegend.legendexplorer.consts.FileConst; 8 | import net.nashlegend.legendexplorer.model.FileItem; 9 | import net.nashlegend.legendexplorer.utils.IconContainer; 10 | import net.nashlegend.legendexplorer.view.FileGridItemView.IconLoadTask; 11 | 12 | import net.nashlegend.legendexplorer.R; 13 | import net.nashlegend.legendutils.Tools.FileUtil; 14 | 15 | import android.app.Service; 16 | import android.content.ActivityNotFoundException; 17 | import android.content.Context; 18 | import android.content.Intent; 19 | import android.graphics.Bitmap; 20 | import android.net.Uri; 21 | import android.os.AsyncTask; 22 | import android.os.AsyncTask.Status; 23 | import android.os.Vibrator; 24 | import android.view.LayoutInflater; 25 | import android.view.View; 26 | import android.view.ViewGroup; 27 | import android.widget.CheckBox; 28 | import android.widget.CompoundButton; 29 | import android.widget.CompoundButton.OnCheckedChangeListener; 30 | import android.widget.FrameLayout; 31 | import android.widget.ImageView; 32 | import android.widget.TextView; 33 | import android.widget.Toast; 34 | import android.view.View.OnClickListener; 35 | import android.view.View.OnLongClickListener; 36 | 37 | /** 38 | * 文件列表单个item的view 39 | * 40 | * @author NashLegend 41 | */ 42 | public class FileItemView extends FrameLayout implements OnClickListener, 43 | OnCheckedChangeListener, OnLongClickListener { 44 | private ImageView icon; 45 | private ImageView symbolView; 46 | private TextView title; 47 | private CheckBox checkBox; 48 | private ViewGroup rootFileItemView; 49 | private FileListAdapter adapter; 50 | private FileItem fileItem; 51 | private IconLoadTask task; 52 | 53 | public FileItemView(Context context) { 54 | super(context); 55 | LayoutInflater inflater = (LayoutInflater) context 56 | .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 57 | inflater.inflate(R.layout.view_file_item, this); 58 | icon = (ImageView) findViewById(R.id.image_file_icon); 59 | symbolView = (ImageView) findViewById(R.id.image_file_sym_icon); 60 | title = (TextView) findViewById(R.id.text_file_title); 61 | rootFileItemView = (ViewGroup) findViewById(R.id.rootFileItemView); 62 | checkBox = (CheckBox) findViewById(R.id.checkbox_file_item_select); 63 | setOnClickListener(this); 64 | setLongClickable(true); 65 | } 66 | 67 | public FileItem getFileItem() { 68 | return fileItem; 69 | } 70 | 71 | public void setFileItem(FileItem fileItem, FileListAdapter adapter) { 72 | if (!fileItem.equals(this.fileItem)) { 73 | this.fileItem = fileItem; 74 | showFileIcon(); 75 | } 76 | this.fileItem = fileItem; 77 | this.adapter = adapter; 78 | title.setText(fileItem.getName()); 79 | toggleSelectState(); 80 | if (fileItem.isInSelectMode()) { 81 | checkBox.setVisibility(View.VISIBLE); 82 | setOnLongClickListener(null); 83 | } else { 84 | checkBox.setVisibility(View.GONE); 85 | setOnLongClickListener(this); 86 | } 87 | } 88 | 89 | /** 90 | * 显示文件图标 91 | */ 92 | private void showFileIcon() { 93 | icon.setImageResource(fileItem.getIcon()); 94 | int tp = fileItem.getFileType(); 95 | if (tp == FileItem.FILE_TYPE_APK || tp == FileItem.FILE_TYPE_AUDIO 96 | || tp == FileItem.FILE_TYPE_IMAGE 97 | || tp == FileItem.FILE_TYPE_VIDEO) { 98 | if (task != null && task.getStatus() == Status.RUNNING) { 99 | task.cancel(true); 100 | } 101 | Bitmap bmp = IconContainer.get(fileItem); 102 | if (bmp == null) { 103 | task = new IconLoadTask(); 104 | task.execute(fileItem); 105 | } else { 106 | icon.setImageBitmap(bmp); 107 | } 108 | } 109 | try { 110 | if (fileItem.isDirectory() && FileUtil.isSymboliclink(fileItem)) { 111 | symbolView.setVisibility(View.VISIBLE); 112 | } else { 113 | symbolView.setVisibility(View.GONE); 114 | } 115 | } catch (IOException e) { 116 | symbolView.setVisibility(View.GONE); 117 | } 118 | } 119 | 120 | class IconLoadTask extends AsyncTask { 121 | 122 | FileItem originalFile; 123 | 124 | @Override 125 | protected Bitmap doInBackground(FileItem... params) { 126 | originalFile = params[0]; 127 | Bitmap bmp = FileUtil.extractFileThumbnail(originalFile, 128 | getContext()); 129 | if (bmp != null) { 130 | IconContainer.put(originalFile, bmp); 131 | } 132 | return bmp; 133 | } 134 | 135 | @Override 136 | protected void onPostExecute(Bitmap result) { 137 | if (fileItem.equals(this.originalFile)) { 138 | if (result != null) { 139 | icon.setImageBitmap(result); 140 | } else { 141 | icon.setImageResource(fileItem.getIcon()); 142 | } 143 | } 144 | super.onPostExecute(result); 145 | } 146 | 147 | } 148 | 149 | /** 150 | * 切换选中、未选中状态,fileItem.setSelected(boolean)先发生; 151 | */ 152 | public void toggleSelectState() { 153 | toggleSelectState(false); 154 | } 155 | 156 | private void toggleSelectState(boolean manual) { 157 | if (fileItem.isSelected()) { 158 | rootFileItemView 159 | .setBackgroundResource(R.drawable.bg_file_item_select); 160 | if (manual && fileItem.isInSelectMode()) { 161 | Intent intent = new Intent(); 162 | intent.setAction(FileConst.Action_FileItem_Select); 163 | getContext().sendBroadcast(intent); 164 | } 165 | } else { 166 | rootFileItemView 167 | .setBackgroundResource(R.drawable.bg_file_item_normal); 168 | if (manual && fileItem.isInSelectMode()) { 169 | Intent intent = new Intent(); 170 | intent.setAction(FileConst.Action_FileItem_Unselect); 171 | getContext().sendBroadcast(intent); 172 | } 173 | } 174 | checkBox.setOnCheckedChangeListener(null); 175 | checkBox.setChecked(fileItem.isSelected()); 176 | checkBox.setOnCheckedChangeListener(this); 177 | } 178 | 179 | @Override 180 | public void onClick(View v) { 181 | if (v.getId() != R.id.checkbox_file_item_select) { 182 | if (fileItem.isInSelectMode()) { 183 | selectOne(); 184 | } else { 185 | if (fileItem.isDirectory()) { 186 | openFolder(); 187 | } else { 188 | openFile(); 189 | } 190 | } 191 | 192 | } 193 | } 194 | 195 | /** 196 | * 打开文件 197 | */ 198 | private void openFile() { 199 | Intent intent = new Intent(Intent.ACTION_VIEW); 200 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 201 | intent.addCategory(Intent.CATEGORY_DEFAULT); 202 | Uri data = Uri.fromFile(fileItem); 203 | intent.setDataAndType(data, "*/*"); 204 | switch (fileItem.getFileType()) { 205 | case FileItem.FILE_TYPE_APK: 206 | intent.setDataAndType(data, 207 | "application/vnd.android.package-archive"); 208 | break; 209 | case FileItem.FILE_TYPE_IMAGE: 210 | intent.setDataAndType(data, "image/*"); 211 | Intent intentImage=new Intent(); 212 | intentImage.putExtra(FileConst.Extra_File_Path, fileItem.getAbsolutePath()); 213 | intentImage.setClass(getContext(), ImageBrowseActivity.class); 214 | getContext().startActivity(intentImage); 215 | return; 216 | case FileItem.FILE_TYPE_AUDIO: 217 | intent.putExtra("oneshot", 0); 218 | intent.putExtra("configchange", 0); 219 | intent.setDataAndType(data, "audio/*"); 220 | break; 221 | case FileItem.FILE_TYPE_TXT: 222 | intent.setDataAndType(data, "text/plain"); 223 | break; 224 | case FileItem.FILE_TYPE_VIDEO: 225 | intent.putExtra("oneshot", 0); 226 | intent.putExtra("configchange", 0); 227 | intent.setDataAndType(data, "video/*"); 228 | break; 229 | case FileItem.FILE_TYPE_ZIP: 230 | intent.setDataAndType(data, "application/zip"); 231 | break; 232 | case FileItem.FILE_TYPE_WORD: 233 | intent.setDataAndType(data, "application/msword"); 234 | break; 235 | case FileItem.FILE_TYPE_PPT: 236 | intent.setDataAndType(data, "application/vnd.ms-powerpoint"); 237 | break; 238 | case FileItem.FILE_TYPE_EXCEL: 239 | intent.setDataAndType(data, "application/vnd.ms-excel"); 240 | break; 241 | case FileItem.FILE_TYPE_HTML: 242 | intent.setDataAndType(data, "text/html"); 243 | break; 244 | case FileItem.FILE_TYPE_PDF: 245 | intent.setDataAndType(data, "application/pdf"); 246 | break; 247 | case FileItem.FILE_TYPE_TORRENT: 248 | intent.setDataAndType(data, "torrent/*"); 249 | break; 250 | case FileItem.FILE_TYPE_CHM: 251 | intent.setDataAndType(data, "application/mshelp"); 252 | break; 253 | 254 | default: 255 | break; 256 | } 257 | try { 258 | getContext().startActivity(intent); 259 | } catch (ActivityNotFoundException e) { 260 | Toast.makeText(getContext(), "打不开此文件类型", 261 | Toast.LENGTH_SHORT).show(); 262 | e.printStackTrace(); 263 | } 264 | } 265 | 266 | public void selectOne() { 267 | if (fileItem.isInSelectMode()) { 268 | fileItem.setSelected(!fileItem.isSelected()); 269 | toggleSelectState(true); 270 | } 271 | } 272 | 273 | public void openFolder() { 274 | Intent intent = new Intent(); 275 | intent.setAction(FileConst.Action_Open_Folder); 276 | intent.putExtra(FileConst.Extra_File_Path, fileItem.getAbsolutePath()); 277 | intent.putExtra(FileConst.Extra_Item_Type, fileItem.getItemType()); 278 | getContext().sendBroadcast(intent); 279 | } 280 | 281 | public FileListAdapter getAdapter() { 282 | return adapter; 283 | } 284 | 285 | @Override 286 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 287 | fileItem.setSelected(isChecked); 288 | toggleSelectState(true); 289 | } 290 | 291 | @Override 292 | public boolean onLongClick(View v) { 293 | if (FileUtil.isInExternalStorage(fileItem)) { 294 | Vibrator vibrator = (Vibrator) getContext().getSystemService( 295 | Service.VIBRATOR_SERVICE); 296 | vibrator.vibrate(20); 297 | 298 | fileItem.setSelected(true); 299 | 300 | Intent intent = new Intent(); 301 | intent.setAction(FileConst.Action_FileItem_Long_Click); 302 | getContext().sendBroadcast(intent); 303 | } 304 | return false; 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/view/FilePropertyView.java: -------------------------------------------------------------------------------- 1 | package net.nashlegend.legendexplorer.view; 2 | 3 | import java.io.File; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Calendar; 6 | import java.util.Date; 7 | 8 | import net.nashlegend.legendexplorer.R; 9 | import net.nashlegend.legendutils.Tools.FileUtil; 10 | 11 | import android.annotation.SuppressLint; 12 | import android.content.Context; 13 | import android.os.AsyncTask; 14 | import android.os.AsyncTask.Status; 15 | import android.text.format.DateFormat; 16 | import android.util.AttributeSet; 17 | import android.view.LayoutInflater; 18 | import android.view.View; 19 | import android.widget.FrameLayout; 20 | import android.widget.LinearLayout; 21 | import android.widget.TextView; 22 | 23 | public class FilePropertyView extends FrameLayout { 24 | 25 | private TextView fileNameTextView; 26 | private TextView fileTypeTextView; 27 | private TextView filePathTextView; 28 | private TextView fileVolumeTextView; 29 | private TextView fileNumberTextView; 30 | private TextView fileTimeTextView; 31 | 32 | public View fileNamelayout; 33 | public View fileTypelayout; 34 | public View filePathlayout; 35 | public View fileVolumelayout; 36 | public View fileNumberlayout; 37 | public View fileTimelayout; 38 | 39 | loadPropertyTask task; 40 | 41 | private File[] files; 42 | 43 | public FilePropertyView(Context context) { 44 | super(context); 45 | LayoutInflater inflater = (LayoutInflater) context 46 | .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 47 | inflater.inflate(R.layout.dialog_file_property, this); 48 | fileNameTextView = (TextView) findViewById(R.id.text_filename); 49 | fileTypeTextView = (TextView) findViewById(R.id.text_filetype); 50 | filePathTextView = (TextView) findViewById(R.id.text_filepath); 51 | fileVolumeTextView = (TextView) findViewById(R.id.text_filevolume); 52 | fileNumberTextView = (TextView) findViewById(R.id.text_fileinvolving); 53 | fileTimeTextView = (TextView) findViewById(R.id.text_modified_time); 54 | fileNamelayout = findViewById(R.id.layout_filename); 55 | fileTypelayout = findViewById(R.id.layout_filetype); 56 | filePathlayout = findViewById(R.id.layout_filepath); 57 | fileVolumelayout = findViewById(R.id.layout_filevolume); 58 | fileNumberlayout = findViewById(R.id.layout_filenumber); 59 | fileTimelayout = findViewById(R.id.layout_modifiedtime); 60 | } 61 | 62 | @SuppressLint("SimpleDateFormat") 63 | public void setFiles(File[] fs, boolean allInOneFolder) { 64 | files = fs; 65 | if (files == null || files.length == 0) { 66 | return; 67 | } 68 | boolean needTask = false; 69 | 70 | if (files.length == 1) { 71 | File file = files[0]; 72 | fileNameTextView.setText(file.getName()); 73 | filePathTextView.setText(file.getParent()); 74 | if (file.isDirectory()) { 75 | fileVolumeTextView.setText("计算中..."); 76 | fileNumberTextView.setText("计算中..."); 77 | fileTypeTextView.setText("文件夹"); 78 | needTask = true; 79 | } else { 80 | fileTypeTextView.setText(FileUtil.getFileSuffix(file)); 81 | fileVolumeTextView.setText(FileUtil.convertStorage(file 82 | .length())); 83 | fileNumberlayout.setVisibility(View.GONE); 84 | } 85 | Date modiDate = new Date(file.lastModified()); 86 | SimpleDateFormat format = new SimpleDateFormat( 87 | "yyyy-MM-dd HH:mm:ss"); 88 | String dateString = format.format(modiDate); 89 | fileTimeTextView.setText(dateString + ""); 90 | } else { 91 | fileNamelayout.setVisibility(View.GONE); 92 | fileTypelayout.setVisibility(View.GONE); 93 | fileTimelayout.setVisibility(View.GONE); 94 | if (allInOneFolder) { 95 | filePathTextView.setText(files[0].getParent()); 96 | } else { 97 | filePathTextView.setText("N/A"); 98 | } 99 | fileVolumeTextView.setText("计算中..."); 100 | fileNumberTextView.setText("计算中..."); 101 | needTask = true; 102 | } 103 | if (needTask) { 104 | task = new loadPropertyTask(); 105 | task.execute(""); 106 | } 107 | } 108 | 109 | public void cancel() { 110 | if (task != null && task.getStatus() == Status.RUNNING) { 111 | task.cancel(true); 112 | } 113 | } 114 | 115 | class loadPropertyTask extends AsyncTask { 116 | 117 | long fsize = 0l; 118 | int fnumber = 0; 119 | 120 | @Override 121 | protected Integer doInBackground(String... params) { 122 | fsize = FileUtil.getFileSize(files); 123 | // 包含文件夹数量 124 | fnumber = FileUtil.getNumFilesInFolder(files, true, true); 125 | return null; 126 | } 127 | 128 | @Override 129 | protected void onPostExecute(Integer result) { 130 | fileVolumeTextView.setText(FileUtil.convertStorage(fsize)); 131 | fileNumberTextView.setText(String.valueOf(fnumber)); 132 | } 133 | 134 | } 135 | 136 | public FilePropertyView(Context context, AttributeSet attrs) { 137 | super(context, attrs); 138 | } 139 | 140 | public FilePropertyView(Context context, AttributeSet attrs, int defStyle) { 141 | super(context, attrs, defStyle); 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/view/FolderViewPager.java: -------------------------------------------------------------------------------- 1 | 2 | package net.nashlegend.legendexplorer.view; 3 | 4 | import android.content.Context; 5 | import android.support.v4.view.ViewPager; 6 | import android.util.AttributeSet; 7 | import android.util.Log; 8 | import android.view.MotionEvent; 9 | 10 | public class FolderViewPager extends ViewPager { 11 | 12 | private boolean scrollEnabled = true; 13 | private float preX = 0f; 14 | private float preY = 0f; 15 | 16 | public FolderViewPager(Context context) { 17 | super(context); 18 | } 19 | 20 | public FolderViewPager(Context context, AttributeSet attrs) { 21 | super(context, attrs); 22 | } 23 | 24 | @Override 25 | public boolean onTouchEvent(MotionEvent arg0) { 26 | if (scrollEnabled) { 27 | return super.onTouchEvent(arg0); 28 | } 29 | return true; 30 | } 31 | 32 | @Override 33 | public boolean onInterceptTouchEvent(MotionEvent arg0) { 34 | switch (arg0.getAction()) { 35 | case MotionEvent.ACTION_DOWN: 36 | preX = arg0.getX(); 37 | preY = arg0.getY(); 38 | break; 39 | case MotionEvent.ACTION_MOVE: 40 | if (!scrollEnabled) { 41 | float currentX = arg0.getX(); 42 | float currentY = arg0.getY(); 43 | if (Math.abs((currentX - preX) / (currentY - preY)) > 1) { 44 | return true; 45 | } 46 | } 47 | break; 48 | 49 | default: 50 | break; 51 | } 52 | return super.onInterceptTouchEvent(arg0); 53 | } 54 | 55 | public boolean isScrollEnabled() { 56 | return scrollEnabled; 57 | } 58 | 59 | public void setScrollEnabled(boolean scrollEnabled) { 60 | // TODO 61 | this.scrollEnabled = scrollEnabled; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/view/ImageViewState.java: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 David Morrissey 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package net.nashlegend.legendexplorer.view; 18 | 19 | import android.graphics.PointF; 20 | 21 | import java.io.Serializable; 22 | 23 | /** 24 | * Wraps the scale, center and orientation of a displayed image for easy restoration on screen rotate. 25 | */ 26 | public class ImageViewState implements Serializable { 27 | 28 | private float scale; 29 | 30 | private float centerX; 31 | 32 | private float centerY; 33 | 34 | private int orientation; 35 | 36 | public ImageViewState(float scale, PointF center, int orientation) { 37 | this.scale = scale; 38 | this.centerX = center.x; 39 | this.centerY = center.y; 40 | this.orientation = orientation; 41 | } 42 | 43 | public float getScale() { 44 | return scale; 45 | } 46 | 47 | public PointF getCenter() { 48 | return new PointF(centerX, centerY); 49 | } 50 | 51 | public int getOrientation() { 52 | return orientation; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/view/ImageViewer.java: -------------------------------------------------------------------------------- 1 | 2 | package net.nashlegend.legendexplorer.view; 3 | 4 | import java.io.File; 5 | import java.util.ArrayList; 6 | 7 | import net.nashlegend.legendutils.Tools.FileUtil; 8 | 9 | import android.animation.Animator; 10 | import android.animation.AnimatorSet; 11 | import android.animation.ObjectAnimator; 12 | import android.annotation.SuppressLint; 13 | import android.content.Context; 14 | import android.graphics.Point; 15 | import android.graphics.PointF; 16 | import android.util.AttributeSet; 17 | import android.view.MotionEvent; 18 | import android.view.VelocityTracker; 19 | import android.view.animation.DecelerateInterpolator; 20 | import android.widget.RelativeLayout; 21 | 22 | public class ImageViewer extends RelativeLayout { 23 | 24 | TheImage leftImage; 25 | TheImage middleImage; 26 | TheImage rightImage; 27 | int scrollDis = 30; 28 | boolean scrolling = false; 29 | boolean resizing = false; 30 | 31 | // 按下时的点 32 | PointF downPoint = new PointF(0f, 0f); 33 | // 拖动开始的点 34 | PointF startPoint = new PointF(0f, 0f); 35 | PointF lastPoint = new PointF(0f, 0f); 36 | 37 | Point leftPoint; 38 | Point middlePoint; 39 | Point rightPoint; 40 | 41 | AnimatorSet animatorSet; 42 | 43 | ArrayList files = new ArrayList(); 44 | int imageIndex = 0; 45 | 46 | VelocityTracker velocityTracker; 47 | 48 | float maxVelovityInDP = 1f; 49 | float maxVelocityValue = 1f; 50 | float minValidVelocityInDP = 0.3f; 51 | float minValidVelocityValue = 0.3f; 52 | 53 | float scaleFrom = 0.8f; 54 | 55 | public ImageViewer(Context context) { 56 | super(context); 57 | } 58 | 59 | public ImageViewer(Context context, AttributeSet attrs) { 60 | super(context, attrs); 61 | } 62 | 63 | public ImageViewer(Context context, AttributeSet attrs, int defStyle) { 64 | super(context, attrs, defStyle); 65 | } 66 | 67 | /** 68 | * test only 69 | */ 70 | public void test() { 71 | File[] filess = new File("/storage/emulated/0/androidesk/wallpapers/").listFiles(); 72 | for (int i = 0; i < filess.length; i++) { 73 | File file = filess[i]; 74 | files.add(file); 75 | } 76 | imageIndex = 0; 77 | setupImages(); 78 | } 79 | 80 | public void setDataSource(String path) { 81 | File FirstImage = new File(path); 82 | if (isImageFile(FirstImage)) { 83 | File parentFile = FirstImage.getParentFile(); 84 | File[] listFiles = parentFile.listFiles(); 85 | if (listFiles != null) { 86 | for (int i = 0; i < parentFile.listFiles().length; i++) { 87 | File file = listFiles[i]; 88 | if (isImageFile(file)) { 89 | files.add(file); 90 | if (file.equals(FirstImage)) { 91 | imageIndex = files.size() - 1; 92 | } 93 | } 94 | } 95 | setupImages(); 96 | } 97 | } 98 | } 99 | 100 | @SuppressLint("Recycle") 101 | public void setupImages() { 102 | if (velocityTracker == null) { 103 | velocityTracker = VelocityTracker.obtain(); 104 | } 105 | maxVelocityValue = maxVelovityInDP 106 | * getContext().getResources().getDisplayMetrics().density; 107 | minValidVelocityValue = minValidVelocityInDP 108 | * getContext().getResources().getDisplayMetrics().density; 109 | 110 | if (files.size() == 1) { 111 | leftImage = null; 112 | middleImage = new TheImage(getContext()); 113 | rightImage = null; 114 | } else { 115 | if (imageIndex == 0) { 116 | leftImage = null; 117 | middleImage = new TheImage(getContext()); 118 | rightImage = new TheImage(getContext()); 119 | } else if (imageIndex == files.size() - 1) { 120 | rightImage = null; 121 | middleImage = new TheImage(getContext()); 122 | leftImage = new TheImage(getContext()); 123 | } else { 124 | leftImage = new TheImage(getContext()); 125 | middleImage = new TheImage(getContext()); 126 | rightImage = new TheImage(getContext()); 127 | } 128 | } 129 | 130 | if (rightImage != null) { 131 | rightImage.load(files.get(imageIndex + 1), getWidth(), getHeight()); 132 | LayoutParams paramsr = new LayoutParams(rightImage.initWidth, rightImage.initHeight); 133 | rightImage.setLayoutParams(paramsr); 134 | addView(rightImage); 135 | rightImage.setX((getWidth() - rightImage.initWidth) / 2); 136 | rightImage.setY((getHeight() - rightImage.initHeight) / 2); 137 | rightImage.setAlpha(0f); 138 | rightImage.setScaleX(scaleFrom); 139 | rightImage.setScaleY(scaleFrom); 140 | rightPoint = new Point((getWidth() - rightImage.initWidth) / 2, 141 | (getHeight() - rightImage.initHeight) / 2); 142 | } 143 | 144 | middleImage.load(files.get(imageIndex), getWidth(), getHeight()); 145 | LayoutParams paramsm = new LayoutParams(middleImage.initWidth, middleImage.initHeight); 146 | middleImage.setLayoutParams(paramsm); 147 | addView(middleImage); 148 | middleImage.setX((getWidth() - middleImage.initWidth) / 2); 149 | middleImage.setY((getHeight() - middleImage.initHeight) / 2); 150 | middlePoint = new Point((getWidth() - middleImage.initWidth) / 2, 151 | (getHeight() - middleImage.initHeight) / 2); 152 | 153 | if (leftImage != null) { 154 | leftImage.load(files.get(imageIndex - 1), getWidth(), getHeight()); 155 | LayoutParams paramsl = new LayoutParams(leftImage.initWidth, leftImage.initHeight); 156 | leftImage.setLayoutParams(paramsl); 157 | addView(leftImage); 158 | leftImage.setX((getWidth() - leftImage.initWidth) / 2 - getWidth()); 159 | leftImage.setY((getHeight() - leftImage.initHeight) / 2); 160 | leftPoint = new Point((getWidth() - leftImage.initWidth) / 2 - getWidth(), 161 | (getHeight() - leftImage.initHeight) / 2); 162 | } 163 | } 164 | 165 | public boolean isImageFile(File file) { 166 | return FileUtil.getFileType(file) == FileUtil.FILE_TYPE_IMAGE; 167 | } 168 | 169 | public float distance(PointF pointF, PointF pointF2) { 170 | return PointF.length(pointF.x - pointF2.x, pointF.y - pointF2.y); 171 | } 172 | 173 | @Override 174 | public boolean onInterceptTouchEvent(MotionEvent ev) { 175 | if (files.size() == 0) { 176 | return true; 177 | } 178 | boolean flag = false; 179 | PointF pointF; 180 | switch (ev.getAction()) { 181 | case MotionEvent.ACTION_DOWN: 182 | scrolling = false; 183 | flag = false; 184 | downPoint = new PointF(ev.getX(), ev.getY()); 185 | startPoint = new PointF(ev.getX(), ev.getY()); 186 | lastPoint = new PointF(ev.getX(), ev.getY()); 187 | break; 188 | case MotionEvent.ACTION_MOVE: 189 | pointF = new PointF(ev.getX(), ev.getY()); 190 | if (middleImage.TouchMode == TheImage.MODE_NORMAL) { 191 | if (scrolling) { 192 | flag = true; 193 | } else { 194 | if (Math.abs(ev.getY() - downPoint.x) > scrollDis) { 195 | flag = true; 196 | scrolling = true; 197 | } else { 198 | flag = false; 199 | } 200 | } 201 | } else { 202 | flag = false; 203 | } 204 | 205 | lastPoint = pointF; 206 | break; 207 | case MotionEvent.ACTION_CANCEL: 208 | break; 209 | case MotionEvent.ACTION_UP: 210 | flag = scrolling; 211 | break; 212 | 213 | default: 214 | break; 215 | } 216 | return flag; 217 | } 218 | 219 | @SuppressLint("ClickableViewAccessibility") 220 | @Override 221 | public boolean onTouchEvent(MotionEvent ev) { 222 | if (files.size() == 0) { 223 | return true; 224 | } 225 | velocityTracker.addMovement(ev); 226 | PointF pointF; 227 | switch (ev.getAction()) { 228 | case MotionEvent.ACTION_DOWN: 229 | 230 | break; 231 | case MotionEvent.ACTION_MOVE: 232 | pointF = new PointF(ev.getX(), ev.getY()); 233 | if (scrolling) { 234 | scrollImageBy(pointF.x - startPoint.x, pointF.y - startPoint.y); 235 | } else { 236 | if (distance(pointF, downPoint) > scrollDis) { 237 | scrolling = true; 238 | startPoint = lastPoint; 239 | scrollImageBy(pointF.x - startPoint.x, pointF.y - startPoint.y); 240 | } 241 | } 242 | lastPoint = pointF; 243 | break; 244 | case MotionEvent.ACTION_CANCEL: 245 | break; 246 | case MotionEvent.ACTION_UP: 247 | if (scrolling) { 248 | scrolling = false; 249 | } 250 | final VelocityTracker tmpTracker = velocityTracker; 251 | tmpTracker.computeCurrentVelocity(1, maxVelocityValue); 252 | onDragEnd(tmpTracker.getXVelocity(), tmpTracker.getYVelocity()); 253 | break; 254 | 255 | default: 256 | break; 257 | } 258 | return super.onTouchEvent(ev); 259 | } 260 | 261 | float factor = 0.3f; 262 | 263 | public void onDragEnd(float vx, float vy) { 264 | if (resizing) { 265 | 266 | } else { 267 | int dura = 200; 268 | if (Math.abs(vx) > minValidVelocityValue) { 269 | if (vx < 0) { 270 | if (middleImage.getScaleX() < 1) { 271 | dura = Math 272 | .abs((int) ((1 - middleImage.getScaleX()) / (1 - scaleFrom) 273 | * getWidth() / vx * factor)); 274 | scrollBack(dura); 275 | } else { 276 | if (imageIndex < files.size() - 1) { 277 | dura = Math 278 | .abs((int) ((getWidth() + middleImage.getX() - middlePoint.x) 279 | / vx * factor)); 280 | scrollLeft(dura); 281 | } 282 | } 283 | } else { 284 | if (middleImage.getScaleX() < 1) { 285 | if (imageIndex > 0) { 286 | dura = Math 287 | .abs((int) ((middleImage.getScaleX() - scaleFrom) 288 | / (1 - scaleFrom) * getWidth() / vx * factor)); 289 | scrollRight(dura); 290 | } 291 | } else { 292 | dura = Math 293 | .abs((int) ((middleImage.getX() - middlePoint.x) / vx * factor)); 294 | scrollBack(dura); 295 | } 296 | } 297 | } else { 298 | if (middleImage.getScaleX() < (1 + scaleFrom) / 2) { 299 | scrollRight(dura); 300 | } else if (middleImage.getX() < middlePoint.x - getWidth() / 2) { 301 | scrollLeft(dura); 302 | } else { 303 | scrollBack(dura); 304 | } 305 | } 306 | } 307 | } 308 | 309 | public void scrollRight(int dura) { 310 | imageIndex--; 311 | if (rightImage != null) { 312 | removeView(rightImage); 313 | } 314 | 315 | rightImage = middleImage; 316 | rightPoint = new Point(middlePoint.x, middlePoint.y); 317 | 318 | middleImage = leftImage; 319 | middlePoint = new Point(leftPoint.x + getWidth(), leftPoint.y); 320 | 321 | if (imageIndex > 0) { 322 | leftImage = new TheImage(getContext()); 323 | leftImage.load(files.get(imageIndex - 1), getWidth(), getHeight()); 324 | LayoutParams paramsl = new LayoutParams(leftImage.initWidth, leftImage.initHeight); 325 | leftImage.setLayoutParams(paramsl); 326 | addView(leftImage); 327 | leftImage.setX((getWidth() - leftImage.initWidth) / 2 - getWidth()); 328 | leftImage.setY((getHeight() - leftImage.initHeight) / 2); 329 | leftPoint = new Point((getWidth() - leftImage.initWidth) / 2 - getWidth(), 330 | (getHeight() - leftImage.initHeight) / 2); 331 | } else { 332 | leftImage = null; 333 | leftPoint = null; 334 | } 335 | scrollBack(dura); 336 | } 337 | 338 | public void scrollLeft(int dura) { 339 | imageIndex++; 340 | if (leftImage != null) { 341 | removeView(leftImage); 342 | } 343 | 344 | leftImage = middleImage; 345 | leftPoint = new Point(middlePoint.x - getWidth(), middlePoint.y); 346 | 347 | middleImage = rightImage; 348 | middlePoint = new Point(rightPoint.x, rightPoint.y); 349 | 350 | if (imageIndex < files.size() - 1) { 351 | rightImage = new TheImage(getContext()); 352 | rightImage.load(files.get(imageIndex + 1), getWidth(), getHeight()); 353 | LayoutParams paramsr = new LayoutParams(rightImage.initWidth, rightImage.initHeight); 354 | rightImage.setLayoutParams(paramsr); 355 | rightImage.setAlpha(0f); 356 | addView(rightImage, 0); 357 | rightImage.setX((getWidth() - rightImage.initWidth) / 2); 358 | rightImage.setY((getHeight() - rightImage.initHeight) / 2); 359 | rightImage.setScaleX(scaleFrom); 360 | rightImage.setScaleY(scaleFrom); 361 | rightPoint = new Point((getWidth() - rightImage.initWidth) / 2, 362 | (getHeight() - rightImage.initHeight) / 2); 363 | } else { 364 | rightImage = null; 365 | rightPoint = null; 366 | } 367 | scrollBack(dura); 368 | } 369 | 370 | public void scrollBack(int dura) { 371 | if (animatorSet != null && animatorSet.isRunning()) { 372 | animatorSet.cancel(); 373 | } 374 | animatorSet = new AnimatorSet(); 375 | ArrayList animators = new ArrayList(); 376 | if (leftImage != null) { 377 | ObjectAnimator animatorLeft = ObjectAnimator.ofFloat(leftImage, "x", 378 | leftImage.getX(), leftPoint.x); 379 | animators.add(animatorLeft); 380 | } 381 | 382 | ObjectAnimator holderMiddleX = ObjectAnimator.ofFloat(middleImage, "x", 383 | middleImage.getX(), middlePoint.x); 384 | ObjectAnimator holderMiddleAlpha = ObjectAnimator.ofFloat(middleImage, "alpha", 385 | middleImage.getAlpha(), 1f); 386 | ObjectAnimator holderMiddleScaleX = ObjectAnimator.ofFloat(middleImage, "scaleX", 387 | middleImage.getScaleX(), 1f); 388 | ObjectAnimator holderMiddleScaleY = ObjectAnimator.ofFloat(middleImage, "scaleY", 389 | middleImage.getScaleY(), 1f); 390 | 391 | animators.add(holderMiddleX); 392 | animators.add(holderMiddleAlpha); 393 | animators.add(holderMiddleScaleX); 394 | animators.add(holderMiddleScaleY); 395 | 396 | if (rightImage != null) { 397 | ObjectAnimator holderRightX = ObjectAnimator.ofFloat(rightImage, "x", 398 | rightImage.getX(), rightPoint.x); 399 | ObjectAnimator holderRightAlpha = ObjectAnimator.ofFloat(rightImage, "alpha", 400 | rightImage.getAlpha(), 0f); 401 | ObjectAnimator holderRightScaleX = ObjectAnimator.ofFloat(rightImage, "scaleX", 402 | rightImage.getScaleX(), scaleFrom); 403 | ObjectAnimator holderRightScaleY = ObjectAnimator.ofFloat(rightImage, "scaleY", 404 | rightImage.getScaleY(), scaleFrom); 405 | 406 | animators.add(holderRightX); 407 | animators.add(holderRightAlpha); 408 | animators.add(holderRightScaleX); 409 | animators.add(holderRightScaleY); 410 | } 411 | animatorSet.playTogether(animators); 412 | animatorSet.setInterpolator(new DecelerateInterpolator()); 413 | animatorSet.setDuration(dura); 414 | animatorSet.start(); 415 | } 416 | 417 | public void scrollImageBy(float x, float y) { 418 | if (resizing) { 419 | 420 | } else { 421 | scrollThreeBodyBy(x, y); 422 | } 423 | } 424 | 425 | public void scrollThreeBodyBy(float x, float y) { 426 | if (x > 0) { 427 | // 右侧,rightImage不动 428 | if (leftImage == null) { 429 | middleImage.setX(middlePoint.x); 430 | } else { 431 | leftImage.setX(leftPoint.x + x); 432 | // middle shrink 433 | float scale = scaleFrom + (1 - scaleFrom) * (1 - x / getWidth()); 434 | middleImage.setScaleX(scale); 435 | middleImage.setScaleY(scale); 436 | middleImage.setAlpha((1 - x / getWidth())); 437 | } 438 | if (rightImage != null) { 439 | rightImage.setAlpha(0f); 440 | } 441 | } else { 442 | // 左侧,leftImage不动 443 | if (rightImage == null) { 444 | middleImage.setX(middlePoint.x); 445 | } else { 446 | middleImage.setX(middlePoint.x + x); 447 | // expand right 448 | float scale = scaleFrom + (1 - scaleFrom) * (-x / getWidth()); 449 | rightImage.setScaleX(scale); 450 | rightImage.setScaleY(scale); 451 | rightImage.setAlpha((-x / getWidth())); 452 | } 453 | if (leftImage != null) { 454 | leftImage.setX(leftPoint.x); 455 | } 456 | } 457 | } 458 | } 459 | -------------------------------------------------------------------------------- /src/net/nashlegend/legendexplorer/view/TheImage.java: -------------------------------------------------------------------------------- 1 | 2 | package net.nashlegend.legendexplorer.view; 3 | 4 | import java.io.File; 5 | import java.lang.reflect.Array; 6 | import java.util.Arrays; 7 | 8 | import android.R.integer; 9 | import android.annotation.SuppressLint; 10 | import android.content.Context; 11 | import android.graphics.Bitmap; 12 | import android.graphics.BitmapFactory; 13 | import android.graphics.Canvas; 14 | import android.graphics.Color; 15 | import android.graphics.Matrix; 16 | import android.graphics.Point; 17 | import android.graphics.PointF; 18 | import android.graphics.drawable.BitmapDrawable; 19 | import android.os.AsyncTask; 20 | import android.util.AttributeSet; 21 | import android.view.GestureDetector; 22 | import android.view.MotionEvent; 23 | import android.view.VelocityTracker; 24 | import android.view.View; 25 | import android.view.GestureDetector.SimpleOnGestureListener; 26 | import android.view.ViewGroup.LayoutParams; 27 | import android.widget.ImageView; 28 | 29 | public class TheImage extends ImageView { 30 | 31 | public int initWidth = 0; 32 | public int initHeight = 0; 33 | public float initScale = 1f; 34 | 35 | public static final int MODE_NORMAL = 0; 36 | public static final int MODE_DRAG = 1; 37 | public static final int MODE_ZOOM = 2; 38 | int TouchMode = MODE_NORMAL; 39 | boolean resizing = false; 40 | private Matrix initMatrix = new Matrix(); 41 | 42 | public TheImage(Context context) { 43 | super(context); 44 | } 45 | 46 | public TheImage(Context context, AttributeSet attrs) { 47 | super(context, attrs); 48 | } 49 | 50 | public TheImage(Context context, AttributeSet attrs, int defStyle) { 51 | super(context, attrs, defStyle); 52 | } 53 | 54 | @Override 55 | protected void onDetachedFromWindow() { 56 | super.onDetachedFromWindow(); 57 | try { 58 | if (!theBitmap.isRecycled()) { 59 | theBitmap.recycle(); 60 | } 61 | } catch (Exception e) { 62 | 63 | } 64 | } 65 | 66 | float dist1 = 10f; 67 | float dist2 = 10f; 68 | PointF downPoint = new PointF(0f, 0f); 69 | PointF lastPoint = new PointF(0f, 0f); 70 | Bitmap theBitmap; 71 | float[] values = { 72 | 0, 0, 0, 0, 0, 0, 0, 0, 0 73 | }; 74 | float[] lastStableValues = { 75 | 0, 0, 0, 0, 0, 0, 0, 0, 0 76 | }; 77 | float[] initValues = { 78 | 0, 0, 0, 0, 0, 0, 0, 0, 0 79 | }; 80 | float scrollDis = 36f; 81 | 82 | @SuppressLint("ClickableViewAccessibility") 83 | @Override 84 | public boolean onTouchEvent(MotionEvent event) { 85 | super.onTouchEvent(event); 86 | boolean flag = true; 87 | int pointIndex = event.getActionIndex(); 88 | switch (event.getAction() & MotionEvent.ACTION_MASK) { 89 | case MotionEvent.ACTION_DOWN: 90 | downPoint = new PointF(event.getX(), event.getY()); 91 | lastPoint = null; 92 | break; 93 | case MotionEvent.ACTION_POINTER_DOWN: 94 | dist1 = distance(event); 95 | getCenter(event); 96 | getImageMatrix().getValues(lastStableValues); 97 | TouchMode = MODE_ZOOM; 98 | break; 99 | case MotionEvent.ACTION_MOVE: 100 | if (TouchMode == MODE_NORMAL) { 101 | if (Math.abs(event.getX() - downPoint.x) > scrollDis) { 102 | flag = false; 103 | } 104 | } else { 105 | getImageMatrix().getValues(values); 106 | if (TouchMode == MODE_ZOOM) { 107 | dist2 = distance(event); 108 | if (values[0] * dist2 / dist1 > initValues[0]) { 109 | getImageMatrix().postScale((dist2 / dist1), (dist2 / dist1), 110 | centerPoint.x, 111 | centerPoint.y); 112 | } else { 113 | // do nothing 114 | } 115 | dist1 = dist2; 116 | } else { 117 | if (lastPoint != null) { 118 | // top left 119 | // top right 120 | // bottom left 121 | // bottom right 122 | 123 | getImageMatrix().postTranslate(event.getX() - lastPoint.x, 124 | event.getY() - lastPoint.y); 125 | } else { 126 | lastPoint = new PointF(); 127 | } 128 | lastPoint.set(event.getX(), event.getY()); 129 | 130 | } 131 | postInvalidate(); 132 | } 133 | 134 | break; 135 | case MotionEvent.ACTION_CANCEL: 136 | break; 137 | case MotionEvent.ACTION_UP: 138 | break; 139 | case MotionEvent.ACTION_POINTER_UP: 140 | if (resizing) { 141 | TouchMode = MODE_DRAG; 142 | } else { 143 | TouchMode = MODE_DRAG; 144 | } 145 | lastPoint=null; 146 | break; 147 | default: 148 | break; 149 | } 150 | detector.onTouchEvent(event); 151 | return flag; 152 | } 153 | 154 | public float distance(MotionEvent event) { 155 | float a = event.getX(1) - event.getX(0); 156 | float b = event.getY(1) - event.getY(0); 157 | return (float) Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2)); 158 | } 159 | 160 | private Point centerPoint = new Point(); 161 | 162 | private void getCenter(MotionEvent event) { 163 | float a = event.getX(1) + event.getX(0); 164 | float b = event.getY(1) + event.getY(0); 165 | centerPoint.set((int) a / 2, (int) b / 2); 166 | } 167 | 168 | public float distance(PointF pointF, PointF pointF2) { 169 | return PointF.length(pointF.x - pointF2.x, pointF.y - pointF2.y); 170 | } 171 | 172 | GestureDetector detector; 173 | 174 | public void load(File file, int w, int h) { 175 | setBackgroundColor(Color.TRANSPARENT); 176 | detector = new GestureDetector(getContext(), new GestureListener()); 177 | 178 | final BitmapFactory.Options options = new BitmapFactory.Options(); 179 | options.inJustDecodeBounds = true; 180 | BitmapFactory.decodeFile(file.getAbsolutePath(), options); 181 | options.inJustDecodeBounds = false; 182 | float hei = options.outHeight; 183 | float wid = options.outWidth; 184 | 185 | float r = 1; 186 | if (wid > w || hei > h) { 187 | float beWidth = wid / w; 188 | float beHeight = hei / h; 189 | if (beWidth < beHeight) { 190 | r = beHeight; 191 | } else { 192 | r = beWidth; 193 | } 194 | } 195 | initWidth = (int) (wid / r); 196 | initHeight = h; 197 | initScale = 1 / r; 198 | if (r < 1) { 199 | r = 1; 200 | } 201 | LoadBMPTask task = new LoadBMPTask(); 202 | task.execute(file.getAbsolutePath()); 203 | } 204 | 205 | class LoadBMPTask extends AsyncTask { 206 | 207 | @Override 208 | protected Bitmap doInBackground(String... params) { 209 | return BitmapFactory.decodeFile(params[0]); 210 | } 211 | 212 | @Override 213 | protected void onPostExecute(Bitmap result) { 214 | theBitmap = result; 215 | setImageBitmap(theBitmap); 216 | getImageMatrix().getValues(initValues); 217 | initMatrix.set(getImageMatrix()); 218 | } 219 | } 220 | 221 | public void resetMatrix() { 222 | if (TouchMode == MODE_NORMAL) { 223 | getImageMatrix().postScale(2, 2, getWidth() / 2, getHeight() / 2); 224 | TouchMode = MODE_DRAG; 225 | } else { 226 | getImageMatrix().set(initMatrix); 227 | TouchMode = MODE_NORMAL; 228 | } 229 | 230 | postInvalidate(); 231 | } 232 | 233 | class GestureListener extends SimpleOnGestureListener { 234 | public GestureListener() { 235 | 236 | } 237 | 238 | @Override 239 | public boolean onDoubleTap(MotionEvent e) { 240 | resetMatrix(); 241 | return super.onDoubleTap(e); 242 | } 243 | } 244 | 245 | } 246 | --------------------------------------------------------------------------------