├── .gitignore
├── LICENSE
├── README.md
├── hook
└── apply-filechooser-patch
├── patch
└── android-platform-filechooser.patch
├── plugin.xml
├── src
└── android
│ ├── FileChooser.java
│ └── filechooser
│ ├── FileChooserActivity.java
│ ├── FileListAdapter.java
│ ├── FileListFragment.java
│ ├── FileLoader.java
│ ├── FileUtils.java
│ ├── LocalStorageProvider.java
│ ├── libs
│ └── android-support-v4.jar
│ └── res
│ ├── drawable-hdpi
│ ├── ic_chooser.png
│ ├── ic_file.png
│ ├── ic_folder.png
│ └── ic_provider.png
│ ├── drawable-mdpi
│ ├── ic_chooser.png
│ ├── ic_file.png
│ ├── ic_folder.png
│ └── ic_provider.png
│ ├── drawable-xhdpi
│ ├── ic_chooser.png
│ ├── ic_file.png
│ ├── ic_folder.png
│ └── ic_provider.png
│ ├── drawable-xxhdpi
│ ├── ic_chooser.png
│ ├── ic_file.png
│ ├── ic_folder.png
│ └── ic_provider.png
│ ├── layout
│ └── file.xml
│ ├── values-ca
│ └── strings.xml
│ ├── values-de
│ └── strings.xml
│ ├── values-es
│ └── strings.xml
│ ├── values-fr
│ └── strings.xml
│ ├── values-ga
│ └── strings.xml
│ ├── values-it
│ └── strings.xml
│ ├── values-ko
│ └── strings.xml
│ ├── values-pl
│ └── strings.xml
│ ├── values-pt-rBR
│ └── strings.xml
│ ├── values-ru
│ └── strings.xml
│ ├── values-v11
│ └── strings.xml
│ ├── values-v19
│ └── bool.xml
│ ├── values
│ ├── bool.xml
│ ├── dimens.xml
│ └── styles.xml
│ └── xml
│ └── mimetypes.xml
└── www
└── filechooser.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # built application files
2 | *.apk
3 | *.ap_
4 |
5 | # files for the dex VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # generated files
12 | bin/
13 | gen/
14 |
15 | # Local configuration file (sdk path, etc)
16 | local.properties
17 |
18 | # Eclipse project files
19 | .classpath
20 | .project
21 |
22 | # Proguard folder generated by Eclipse
23 | proguard/
24 |
25 | # Intellij project files
26 | *.iml
27 | *.ipr
28 | *.iws
29 | .idea/
30 |
31 | .DS_Store
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Cesidio DiBenedetto
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FileChooser Cordova plugin for Android (mainly KitKat)
2 |
3 | This plugin was created as a workaround for https://issues.apache.org/jira/browse/CB-5294 and https://code.google.com/p/android/issues/detail?id=62220. This plugin is mainly configured for Android 4.4 so I would recommend to continue to use the native file dialogs for earlier versions of Android. There might be issues with the plugin as I have not fully tested all possible scenarios on many devices, but I have installed it on a Nexus 5 and it worked fine.
4 |
5 |
6 | The core pieces of the code were taken from https://github.com/iPaulPro/aFileChooser. A huge thanks to him!!! All I did was write the plugin wrapper around it. Please take note of the second part of the `Setup` step from the aforementioned **[aFileChooser](https://github.com/iPaulPro/aFileChooser)**.
7 | ```
8 | Note that like a ContentProvider, the DocumentProvider authority must be unique.
9 | You should change com.ianhanniballake.localstorage.documents in your Manifest, as
10 | well as the LocalStorageProvider.AUTHORITY field.
11 | ```
12 |
13 |
14 | ### Installation
15 | ```
16 | cordova plugin add https://github.com/cdibened/filechooser.git
17 | ```
18 |
19 | ### Configuration
20 |
21 | You will have to `import your.package.name.R` into the following java files.
22 |
23 | ```
24 | FileChooser.java
25 | FileChooserActivity.java
26 | FileListAdapter.java
27 | FileListFragment.java
28 | LocalStorageProvider.java
29 | ```
30 |
31 | ### Usage
32 |
33 | The first argument, which will eventually be filechooser parameters such as multi-select, mime-types...., is currently ignored but must be passed in.
34 |
35 | ```
36 | var success = function(data) {
37 | console.log( data.filepath );
38 | };
39 |
40 | var error = function(msg) {
41 | console.log( msg );
42 | };
43 |
44 | filechooser.open({},success,error);
45 | ```
46 |
47 | ### Example
48 |
49 | ```
50 | var success = function( data ) {
51 | var filepath = data.filepath;
52 | function win(r) {
53 | console.log("Code = " + r.responseCode);
54 | console.log("Response = " + r.response);
55 | console.log("Sent = " + r.bytesSent);
56 | }
57 |
58 | function fail(error) {
59 | console.log("An error has occurred: Code = " + error.code);
60 | console.log("upload error source " + error.source);
61 | console.log("upload error target " + error.target);
62 | }
63 |
64 | var uri = encodeURI("http://localhost/upload/processupload.php");
65 | var options = new FileUploadOptions();
66 | options.fileKey="file";
67 | options.fileName=filepath.substr(filepath.lastIndexOf('/')+1);
68 |
69 | var ft = new FileTransfer();
70 | ft.onprogress = function(progressEvent) {
71 | if (progressEvent.lengthComputable) {
72 | loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total);
73 | }
74 | else {
75 | loadingStatus.increment();
76 | }
77 | };
78 |
79 | ft.upload(filepath, uri, win, fail, options);
80 | };
81 |
82 | var error = function( msg ) {
83 | console.log( msg );
84 | };
85 |
86 |
87 |
88 | if( device.platform.toLowerCase() === 'android' && device.version.indexOf( '4.4' ) === 0 ) {
89 | $('#fileinput').click( function(e) {
90 | filechooser.open( {}, success, error );
91 | });
92 | }
93 | ```
94 |
95 |
96 | ### Next up
97 | - automatically `import your.package.name.R` in the java files
98 | - add support for parameters in the filechooser
99 |
--------------------------------------------------------------------------------
/hook/apply-filechooser-patch:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -e
3 |
4 | patch_file='plugin/com.cesidiodibenedetto.filechooser/patch/android-platform-filechooser.patch'
5 | package="$(sed -rn '/^ /dev/null
12 | then
13 | echo "Missing 'patch' command." >&2
14 | exit 1
15 | fi
16 |
17 | if gen_patch | ( cd platforms/android && patch -p1 --dry-run --batch --forward --silent >/dev/null )
18 | then
19 | gen_patch | ( cd platforms/android && patch -p1 --batch --forward -r - )
20 | else
21 | echo "Skipping patches, they are already applied or broken." >&2
22 | fi
23 |
24 |
--------------------------------------------------------------------------------
/patch/android-platform-filechooser.patch:
--------------------------------------------------------------------------------
1 | diff -ruw android.orig/src/com/cesidiodibenedetto/filechooser/FileChooser.java android/src/com/cesidiodibenedetto/filechooser/FileChooser.java
2 | --- android.orig/src/com/cesidiodibenedetto/filechooser/FileChooser.java 2015-06-22 18:55:47.000000000 +0200
3 | +++ android/src/com/cesidiodibenedetto/filechooser/FileChooser.java 2015-06-22 19:01:52.908404787 +0200
4 | @@ -22,6 +22,8 @@
5 |
6 | import com.ipaulpro.afilechooser.utils.FileUtils;
7 |
8 | +import PACKAGE.R;
9 | +
10 | /**
11 | * FileChooser is a PhoneGap plugin that acts as polyfill for Android KitKat and web
12 | * applications that need support for
13 | diff -ruw android.orig/src/com/ianhanniballake/localstorage/LocalStorageProvider.java android/src/com/ianhanniballake/localstorage/LocalStorageProvider.java
14 | --- android.orig/src/com/ianhanniballake/localstorage/LocalStorageProvider.java 2015-06-22 18:55:47.000000000 +0200
15 | +++ android/src/com/ianhanniballake/localstorage/LocalStorageProvider.java 2015-06-22 19:01:52.900404713 +0200
16 | @@ -21,6 +21,8 @@
17 | import java.io.FileOutputStream;
18 | import java.io.IOException;
19 |
20 | +import PACKAGE.R;
21 | +
22 | public class LocalStorageProvider extends DocumentsProvider {
23 |
24 | public static final String AUTHORITY = "com.ianhanniballake.localstorage.documents";
25 | diff -ruw android.orig/src/com/ipaulpro/afilechooser/FileChooserActivity.java android/src/com/ipaulpro/afilechooser/FileChooserActivity.java
26 | --- android.orig/src/com/ipaulpro/afilechooser/FileChooserActivity.java 2015-06-22 18:55:47.000000000 +0200
27 | +++ android/src/com/ipaulpro/afilechooser/FileChooserActivity.java 2015-06-22 18:59:58.815348192 +0200
28 | @@ -36,6 +36,8 @@
29 |
30 | import java.io.File;
31 |
32 | +import PACKAGE.R;
33 | +
34 | /**
35 | * Main Activity that handles the FileListFragments
36 | *
37 | diff -ruw android.orig/src/com/ipaulpro/afilechooser/FileListAdapter.java android/src/com/ipaulpro/afilechooser/FileListAdapter.java
38 | --- android.orig/src/com/ipaulpro/afilechooser/FileListAdapter.java 2015-06-22 18:55:47.000000000 +0200
39 | +++ android/src/com/ipaulpro/afilechooser/FileListAdapter.java 2015-06-22 18:59:03.178832147 +0200
40 | @@ -27,6 +27,8 @@
41 | import java.util.ArrayList;
42 | import java.util.List;
43 |
44 | +import PACKAGE.R;
45 | +
46 | /**
47 | * List adapter for Files.
48 | *
49 | diff -ruw android.orig/src/com/ipaulpro/afilechooser/FileListFragment.java android/src/com/ipaulpro/afilechooser/FileListFragment.java
50 | --- android.orig/src/com/ipaulpro/afilechooser/FileListFragment.java 2015-06-22 18:55:47.000000000 +0200
51 | +++ android/src/com/ipaulpro/afilechooser/FileListFragment.java 2015-06-22 18:59:40.251176067 +0200
52 | @@ -28,6 +28,8 @@
53 | import java.io.File;
54 | import java.util.List;
55 |
56 | +import PACKAGE.R;
57 | +
58 | /**
59 | * Fragment that displays a list of Files in a given path.
60 | *
61 | diff -ruw android.orig/src/com/ipaulpro/afilechooser/FileLoader.java android/src/com/ipaulpro/afilechooser/FileLoader.java
62 | --- android.orig/src/com/ipaulpro/afilechooser/FileLoader.java 2015-06-22 18:55:47.000000000 +0200
63 | +++ android/src/com/ipaulpro/afilechooser/FileLoader.java 2015-06-22 19:00:41.147740468 +0200
64 | @@ -27,6 +27,8 @@
65 | import java.util.Arrays;
66 | import java.util.List;
67 |
68 | +import PACKAGE.R;
69 | +
70 | /**
71 | * Loader that returns a list of Files in a given file path.
72 | *
73 | diff -ruw android.orig/src/com/ipaulpro/afilechooser/utils/FileUtils.java android/src/com/ipaulpro/afilechooser/utils/FileUtils.java
74 | --- android.orig/src/com/ipaulpro/afilechooser/utils/FileUtils.java 2015-06-22 18:55:47.000000000 +0200
75 | +++ android/src/com/ipaulpro/afilechooser/utils/FileUtils.java 2015-06-22 19:00:49.395816864 +0200
76 | @@ -38,6 +38,8 @@
77 | import java.text.DecimalFormat;
78 | import java.util.Comparator;
79 |
80 | +import PACKAGE.R;
81 | +
82 | /**
83 | * @version 2009-07-03
84 | * @author Peli
85 |
--------------------------------------------------------------------------------
/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 | FileChooser
7 | File Chooser for Cordova to overcome Android 4.4 issues outlined here: https://issues.apache.org/jira/browse/CB-5294 and https://code.google.com/p/android/issues/detail?id=62220
8 | MIT
9 | cordova,filechooser,android
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | Empty Directory
25 | Storage was removed or unmounted.
26 | Select a file
27 | File Browser
28 | Error selecting File
29 | Internal storage
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/src/android/FileChooser.java:
--------------------------------------------------------------------------------
1 | package com.cesidiodibenedetto.filechooser;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | import org.apache.cordova.CordovaActivity;
7 | import org.json.JSONArray;
8 | import org.json.JSONException;
9 | import org.json.JSONObject;
10 |
11 | import android.app.Activity;
12 | import android.content.ActivityNotFoundException;
13 | import android.content.Intent;
14 | import android.net.Uri;
15 | import android.os.Bundle;
16 | import android.util.Log;
17 |
18 | import org.apache.cordova.CallbackContext;
19 | import org.apache.cordova.CordovaPlugin;
20 | import org.apache.cordova.CordovaResourceApi;
21 | import org.apache.cordova.PluginResult;
22 |
23 | import com.ipaulpro.afilechooser.utils.FileUtils;
24 |
25 | /**
26 | * FileChooser is a PhoneGap plugin that acts as polyfill for Android KitKat and web
27 | * applications that need support for
28 | *
29 | */
30 | public class FileChooser extends CordovaPlugin {
31 |
32 | private CallbackContext callbackContext = null;
33 | private static final String TAG = "FileChooser";
34 | private static final int REQUEST_CODE = 6666; // onActivityResult request code
35 |
36 | private void showFileChooser() {
37 | // Use the GET_CONTENT intent from the utility class
38 | Intent target = FileUtils.createGetContentIntent();
39 | // Create the chooser Intent
40 | Intent intent = Intent.createChooser(
41 | target, this.cordova.getActivity().getString(R.string.chooser_title));
42 | try {
43 | this.cordova.startActivityForResult((CordovaPlugin) this, intent, REQUEST_CODE);
44 | } catch (ActivityNotFoundException e) {
45 | // The reason for the existence of aFileChooser
46 | }
47 | }
48 |
49 | @Override
50 | public void onActivityResult(int requestCode, int resultCode, Intent data) {
51 | if( requestCode == REQUEST_CODE) {
52 | // If the file selection was successful
53 | if (resultCode == Activity.RESULT_OK) {
54 | if (data != null) {
55 | // Get the URI of the selected file
56 | final Uri uri = data.getData();
57 | Log.i(TAG, "Uri = " + uri.toString());
58 | JSONObject obj = new JSONObject();
59 | try {
60 | // Get the file path from the URI
61 | final String path = FileUtils.getPath(this.cordova.getActivity(), uri);
62 | obj.put("filepath", path);
63 | this.callbackContext.success(obj);
64 | } catch (Exception e) {
65 | Log.e("FileChooser", "File select error", e);
66 | this.callbackContext.error(e.getMessage());
67 | }
68 | }
69 | }
70 | }
71 | }
72 |
73 | @Override
74 | public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
75 | this.callbackContext = callbackContext;
76 | if (action.equals("open")) {
77 | showFileChooser();
78 | return true;
79 | }
80 | else {
81 | return false;
82 | }
83 | }
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/src/android/filechooser/FileChooserActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Paul Burke
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 com.ipaulpro.afilechooser;
18 |
19 | import android.app.ActionBar;
20 | import android.content.BroadcastReceiver;
21 | import android.content.Context;
22 | import android.content.Intent;
23 | import android.content.IntentFilter;
24 | import android.net.Uri;
25 | import android.os.Build;
26 | import android.os.Bundle;
27 | import android.os.Environment;
28 | import android.support.v4.app.FragmentActivity;
29 | import android.support.v4.app.FragmentManager;
30 | import android.support.v4.app.FragmentManager.BackStackEntry;
31 | import android.support.v4.app.FragmentManager.OnBackStackChangedListener;
32 | import android.support.v4.app.FragmentTransaction;
33 | import android.view.Menu;
34 | import android.view.MenuItem;
35 | import android.widget.Toast;
36 |
37 | import java.io.File;
38 |
39 | /**
40 | * Main Activity that handles the FileListFragments
41 | *
42 | * @version 2013-06-25
43 | * @author paulburke (ipaulpro)
44 | */
45 | public class FileChooserActivity extends FragmentActivity implements
46 | OnBackStackChangedListener, FileListFragment.Callbacks {
47 |
48 | public static final String PATH = "path";
49 | public static final String EXTERNAL_BASE_PATH = Environment
50 | .getExternalStorageDirectory().getAbsolutePath();
51 |
52 | private static final boolean HAS_ACTIONBAR = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
53 |
54 | private FragmentManager mFragmentManager;
55 | private BroadcastReceiver mStorageListener = new BroadcastReceiver() {
56 | @Override
57 | public void onReceive(Context context, Intent intent) {
58 | Toast.makeText(context, R.string.storage_removed, Toast.LENGTH_LONG).show();
59 | finishWithResult(null);
60 | }
61 | };
62 |
63 | private String mPath;
64 |
65 | @Override
66 | protected void onCreate(Bundle savedInstanceState) {
67 | super.onCreate(savedInstanceState);
68 |
69 | mFragmentManager = getSupportFragmentManager();
70 | mFragmentManager.addOnBackStackChangedListener(this);
71 |
72 | if (savedInstanceState == null) {
73 | mPath = EXTERNAL_BASE_PATH;
74 | addFragment();
75 | } else {
76 | mPath = savedInstanceState.getString(PATH);
77 | }
78 |
79 | setTitle(mPath);
80 | }
81 |
82 | @Override
83 | protected void onPause() {
84 | super.onPause();
85 |
86 | unregisterStorageListener();
87 | }
88 |
89 | @Override
90 | protected void onResume() {
91 | super.onResume();
92 |
93 | registerStorageListener();
94 | }
95 |
96 | @Override
97 | protected void onSaveInstanceState(Bundle outState) {
98 | super.onSaveInstanceState(outState);
99 |
100 | outState.putString(PATH, mPath);
101 | }
102 |
103 | @Override
104 | public void onBackStackChanged() {
105 |
106 | int count = mFragmentManager.getBackStackEntryCount();
107 | if (count > 0) {
108 | BackStackEntry fragment = mFragmentManager.getBackStackEntryAt(count - 1);
109 | mPath = fragment.getName();
110 | } else {
111 | mPath = EXTERNAL_BASE_PATH;
112 | }
113 |
114 | setTitle(mPath);
115 | if (HAS_ACTIONBAR)
116 | invalidateOptionsMenu();
117 | }
118 |
119 | @Override
120 | public boolean onCreateOptionsMenu(Menu menu) {
121 | if (HAS_ACTIONBAR) {
122 | boolean hasBackStack = mFragmentManager.getBackStackEntryCount() > 0;
123 |
124 | ActionBar actionBar = getActionBar();
125 | actionBar.setDisplayHomeAsUpEnabled(hasBackStack);
126 | actionBar.setHomeButtonEnabled(hasBackStack);
127 | }
128 |
129 | return true;
130 | }
131 |
132 | @Override
133 | public boolean onOptionsItemSelected(MenuItem item) {
134 | switch (item.getItemId()) {
135 | case android.R.id.home:
136 | mFragmentManager.popBackStack();
137 | return true;
138 | }
139 |
140 | return super.onOptionsItemSelected(item);
141 | }
142 |
143 | /**
144 | * Add the initial Fragment with given path.
145 | */
146 | private void addFragment() {
147 | FileListFragment fragment = FileListFragment.newInstance(mPath);
148 | mFragmentManager.beginTransaction()
149 | .add(android.R.id.content, fragment).commit();
150 | }
151 |
152 | /**
153 | * "Replace" the existing Fragment with a new one using given path. We're
154 | * really adding a Fragment to the back stack.
155 | *
156 | * @param file The file (directory) to display.
157 | */
158 | private void replaceFragment(File file) {
159 | mPath = file.getAbsolutePath();
160 |
161 | FileListFragment fragment = FileListFragment.newInstance(mPath);
162 | mFragmentManager.beginTransaction()
163 | .replace(android.R.id.content, fragment)
164 | .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
165 | .addToBackStack(mPath).commit();
166 | }
167 |
168 | /**
169 | * Finish this Activity with a result code and URI of the selected file.
170 | *
171 | * @param file The file selected.
172 | */
173 | private void finishWithResult(File file) {
174 | if (file != null) {
175 | Uri uri = Uri.fromFile(file);
176 | setResult(RESULT_OK, new Intent().setData(uri));
177 | finish();
178 | } else {
179 | setResult(RESULT_CANCELED);
180 | finish();
181 | }
182 | }
183 |
184 | /**
185 | * Called when the user selects a File
186 | *
187 | * @param file The file that was selected
188 | */
189 | @Override
190 | public void onFileSelected(File file) {
191 | if (file != null) {
192 | if (file.isDirectory()) {
193 | replaceFragment(file);
194 | } else {
195 | finishWithResult(file);
196 | }
197 | } else {
198 | Toast.makeText(FileChooserActivity.this, R.string.error_selecting_file,
199 | Toast.LENGTH_SHORT).show();
200 | }
201 | }
202 |
203 | /**
204 | * Register the external storage BroadcastReceiver.
205 | */
206 | private void registerStorageListener() {
207 | IntentFilter filter = new IntentFilter();
208 | filter.addAction(Intent.ACTION_MEDIA_REMOVED);
209 | registerReceiver(mStorageListener, filter);
210 | }
211 |
212 | /**
213 | * Unregister the external storage BroadcastReceiver.
214 | */
215 | private void unregisterStorageListener() {
216 | unregisterReceiver(mStorageListener);
217 | }
218 | }
219 |
--------------------------------------------------------------------------------
/src/android/filechooser/FileListAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Paul Burke
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 com.ipaulpro.afilechooser;
18 |
19 | import android.content.Context;
20 | import android.view.LayoutInflater;
21 | import android.view.View;
22 | import android.view.ViewGroup;
23 | import android.widget.BaseAdapter;
24 | import android.widget.TextView;
25 |
26 | import java.io.File;
27 | import java.util.ArrayList;
28 | import java.util.List;
29 |
30 | /**
31 | * List adapter for Files.
32 | *
33 | * @version 2013-12-11
34 | * @author paulburke (ipaulpro)
35 | */
36 | public class FileListAdapter extends BaseAdapter {
37 |
38 | private final static int ICON_FOLDER = R.drawable.ic_folder;
39 | private final static int ICON_FILE = R.drawable.ic_file;
40 |
41 | private final LayoutInflater mInflater;
42 |
43 | private List mData = new ArrayList();
44 |
45 | public FileListAdapter(Context context) {
46 | mInflater = LayoutInflater.from(context);
47 | }
48 |
49 | public void add(File file) {
50 | mData.add(file);
51 | notifyDataSetChanged();
52 | }
53 |
54 | public void remove(File file) {
55 | mData.remove(file);
56 | notifyDataSetChanged();
57 | }
58 |
59 | public void insert(File file, int index) {
60 | mData.add(index, file);
61 | notifyDataSetChanged();
62 | }
63 |
64 | public void clear() {
65 | mData.clear();
66 | notifyDataSetChanged();
67 | }
68 |
69 | @Override
70 | public File getItem(int position) {
71 | return mData.get(position);
72 | }
73 |
74 | @Override
75 | public long getItemId(int position) {
76 | return position;
77 | }
78 |
79 | @Override
80 | public int getCount() {
81 | return mData.size();
82 | }
83 |
84 | public List getListItems() {
85 | return mData;
86 | }
87 |
88 | /**
89 | * Set the list items without notifying on the clear. This prevents loss of
90 | * scroll position.
91 | *
92 | * @param data
93 | */
94 | public void setListItems(List data) {
95 | mData = data;
96 | notifyDataSetChanged();
97 | }
98 |
99 | @Override
100 | public View getView(int position, View convertView, ViewGroup parent) {
101 | View row = convertView;
102 |
103 | if (row == null)
104 | row = mInflater.inflate(R.layout.file, parent, false);
105 |
106 | TextView view = (TextView) row;
107 |
108 | // Get the file at the current position
109 | final File file = getItem(position);
110 |
111 | // Set the TextView as the file name
112 | view.setText(file.getName());
113 |
114 | // If the item is not a directory, use the file icon
115 | int icon = file.isDirectory() ? ICON_FOLDER : ICON_FILE;
116 | view.setCompoundDrawablesWithIntrinsicBounds(icon, 0, 0, 0);
117 |
118 | return row;
119 | }
120 |
121 | }
--------------------------------------------------------------------------------
/src/android/filechooser/FileListFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Paul Burke
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 com.ipaulpro.afilechooser;
18 |
19 | import android.app.Activity;
20 | import android.os.Bundle;
21 | import android.os.Environment;
22 | import android.support.v4.app.ListFragment;
23 | import android.support.v4.app.LoaderManager;
24 | import android.support.v4.content.Loader;
25 | import android.view.View;
26 | import android.widget.ListView;
27 |
28 | import java.io.File;
29 | import java.util.List;
30 |
31 | /**
32 | * Fragment that displays a list of Files in a given path.
33 | *
34 | * @version 2013-12-11
35 | * @author paulburke (ipaulpro)
36 | */
37 | public class FileListFragment extends ListFragment implements
38 | LoaderManager.LoaderCallbacks> {
39 |
40 | /**
41 | * Interface to listen for events.
42 | */
43 | public interface Callbacks {
44 | /**
45 | * Called when a file is selected from the list.
46 | *
47 | * @param file The file selected
48 | */
49 | public void onFileSelected(File file);
50 | }
51 |
52 | private static final int LOADER_ID = 0;
53 |
54 | private FileListAdapter mAdapter;
55 | private String mPath;
56 |
57 | private Callbacks mListener;
58 |
59 | /**
60 | * Create a new instance with the given file path.
61 | *
62 | * @param path The absolute path of the file (directory) to display.
63 | * @return A new Fragment with the given file path.
64 | */
65 | public static FileListFragment newInstance(String path) {
66 | FileListFragment fragment = new FileListFragment();
67 | Bundle args = new Bundle();
68 | args.putString(FileChooserActivity.PATH, path);
69 | fragment.setArguments(args);
70 |
71 | return fragment;
72 | }
73 |
74 | @Override
75 | public void onAttach(Activity activity) {
76 | super.onAttach(activity);
77 |
78 | try {
79 | mListener = (Callbacks) activity;
80 | } catch (ClassCastException e) {
81 | throw new ClassCastException(activity.toString()
82 | + " must implement FileListFragment.Callbacks");
83 | }
84 | }
85 |
86 | @Override
87 | public void onCreate(Bundle savedInstanceState) {
88 | super.onCreate(savedInstanceState);
89 |
90 | mAdapter = new FileListAdapter(getActivity());
91 | mPath = getArguments() != null ? getArguments().getString(
92 | FileChooserActivity.PATH) : Environment
93 | .getExternalStorageDirectory().getAbsolutePath();
94 | }
95 |
96 | @Override
97 | public void onActivityCreated(Bundle savedInstanceState) {
98 | setEmptyText(getString(R.string.empty_directory));
99 | setListAdapter(mAdapter);
100 | setListShown(false);
101 |
102 | getLoaderManager().initLoader(LOADER_ID, null, this);
103 |
104 | super.onActivityCreated(savedInstanceState);
105 | }
106 |
107 | @Override
108 | public void onListItemClick(ListView l, View v, int position, long id) {
109 | FileListAdapter adapter = (FileListAdapter) l.getAdapter();
110 | if (adapter != null) {
111 | File file = (File) adapter.getItem(position);
112 | mPath = file.getAbsolutePath();
113 | mListener.onFileSelected(file);
114 | }
115 | }
116 |
117 | @Override
118 | public Loader> onCreateLoader(int id, Bundle args) {
119 | return new FileLoader(getActivity(), mPath);
120 | }
121 |
122 | @Override
123 | public void onLoadFinished(Loader> loader, List data) {
124 | mAdapter.setListItems(data);
125 |
126 | if (isResumed())
127 | setListShown(true);
128 | else
129 | setListShownNoAnimation(true);
130 | }
131 |
132 | @Override
133 | public void onLoaderReset(Loader> loader) {
134 | mAdapter.clear();
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/src/android/filechooser/FileLoader.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 Paul Burke
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 com.ipaulpro.afilechooser;
18 |
19 | import android.content.Context;
20 | import android.os.FileObserver;
21 | import android.support.v4.content.AsyncTaskLoader;
22 |
23 | import com.ipaulpro.afilechooser.utils.FileUtils;
24 |
25 | import java.io.File;
26 | import java.util.ArrayList;
27 | import java.util.Arrays;
28 | import java.util.List;
29 |
30 | /**
31 | * Loader that returns a list of Files in a given file path.
32 | *
33 | * @version 2013-12-11
34 | * @author paulburke (ipaulpro)
35 | */
36 | public class FileLoader extends AsyncTaskLoader> {
37 |
38 | private static final int FILE_OBSERVER_MASK = FileObserver.CREATE
39 | | FileObserver.DELETE | FileObserver.DELETE_SELF
40 | | FileObserver.MOVED_FROM | FileObserver.MOVED_TO
41 | | FileObserver.MODIFY | FileObserver.MOVE_SELF;
42 |
43 | private FileObserver mFileObserver;
44 |
45 | private List mData;
46 | private String mPath;
47 |
48 | public FileLoader(Context context, String path) {
49 | super(context);
50 | this.mPath = path;
51 | }
52 |
53 | @Override
54 | public List loadInBackground() {
55 |
56 | ArrayList list = new ArrayList();
57 |
58 | // Current directory File instance
59 | final File pathDir = new File(mPath);
60 |
61 | // List file in this directory with the directory filter
62 | final File[] dirs = pathDir.listFiles(FileUtils.sDirFilter);
63 | if (dirs != null) {
64 | // Sort the folders alphabetically
65 | Arrays.sort(dirs, FileUtils.sComparator);
66 | // Add each folder to the File list for the list adapter
67 | for (File dir : dirs)
68 | list.add(dir);
69 | }
70 |
71 | // List file in this directory with the file filter
72 | final File[] files = pathDir.listFiles(FileUtils.sFileFilter);
73 | if (files != null) {
74 | // Sort the files alphabetically
75 | Arrays.sort(files, FileUtils.sComparator);
76 | // Add each file to the File list for the list adapter
77 | for (File file : files)
78 | list.add(file);
79 | }
80 |
81 | return list;
82 | }
83 |
84 | @Override
85 | public void deliverResult(List data) {
86 | if (isReset()) {
87 | onReleaseResources(data);
88 | return;
89 | }
90 |
91 | List oldData = mData;
92 | mData = data;
93 |
94 | if (isStarted())
95 | super.deliverResult(data);
96 |
97 | if (oldData != null && oldData != data)
98 | onReleaseResources(oldData);
99 | }
100 |
101 | @Override
102 | protected void onStartLoading() {
103 | if (mData != null)
104 | deliverResult(mData);
105 |
106 | if (mFileObserver == null) {
107 | mFileObserver = new FileObserver(mPath, FILE_OBSERVER_MASK) {
108 | @Override
109 | public void onEvent(int event, String path) {
110 | onContentChanged();
111 | }
112 | };
113 | }
114 | mFileObserver.startWatching();
115 |
116 | if (takeContentChanged() || mData == null)
117 | forceLoad();
118 | }
119 |
120 | @Override
121 | protected void onStopLoading() {
122 | cancelLoad();
123 | }
124 |
125 | @Override
126 | protected void onReset() {
127 | onStopLoading();
128 |
129 | if (mData != null) {
130 | onReleaseResources(mData);
131 | mData = null;
132 | }
133 | }
134 |
135 | @Override
136 | public void onCanceled(List data) {
137 | super.onCanceled(data);
138 |
139 | onReleaseResources(data);
140 | }
141 |
142 | protected void onReleaseResources(List data) {
143 |
144 | if (mFileObserver != null) {
145 | mFileObserver.stopWatching();
146 | mFileObserver = null;
147 | }
148 | }
149 | }
--------------------------------------------------------------------------------
/src/android/filechooser/FileUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2007-2008 OpenIntents.org
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 com.ipaulpro.afilechooser.utils;
18 |
19 | import android.content.ContentResolver;
20 | import android.content.ContentUris;
21 | import android.content.Context;
22 | import android.content.Intent;
23 | import android.database.Cursor;
24 | import android.database.DatabaseUtils;
25 | import android.graphics.Bitmap;
26 | import android.net.Uri;
27 | import android.os.Build;
28 | import android.os.Environment;
29 | import android.provider.DocumentsContract;
30 | import android.provider.MediaStore;
31 | import android.util.Log;
32 | import android.webkit.MimeTypeMap;
33 |
34 | import com.ianhanniballake.localstorage.LocalStorageProvider;
35 |
36 | import java.io.File;
37 | import java.io.FileFilter;
38 | import java.text.DecimalFormat;
39 | import java.util.Comparator;
40 |
41 | /**
42 | * @version 2009-07-03
43 | * @author Peli
44 | * @version 2013-12-11
45 | * @author paulburke (ipaulpro)
46 | */
47 | public class FileUtils {
48 | private FileUtils() {} //private constructor to enforce Singleton pattern
49 |
50 | /** TAG for log messages. */
51 | static final String TAG = "FileUtils";
52 | private static final boolean DEBUG = false; // Set to true to enable logging
53 |
54 | public static final String MIME_TYPE_AUDIO = "audio/*";
55 | public static final String MIME_TYPE_TEXT = "text/*";
56 | public static final String MIME_TYPE_IMAGE = "image/*";
57 | public static final String MIME_TYPE_VIDEO = "video/*";
58 | public static final String MIME_TYPE_APP = "application/*";
59 |
60 | public static final String HIDDEN_PREFIX = ".";
61 |
62 | /**
63 | * Gets the extension of a file name, like ".png" or ".jpg".
64 | *
65 | * @param uri
66 | * @return Extension including the dot("."); "" if there is no extension;
67 | * null if uri was null.
68 | */
69 | public static String getExtension(String uri) {
70 | if (uri == null) {
71 | return null;
72 | }
73 |
74 | int dot = uri.lastIndexOf(".");
75 | if (dot >= 0) {
76 | return uri.substring(dot);
77 | } else {
78 | // No extension.
79 | return "";
80 | }
81 | }
82 |
83 | /**
84 | * @return Whether the URI is a local one.
85 | */
86 | public static boolean isLocal(String url) {
87 | if (url != null && !url.startsWith("http://") && !url.startsWith("https://")) {
88 | return true;
89 | }
90 | return false;
91 | }
92 |
93 | /**
94 | * @return True if Uri is a MediaStore Uri.
95 | * @author paulburke
96 | */
97 | public static boolean isMediaUri(Uri uri) {
98 | return "media".equalsIgnoreCase(uri.getAuthority());
99 | }
100 |
101 | /**
102 | * Convert File into Uri.
103 | *
104 | * @param file
105 | * @return uri
106 | */
107 | public static Uri getUri(File file) {
108 | if (file != null) {
109 | return Uri.fromFile(file);
110 | }
111 | return null;
112 | }
113 |
114 | /**
115 | * Returns the path only (without file name).
116 | *
117 | * @param file
118 | * @return
119 | */
120 | public static File getPathWithoutFilename(File file) {
121 | if (file != null) {
122 | if (file.isDirectory()) {
123 | // no file to be split off. Return everything
124 | return file;
125 | } else {
126 | String filename = file.getName();
127 | String filepath = file.getAbsolutePath();
128 |
129 | // Construct path without file name.
130 | String pathwithoutname = filepath.substring(0,
131 | filepath.length() - filename.length());
132 | if (pathwithoutname.endsWith("/")) {
133 | pathwithoutname = pathwithoutname.substring(0, pathwithoutname.length() - 1);
134 | }
135 | return new File(pathwithoutname);
136 | }
137 | }
138 | return null;
139 | }
140 |
141 | /**
142 | * @return The MIME type for the given file.
143 | */
144 | public static String getMimeType(File file) {
145 |
146 | String extension = getExtension(file.getName());
147 |
148 | if (extension.length() > 0)
149 | return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.substring(1));
150 |
151 | return "application/octet-stream";
152 | }
153 |
154 | /**
155 | * @return The MIME type for the give Uri.
156 | */
157 | public static String getMimeType(Context context, Uri uri) {
158 | File file = new File(getPath(context, uri));
159 | return getMimeType(file);
160 | }
161 |
162 | /**
163 | * @param uri The Uri to check.
164 | * @return Whether the Uri authority is {@link LocalStorageProvider}.
165 | * @author paulburke
166 | */
167 | public static boolean isLocalStorageDocument(Uri uri) {
168 | return LocalStorageProvider.AUTHORITY.equals(uri.getAuthority());
169 | }
170 |
171 | /**
172 | * @param uri The Uri to check.
173 | * @return Whether the Uri authority is ExternalStorageProvider.
174 | * @author paulburke
175 | */
176 | public static boolean isExternalStorageDocument(Uri uri) {
177 | return "com.android.externalstorage.documents".equals(uri.getAuthority());
178 | }
179 |
180 | /**
181 | * @param uri The Uri to check.
182 | * @return Whether the Uri authority is DownloadsProvider.
183 | * @author paulburke
184 | */
185 | public static boolean isDownloadsDocument(Uri uri) {
186 | return "com.android.providers.downloads.documents".equals(uri.getAuthority());
187 | }
188 |
189 | /**
190 | * @param uri The Uri to check.
191 | * @return Whether the Uri authority is MediaProvider.
192 | * @author paulburke
193 | */
194 | public static boolean isMediaDocument(Uri uri) {
195 | return "com.android.providers.media.documents".equals(uri.getAuthority());
196 | }
197 |
198 | /**
199 | * @param uri The Uri to check.
200 | * @return Whether the Uri authority is Google Photos.
201 | */
202 | public static boolean isGooglePhotosUri(Uri uri) {
203 | return "com.google.android.apps.photos.content".equals(uri.getAuthority());
204 | }
205 |
206 | /**
207 | * Get the value of the data column for this Uri. This is useful for
208 | * MediaStore Uris, and other file-based ContentProviders.
209 | *
210 | * @param context The context.
211 | * @param uri The Uri to query.
212 | * @param selection (Optional) Filter used in the query.
213 | * @param selectionArgs (Optional) Selection arguments used in the query.
214 | * @return The value of the _data column, which is typically a file path.
215 | * @author paulburke
216 | */
217 | public static String getDataColumn(Context context, Uri uri, String selection,
218 | String[] selectionArgs) {
219 |
220 | Cursor cursor = null;
221 | final String column = "_data";
222 | final String[] projection = {
223 | column
224 | };
225 |
226 | try {
227 | cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
228 | null);
229 | if (cursor != null && cursor.moveToFirst()) {
230 | if (DEBUG)
231 | DatabaseUtils.dumpCursor(cursor);
232 |
233 | final int column_index = cursor.getColumnIndexOrThrow(column);
234 | return cursor.getString(column_index);
235 | }
236 | } finally {
237 | if (cursor != null)
238 | cursor.close();
239 | }
240 | return null;
241 | }
242 |
243 | /**
244 | * Get a file path from a Uri. This will get the the path for Storage Access
245 | * Framework Documents, as well as the _data field for the MediaStore and
246 | * other file-based ContentProviders.
247 | *
248 | * Callers should check whether the path is local before assuming it
249 | * represents a local file.
250 | *
251 | * @param context The context.
252 | * @param uri The Uri to query.
253 | * @see #isLocal(String)
254 | * @see #getFile(Context, Uri)
255 | * @author paulburke
256 | */
257 | public static String getPath(final Context context, final Uri uri) {
258 |
259 | if (DEBUG)
260 | Log.d(TAG + " File -",
261 | "Authority: " + uri.getAuthority() +
262 | ", Fragment: " + uri.getFragment() +
263 | ", Port: " + uri.getPort() +
264 | ", Query: " + uri.getQuery() +
265 | ", Scheme: " + uri.getScheme() +
266 | ", Host: " + uri.getHost() +
267 | ", Segments: " + uri.getPathSegments().toString()
268 | );
269 |
270 | final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
271 |
272 | // DocumentProvider
273 | if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
274 | // LocalStorageProvider
275 | if (isLocalStorageDocument(uri)) {
276 | // The path is the id
277 | return DocumentsContract.getDocumentId(uri);
278 | }
279 | // ExternalStorageProvider
280 | else if (isExternalStorageDocument(uri)) {
281 | final String docId = DocumentsContract.getDocumentId(uri);
282 | final String[] split = docId.split(":");
283 | final String type = split[0];
284 |
285 | if ("primary".equalsIgnoreCase(type)) {
286 | return Environment.getExternalStorageDirectory() + "/" + split[1];
287 | }
288 |
289 | // TODO handle non-primary volumes
290 | }
291 | // DownloadsProvider
292 | else if (isDownloadsDocument(uri)) {
293 |
294 | final String id = DocumentsContract.getDocumentId(uri);
295 | final Uri contentUri = ContentUris.withAppendedId(
296 | Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
297 |
298 | return getDataColumn(context, contentUri, null, null);
299 | }
300 | // MediaProvider
301 | else if (isMediaDocument(uri)) {
302 | final String docId = DocumentsContract.getDocumentId(uri);
303 | final String[] split = docId.split(":");
304 | final String type = split[0];
305 |
306 | Uri contentUri = null;
307 | if ("image".equals(type)) {
308 | contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
309 | } else if ("video".equals(type)) {
310 | contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
311 | } else if ("audio".equals(type)) {
312 | contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
313 | }
314 |
315 | final String selection = "_id=?";
316 | final String[] selectionArgs = new String[] {
317 | split[1]
318 | };
319 |
320 | return getDataColumn(context, contentUri, selection, selectionArgs);
321 | }
322 | }
323 | // MediaStore (and general)
324 | else if ("content".equalsIgnoreCase(uri.getScheme())) {
325 |
326 | // Return the remote address
327 | if (isGooglePhotosUri(uri))
328 | return uri.getLastPathSegment();
329 |
330 | return getDataColumn(context, uri, null, null);
331 | }
332 | // File
333 | else if ("file".equalsIgnoreCase(uri.getScheme())) {
334 | return uri.getPath();
335 | }
336 |
337 | return null;
338 | }
339 |
340 | /**
341 | * Convert Uri into File, if possible.
342 | *
343 | * @return file A local file that the Uri was pointing to, or null if the
344 | * Uri is unsupported or pointed to a remote resource.
345 | * @see #getPath(Context, Uri)
346 | * @author paulburke
347 | */
348 | public static File getFile(Context context, Uri uri) {
349 | if (uri != null) {
350 | String path = getPath(context, uri);
351 | if (path != null && isLocal(path)) {
352 | return new File(path);
353 | }
354 | }
355 | return null;
356 | }
357 |
358 | /**
359 | * Get the file size in a human-readable string.
360 | *
361 | * @param size
362 | * @return
363 | * @author paulburke
364 | */
365 | public static String getReadableFileSize(int size) {
366 | final int BYTES_IN_KILOBYTES = 1024;
367 | final DecimalFormat dec = new DecimalFormat("###.#");
368 | final String KILOBYTES = " KB";
369 | final String MEGABYTES = " MB";
370 | final String GIGABYTES = " GB";
371 | float fileSize = 0;
372 | String suffix = KILOBYTES;
373 |
374 | if (size > BYTES_IN_KILOBYTES) {
375 | fileSize = size / BYTES_IN_KILOBYTES;
376 | if (fileSize > BYTES_IN_KILOBYTES) {
377 | fileSize = fileSize / BYTES_IN_KILOBYTES;
378 | if (fileSize > BYTES_IN_KILOBYTES) {
379 | fileSize = fileSize / BYTES_IN_KILOBYTES;
380 | suffix = GIGABYTES;
381 | } else {
382 | suffix = MEGABYTES;
383 | }
384 | }
385 | }
386 | return String.valueOf(dec.format(fileSize) + suffix);
387 | }
388 |
389 | /**
390 | * Attempt to retrieve the thumbnail of given File from the MediaStore. This
391 | * should not be called on the UI thread.
392 | *
393 | * @param context
394 | * @param file
395 | * @return
396 | * @author paulburke
397 | */
398 | public static Bitmap getThumbnail(Context context, File file) {
399 | return getThumbnail(context, getUri(file), getMimeType(file));
400 | }
401 |
402 | /**
403 | * Attempt to retrieve the thumbnail of given Uri from the MediaStore. This
404 | * should not be called on the UI thread.
405 | *
406 | * @param context
407 | * @param uri
408 | * @return
409 | * @author paulburke
410 | */
411 | public static Bitmap getThumbnail(Context context, Uri uri) {
412 | return getThumbnail(context, uri, getMimeType(context, uri));
413 | }
414 |
415 | /**
416 | * Attempt to retrieve the thumbnail of given Uri from the MediaStore. This
417 | * should not be called on the UI thread.
418 | *
419 | * @param context
420 | * @param uri
421 | * @param mimeType
422 | * @return
423 | * @author paulburke
424 | */
425 | public static Bitmap getThumbnail(Context context, Uri uri, String mimeType) {
426 | if (DEBUG)
427 | Log.d(TAG, "Attempting to get thumbnail");
428 |
429 | if (!isMediaUri(uri)) {
430 | Log.e(TAG, "You can only retrieve thumbnails for images and videos.");
431 | return null;
432 | }
433 |
434 | Bitmap bm = null;
435 | if (uri != null) {
436 | final ContentResolver resolver = context.getContentResolver();
437 | Cursor cursor = null;
438 | try {
439 | cursor = resolver.query(uri, null, null, null, null);
440 | if (cursor.moveToFirst()) {
441 | final int id = cursor.getInt(0);
442 | if (DEBUG)
443 | Log.d(TAG, "Got thumb ID: " + id);
444 |
445 | if (mimeType.contains("video")) {
446 | bm = MediaStore.Video.Thumbnails.getThumbnail(
447 | resolver,
448 | id,
449 | MediaStore.Video.Thumbnails.MINI_KIND,
450 | null);
451 | }
452 | else if (mimeType.contains(FileUtils.MIME_TYPE_IMAGE)) {
453 | bm = MediaStore.Images.Thumbnails.getThumbnail(
454 | resolver,
455 | id,
456 | MediaStore.Images.Thumbnails.MINI_KIND,
457 | null);
458 | }
459 | }
460 | } catch (Exception e) {
461 | if (DEBUG)
462 | Log.e(TAG, "getThumbnail", e);
463 | } finally {
464 | if (cursor != null)
465 | cursor.close();
466 | }
467 | }
468 | return bm;
469 | }
470 |
471 | /**
472 | * File and folder comparator. TODO Expose sorting option method
473 | *
474 | * @author paulburke
475 | */
476 | public static Comparator sComparator = new Comparator() {
477 | @Override
478 | public int compare(File f1, File f2) {
479 | // Sort alphabetically by lower case, which is much cleaner
480 | return f1.getName().toLowerCase().compareTo(
481 | f2.getName().toLowerCase());
482 | }
483 | };
484 |
485 | /**
486 | * File (not directories) filter.
487 | *
488 | * @author paulburke
489 | */
490 | public static FileFilter sFileFilter = new FileFilter() {
491 | @Override
492 | public boolean accept(File file) {
493 | final String fileName = file.getName();
494 | // Return files only (not directories) and skip hidden files
495 | return file.isFile() && !fileName.startsWith(HIDDEN_PREFIX);
496 | }
497 | };
498 |
499 | /**
500 | * Folder (directories) filter.
501 | *
502 | * @author paulburke
503 | */
504 | public static FileFilter sDirFilter = new FileFilter() {
505 | @Override
506 | public boolean accept(File file) {
507 | final String fileName = file.getName();
508 | // Return directories only and skip hidden directories
509 | return file.isDirectory() && !fileName.startsWith(HIDDEN_PREFIX);
510 | }
511 | };
512 |
513 | /**
514 | * Get the Intent for selecting content to be used in an Intent Chooser.
515 | *
516 | * @return The intent for opening a file with Intent.createChooser()
517 | * @author paulburke
518 | */
519 | public static Intent createGetContentIntent() {
520 | // Implicitly allow the user to select a particular kind of data
521 | final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
522 | // The MIME data type filter
523 | intent.setType("*/*");
524 | // Only return URIs that can be opened with ContentResolver
525 | intent.addCategory(Intent.CATEGORY_OPENABLE);
526 | return intent;
527 | }
528 | }
529 |
--------------------------------------------------------------------------------
/src/android/filechooser/LocalStorageProvider.java:
--------------------------------------------------------------------------------
1 |
2 | package com.ianhanniballake.localstorage;
3 |
4 | import android.content.res.AssetFileDescriptor;
5 | import android.database.Cursor;
6 | import android.database.MatrixCursor;
7 | import android.graphics.Bitmap;
8 | import android.graphics.BitmapFactory;
9 | import android.graphics.Point;
10 | import android.os.CancellationSignal;
11 | import android.os.Environment;
12 | import android.os.ParcelFileDescriptor;
13 | import android.provider.DocumentsContract.Document;
14 | import android.provider.DocumentsContract.Root;
15 | import android.provider.DocumentsProvider;
16 | import android.util.Log;
17 | import android.webkit.MimeTypeMap;
18 |
19 | import java.io.File;
20 | import java.io.FileNotFoundException;
21 | import java.io.FileOutputStream;
22 | import java.io.IOException;
23 |
24 | public class LocalStorageProvider extends DocumentsProvider {
25 |
26 | public static final String AUTHORITY = "com.ianhanniballake.localstorage.documents";
27 |
28 | /**
29 | * Default root projection: everything but Root.COLUMN_MIME_TYPES
30 | */
31 | private final static String[] DEFAULT_ROOT_PROJECTION = new String[] {
32 | Root.COLUMN_ROOT_ID,
33 | Root.COLUMN_FLAGS, Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID, Root.COLUMN_ICON,
34 | Root.COLUMN_AVAILABLE_BYTES
35 | };
36 | /**
37 | * Default document projection: everything but Document.COLUMN_ICON and
38 | * Document.COLUMN_SUMMARY
39 | */
40 | private final static String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
41 | Document.COLUMN_DOCUMENT_ID,
42 | Document.COLUMN_DISPLAY_NAME, Document.COLUMN_FLAGS, Document.COLUMN_MIME_TYPE,
43 | Document.COLUMN_SIZE,
44 | Document.COLUMN_LAST_MODIFIED
45 | };
46 |
47 | @Override
48 | public Cursor queryRoots(final String[] projection) throws FileNotFoundException {
49 | // Create a cursor with either the requested fields, or the default
50 | // projection if "projection" is null.
51 | final MatrixCursor result = new MatrixCursor(projection != null ? projection
52 | : DEFAULT_ROOT_PROJECTION);
53 | // Add Home directory
54 | File homeDir = Environment.getExternalStorageDirectory();
55 | final MatrixCursor.RowBuilder row = result.newRow();
56 | // These columns are required
57 | row.add(Root.COLUMN_ROOT_ID, homeDir.getAbsolutePath());
58 | row.add(Root.COLUMN_DOCUMENT_ID, homeDir.getAbsolutePath());
59 | row.add(Root.COLUMN_TITLE, getContext().getString(R.string.internal_storage));
60 | row.add(Root.COLUMN_FLAGS, Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE);
61 | row.add(Root.COLUMN_ICON, R.drawable.ic_provider);
62 | // These columns are optional
63 | row.add(Root.COLUMN_AVAILABLE_BYTES, homeDir.getFreeSpace());
64 | // Root.COLUMN_MIME_TYPE is another optional column and useful if you
65 | // have multiple roots with different
66 | // types of mime types (roots that don't match the requested mime type
67 | // are automatically hidden)
68 | return result;
69 | }
70 |
71 | @Override
72 | public String createDocument(final String parentDocumentId, final String mimeType,
73 | final String displayName) throws FileNotFoundException {
74 | File newFile = new File(parentDocumentId, displayName);
75 | try {
76 | newFile.createNewFile();
77 | return newFile.getAbsolutePath();
78 | } catch (IOException e) {
79 | Log.e(LocalStorageProvider.class.getSimpleName(), "Error creating new file " + newFile);
80 | }
81 | return null;
82 | }
83 |
84 | @Override
85 | public AssetFileDescriptor openDocumentThumbnail(final String documentId, final Point sizeHint,
86 | final CancellationSignal signal) throws FileNotFoundException {
87 | // Assume documentId points to an image file. Build a thumbnail no
88 | // larger than twice the sizeHint
89 | BitmapFactory.Options options = new BitmapFactory.Options();
90 | options.inJustDecodeBounds = true;
91 | BitmapFactory.decodeFile(documentId, options);
92 | final int targetHeight = 2 * sizeHint.y;
93 | final int targetWidth = 2 * sizeHint.x;
94 | final int height = options.outHeight;
95 | final int width = options.outWidth;
96 | options.inSampleSize = 1;
97 | if (height > targetHeight || width > targetWidth) {
98 | final int halfHeight = height / 2;
99 | final int halfWidth = width / 2;
100 | // Calculate the largest inSampleSize value that is a power of 2 and
101 | // keeps both
102 | // height and width larger than the requested height and width.
103 | while ((halfHeight / options.inSampleSize) > targetHeight
104 | || (halfWidth / options.inSampleSize) > targetWidth) {
105 | options.inSampleSize *= 2;
106 | }
107 | }
108 | options.inJustDecodeBounds = false;
109 | Bitmap bitmap = BitmapFactory.decodeFile(documentId, options);
110 | // Write out the thumbnail to a temporary file
111 | File tempFile = null;
112 | FileOutputStream out = null;
113 | try {
114 | tempFile = File.createTempFile("thumbnail", null, getContext().getCacheDir());
115 | out = new FileOutputStream(tempFile);
116 | bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
117 | } catch (IOException e) {
118 | Log.e(LocalStorageProvider.class.getSimpleName(), "Error writing thumbnail", e);
119 | return null;
120 | } finally {
121 | if (out != null)
122 | try {
123 | out.close();
124 | } catch (IOException e) {
125 | Log.e(LocalStorageProvider.class.getSimpleName(), "Error closing thumbnail", e);
126 | }
127 | }
128 | // It appears the Storage Framework UI caches these results quite
129 | // aggressively so there is little reason to
130 | // write your own caching layer beyond what you need to return a single
131 | // AssetFileDescriptor
132 | return new AssetFileDescriptor(ParcelFileDescriptor.open(tempFile,
133 | ParcelFileDescriptor.MODE_READ_ONLY), 0,
134 | AssetFileDescriptor.UNKNOWN_LENGTH);
135 | }
136 |
137 | @Override
138 | public Cursor queryChildDocuments(final String parentDocumentId, final String[] projection,
139 | final String sortOrder) throws FileNotFoundException {
140 | // Create a cursor with either the requested fields, or the default
141 | // projection if "projection" is null.
142 | final MatrixCursor result = new MatrixCursor(projection != null ? projection
143 | : DEFAULT_DOCUMENT_PROJECTION);
144 | final File parent = new File(parentDocumentId);
145 | for (File file : parent.listFiles()) {
146 | // Don't show hidden files/folders
147 | if (!file.getName().startsWith(".")) {
148 | // Adds the file's display name, MIME type, size, and so on.
149 | includeFile(result, file);
150 | }
151 | }
152 | return result;
153 | }
154 |
155 | @Override
156 | public Cursor queryDocument(final String documentId, final String[] projection)
157 | throws FileNotFoundException {
158 | // Create a cursor with either the requested fields, or the default
159 | // projection if "projection" is null.
160 | final MatrixCursor result = new MatrixCursor(projection != null ? projection
161 | : DEFAULT_DOCUMENT_PROJECTION);
162 | includeFile(result, new File(documentId));
163 | return result;
164 | }
165 |
166 | private void includeFile(final MatrixCursor result, final File file)
167 | throws FileNotFoundException {
168 | final MatrixCursor.RowBuilder row = result.newRow();
169 | // These columns are required
170 | row.add(Document.COLUMN_DOCUMENT_ID, file.getAbsolutePath());
171 | row.add(Document.COLUMN_DISPLAY_NAME, file.getName());
172 | String mimeType = getDocumentType(file.getAbsolutePath());
173 | row.add(Document.COLUMN_MIME_TYPE, mimeType);
174 | int flags = file.canWrite() ? Document.FLAG_SUPPORTS_DELETE | Document.FLAG_SUPPORTS_WRITE
175 | : 0;
176 | // We only show thumbnails for image files - expect a call to
177 | // openDocumentThumbnail for each file that has
178 | // this flag set
179 | if (mimeType.startsWith("image/"))
180 | flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
181 | row.add(Document.COLUMN_FLAGS, flags);
182 | // COLUMN_SIZE is required, but can be null
183 | row.add(Document.COLUMN_SIZE, file.length());
184 | // These columns are optional
185 | row.add(Document.COLUMN_LAST_MODIFIED, file.lastModified());
186 | // Document.COLUMN_ICON can be a resource id identifying a custom icon.
187 | // The system provides default icons
188 | // based on mime type
189 | // Document.COLUMN_SUMMARY is optional additional information about the
190 | // file
191 | }
192 |
193 | @Override
194 | public String getDocumentType(final String documentId) throws FileNotFoundException {
195 | File file = new File(documentId);
196 | if (file.isDirectory())
197 | return Document.MIME_TYPE_DIR;
198 | // From FileProvider.getType(Uri)
199 | final int lastDot = file.getName().lastIndexOf('.');
200 | if (lastDot >= 0) {
201 | final String extension = file.getName().substring(lastDot + 1);
202 | final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
203 | if (mime != null) {
204 | return mime;
205 | }
206 | }
207 | return "application/octet-stream";
208 | }
209 |
210 | @Override
211 | public void deleteDocument(final String documentId) throws FileNotFoundException {
212 | new File(documentId).delete();
213 | }
214 |
215 | @Override
216 | public ParcelFileDescriptor openDocument(final String documentId, final String mode,
217 | final CancellationSignal signal) throws FileNotFoundException {
218 | File file = new File(documentId);
219 | final boolean isWrite = (mode.indexOf('w') != -1);
220 | if (isWrite) {
221 | return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
222 | } else {
223 | return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
224 | }
225 | }
226 |
227 | @Override
228 | public boolean onCreate() {
229 | return true;
230 | }
231 | }
232 |
--------------------------------------------------------------------------------
/src/android/filechooser/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cdibened/filechooser/36dd9fdecde0a213f089fafa86abc6d257fe6b7c/src/android/filechooser/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/src/android/filechooser/res/drawable-hdpi/ic_chooser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cdibened/filechooser/36dd9fdecde0a213f089fafa86abc6d257fe6b7c/src/android/filechooser/res/drawable-hdpi/ic_chooser.png
--------------------------------------------------------------------------------
/src/android/filechooser/res/drawable-hdpi/ic_file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cdibened/filechooser/36dd9fdecde0a213f089fafa86abc6d257fe6b7c/src/android/filechooser/res/drawable-hdpi/ic_file.png
--------------------------------------------------------------------------------
/src/android/filechooser/res/drawable-hdpi/ic_folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cdibened/filechooser/36dd9fdecde0a213f089fafa86abc6d257fe6b7c/src/android/filechooser/res/drawable-hdpi/ic_folder.png
--------------------------------------------------------------------------------
/src/android/filechooser/res/drawable-hdpi/ic_provider.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cdibened/filechooser/36dd9fdecde0a213f089fafa86abc6d257fe6b7c/src/android/filechooser/res/drawable-hdpi/ic_provider.png
--------------------------------------------------------------------------------
/src/android/filechooser/res/drawable-mdpi/ic_chooser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cdibened/filechooser/36dd9fdecde0a213f089fafa86abc6d257fe6b7c/src/android/filechooser/res/drawable-mdpi/ic_chooser.png
--------------------------------------------------------------------------------
/src/android/filechooser/res/drawable-mdpi/ic_file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cdibened/filechooser/36dd9fdecde0a213f089fafa86abc6d257fe6b7c/src/android/filechooser/res/drawable-mdpi/ic_file.png
--------------------------------------------------------------------------------
/src/android/filechooser/res/drawable-mdpi/ic_folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cdibened/filechooser/36dd9fdecde0a213f089fafa86abc6d257fe6b7c/src/android/filechooser/res/drawable-mdpi/ic_folder.png
--------------------------------------------------------------------------------
/src/android/filechooser/res/drawable-mdpi/ic_provider.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cdibened/filechooser/36dd9fdecde0a213f089fafa86abc6d257fe6b7c/src/android/filechooser/res/drawable-mdpi/ic_provider.png
--------------------------------------------------------------------------------
/src/android/filechooser/res/drawable-xhdpi/ic_chooser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cdibened/filechooser/36dd9fdecde0a213f089fafa86abc6d257fe6b7c/src/android/filechooser/res/drawable-xhdpi/ic_chooser.png
--------------------------------------------------------------------------------
/src/android/filechooser/res/drawable-xhdpi/ic_file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cdibened/filechooser/36dd9fdecde0a213f089fafa86abc6d257fe6b7c/src/android/filechooser/res/drawable-xhdpi/ic_file.png
--------------------------------------------------------------------------------
/src/android/filechooser/res/drawable-xhdpi/ic_folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cdibened/filechooser/36dd9fdecde0a213f089fafa86abc6d257fe6b7c/src/android/filechooser/res/drawable-xhdpi/ic_folder.png
--------------------------------------------------------------------------------
/src/android/filechooser/res/drawable-xhdpi/ic_provider.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cdibened/filechooser/36dd9fdecde0a213f089fafa86abc6d257fe6b7c/src/android/filechooser/res/drawable-xhdpi/ic_provider.png
--------------------------------------------------------------------------------
/src/android/filechooser/res/drawable-xxhdpi/ic_chooser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cdibened/filechooser/36dd9fdecde0a213f089fafa86abc6d257fe6b7c/src/android/filechooser/res/drawable-xxhdpi/ic_chooser.png
--------------------------------------------------------------------------------
/src/android/filechooser/res/drawable-xxhdpi/ic_file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cdibened/filechooser/36dd9fdecde0a213f089fafa86abc6d257fe6b7c/src/android/filechooser/res/drawable-xxhdpi/ic_file.png
--------------------------------------------------------------------------------
/src/android/filechooser/res/drawable-xxhdpi/ic_folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cdibened/filechooser/36dd9fdecde0a213f089fafa86abc6d257fe6b7c/src/android/filechooser/res/drawable-xxhdpi/ic_folder.png
--------------------------------------------------------------------------------
/src/android/filechooser/res/drawable-xxhdpi/ic_provider.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cdibened/filechooser/36dd9fdecde0a213f089fafa86abc6d257fe6b7c/src/android/filechooser/res/drawable-xxhdpi/ic_provider.png
--------------------------------------------------------------------------------
/src/android/filechooser/res/layout/file.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
21 |
--------------------------------------------------------------------------------
/src/android/filechooser/res/values-ca/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Carpeta buida
5 | S\'ha tret o desmuntat l\'emmagatzematge.
6 | Seleccioneu un fitxer
7 | Error en seleccionar el fitxer
8 |
9 |
--------------------------------------------------------------------------------
/src/android/filechooser/res/values-de/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Leerer Ordner
4 | Speicher wurde entferntet.
5 | Wähle eine Datei
6 | Fehler beim Öffnen der Datei
7 |
--------------------------------------------------------------------------------
/src/android/filechooser/res/values-es/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Directorio vacío
5 | Se ha retirado o desmontado el almacenamiento.
6 | Seleccione un archivo
7 | Error al seleccionar el archivo
8 |
9 |
--------------------------------------------------------------------------------
/src/android/filechooser/res/values-fr/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Dossier vide
5 | Le stockage a été enlevé ou démonté.
6 | Sélectionnez un fichier
7 | Erreur lors de la sélection du fichier
8 |
9 |
--------------------------------------------------------------------------------
/src/android/filechooser/res/values-ga/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Comhadlann fholamh
5 | Baineadh amach an gléas stórála nó dínascadh é.
6 | Roghnaigh comhad
7 | Tharla botún fad is a bhí comhad á roghnú
8 |
9 |
--------------------------------------------------------------------------------
/src/android/filechooser/res/values-it/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Directory vuota
5 | Lo spazio di archiviazione è stato rimosso o smontato.
6 | Selezionare un file
7 | Errore nel selezionare il File
8 |
9 |
--------------------------------------------------------------------------------
/src/android/filechooser/res/values-ko/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 빈 디렉토리
5 | 저장소가 제거되었습니다.
6 | 파일 선택
7 | 파일 선택 오류
8 |
9 |
--------------------------------------------------------------------------------
/src/android/filechooser/res/values-pl/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Pusty katalog
5 | Pamięć została usunięta lub odmontowana.
6 | Wybierz plik
7 | Błąd, podczas wybierania pliku
8 |
9 |
--------------------------------------------------------------------------------
/src/android/filechooser/res/values-pt-rBR/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Pasta Vazia
5 | Unidade externa removida ou não preparada.
6 | Selecione um Arquivo
7 | Erro ao selecionar o Arquivo
8 |
9 |
--------------------------------------------------------------------------------
/src/android/filechooser/res/values-ru/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Пустая папка
5 | Storage was removed or unmounted.
6 | Выберите файл
7 | Ошибка при выборе файла
8 |
9 |
--------------------------------------------------------------------------------
/src/android/filechooser/res/values-v11/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 | Choose a file
19 |
--------------------------------------------------------------------------------
/src/android/filechooser/res/values-v19/bool.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | false
5 | true
6 |
7 |
--------------------------------------------------------------------------------
/src/android/filechooser/res/values/bool.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 | false
6 |
7 |
--------------------------------------------------------------------------------
/src/android/filechooser/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 | 0dp
19 | 16dp
20 |
--------------------------------------------------------------------------------
/src/android/filechooser/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
28 |
29 |
--------------------------------------------------------------------------------
/src/android/filechooser/res/xml/mimetypes.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/www/filechooser.js:
--------------------------------------------------------------------------------
1 | /**
2 | * cordova FileChooser plugin
3 | */
4 | (function(cordova){
5 | var FileChooser = function() {
6 |
7 | };
8 |
9 | FileChooser.prototype.open = function(params, success, fail) {
10 | return cordova.exec(function(args) {
11 | success(args);
12 | }, function(args) {
13 | fail(args);
14 | }, 'FileChooser', 'open', [params||{}]);
15 | };
16 |
17 | window.filechooser = new FileChooser();
18 |
19 | // backwards compatibility
20 | window.plugins = window.plugins || {};
21 | window.plugins.filechooser = window.filechooser;
22 | })(window.PhoneGap || window.Cordova || window.cordova);
--------------------------------------------------------------------------------