├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── values-w820dp
│ │ │ │ └── dimens.xml
│ │ │ └── layout
│ │ │ │ └── activity_file_picker.xml
│ │ ├── java
│ │ │ └── dk
│ │ │ │ └── nodes
│ │ │ │ └── filepicker
│ │ │ │ ├── processors
│ │ │ │ ├── UriProcessListener.java
│ │ │ │ ├── IUriProcessor.java
│ │ │ │ ├── GoogleMediaProcessor.java
│ │ │ │ ├── GooglePhotosProcessor.java
│ │ │ │ ├── GoogleDriveProcessor.java
│ │ │ │ ├── UriProcessor.java
│ │ │ │ ├── tasks
│ │ │ │ │ ├── GetPhotosTask.java
│ │ │ │ │ ├── GetFileTask.java
│ │ │ │ │ └── GetGoogleDriveFileTask.java
│ │ │ │ ├── GoogleDocumentsProcessor.java
│ │ │ │ └── GenericContentProviderProcessor.java
│ │ │ │ ├── utils
│ │ │ │ └── Logger.java
│ │ │ │ ├── intentHelper
│ │ │ │ ├── FilePickerFileIntent.java
│ │ │ │ ├── FilePickerCameraIntent.java
│ │ │ │ └── FilePickerChooserIntent.java
│ │ │ │ ├── permissionHelper
│ │ │ │ └── FilePickerPermissionHelper.java
│ │ │ │ ├── FilePickerConstants.java
│ │ │ │ ├── bitmapHelper
│ │ │ │ └── FilePickerBitmapHelper.java
│ │ │ │ ├── uriHelper
│ │ │ │ └── FilePickerUriHelper.java
│ │ │ │ └── FilePickerActivity.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── dk
│ │ │ └── nodes
│ │ │ └── filepicker
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── dk
│ │ └── nodes
│ │ └── filepicker
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
├── build.gradle
└── maven-push.gradle
├── filepicker.example
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ └── layout
│ │ │ │ └── activity_file_picker_example.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── dk
│ │ │ └── nodes
│ │ │ └── filepickerexample
│ │ │ ├── ImgurManager.java
│ │ │ └── FilePickerExampleActivity.java
│ ├── test
│ │ └── java
│ │ │ └── dk
│ │ │ └── nodes
│ │ │ └── filepickerexample
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── dk
│ │ └── nodes
│ │ └── filepickerexample
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── .idea
├── copyright
│ └── profiles_settings.xml
├── vcs.xml
├── modules.xml
├── runConfigurations.xml
├── gradle.xml
├── compiler.xml
└── misc.xml
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── gradlew.bat
├── README.md
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/filepicker.example/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':filepicker.example'
2 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | filepicker
3 |
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/filepicker/master/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/filepicker.example/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | filepicker.example
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/filepicker/master/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/filepicker/master/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/filepicker/master/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/filepicker/master/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/filepicker/master/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/filepicker.example/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/filepicker/master/filepicker.example/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/filepicker.example/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/filepicker/master/filepicker.example/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/filepicker.example/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/filepicker/master/filepicker.example/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/filepicker.example/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/filepicker/master/filepicker.example/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/filepicker.example/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/filepicker/master/filepicker.example/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/filepicker.example/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Mar 03 11:12:04 CET 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/java/dk/nodes/filepicker/processors/UriProcessListener.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker.processors;
2 |
3 | import android.content.Intent;
4 |
5 | /**
6 | * Created by bison on 31/10/17.
7 | */
8 |
9 | public interface UriProcessListener {
10 | void onProcessingSuccess(Intent intent);
11 | void onProcessingFailure();
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/dk/nodes/filepicker/processors/IUriProcessor.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker.processors;
2 |
3 | import android.content.Context;
4 | import android.net.Uri;
5 |
6 | /**
7 | * Created by bison on 31/10/17.
8 | */
9 |
10 | public interface IUriProcessor {
11 | void process(Context context, Uri uri, UriProcessListener uriProcessListener);
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/filepicker.example/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/dk/nodes/filepicker/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/filepicker.example/src/test/java/dk/nodes/filepickerexample/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepickerexample;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_file_picker.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
12 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/java/dk/nodes/filepicker/utils/Logger.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker.utils;
2 |
3 | import android.util.Log;
4 |
5 | import dk.nodes.filepicker.BuildConfig;
6 |
7 | /**
8 | * Created by bison on 31/10/17.
9 | */
10 |
11 | public class Logger {
12 | public static void loge(String tag, String msg)
13 | {
14 | if(BuildConfig.DEBUG)
15 | {
16 | Log.e(tag, msg);
17 | }
18 | }
19 |
20 | public static void logd(String tag, String msg)
21 | {
22 | if(BuildConfig.DEBUG)
23 | {
24 | Log.d(tag, msg);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 | # Files for the ART/Dalvik VM
5 | *.dex
6 | # Java class files
7 | *.class
8 | # Generated files
9 | bin/
10 | gen/
11 | out/
12 | # Gradle files
13 | .gradle/
14 | build/
15 | # Local configuration file (sdk path, etc)
16 | local.properties
17 | # Log Files
18 | *.log
19 | # Android Studio Navigation editor temp files
20 | .navigation/
21 | # Android Studio captures folder
22 | captures/
23 | # Intellij
24 | *.iml
25 | .idea/workspace.xml
26 | .idea/libraries
27 | projectFilesBackup/
28 | # External native build folder generated in Android Studio 2.2 and later
29 | .externalNativeBuild
30 | # Windows thumbnail db
31 | Thumbs.db
32 | # OSX files
33 | .DS_Store
34 | #NDK
35 | obj/
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/Mario/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/filepicker.example/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/joso/sdk/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/java/dk/nodes/filepicker/intentHelper/FilePickerFileIntent.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker.intentHelper;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Intent;
5 |
6 | public class FilePickerFileIntent {
7 |
8 | public static Intent fileIntent(String type) {
9 | type = null != type ? type : "image/*";
10 | return new Intent().setAction(Intent.ACTION_GET_CONTENT).setType(type);
11 | }
12 |
13 | public static void setType(Intent intent, String type) {
14 | intent.setType(type);
15 | }
16 |
17 | @TargetApi(19)
18 | public static void setTypes(Intent intent, String[] types) {
19 | intent.setType("*/*");
20 | intent.putExtra(Intent.EXTRA_MIME_TYPES, types);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
9 |
10 |
11 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/dk/nodes/filepicker/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("dk.nodes.filepicker", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/filepicker.example/src/androidTest/java/dk/nodes/filepickerexample/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepickerexample;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("dk.nodes.filepickerexample", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/dk/nodes/filepicker/permissionHelper/FilePickerPermissionHelper.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker.permissionHelper;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.pm.PackageManager;
6 | import android.support.annotation.NonNull;
7 | import android.support.v4.app.ActivityCompat;
8 |
9 | public class FilePickerPermissionHelper {
10 |
11 | public static boolean requirePermission(Context context, String... permissions) {
12 | for (String permission : permissions) {
13 | if (PackageManager.PERMISSION_DENIED == ActivityCompat.checkSelfPermission(context, permission)) {
14 | return true;
15 | }
16 | }
17 | return false;
18 | }
19 |
20 | public static void askPermission(Activity activity, int requestCode, @NonNull String... permissions) {
21 | ActivityCompat.requestPermissions(activity, permissions, requestCode);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/dk/nodes/filepicker/intentHelper/FilePickerCameraIntent.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker.intentHelper;
2 |
3 | import android.app.Activity;
4 | import android.content.ContentValues;
5 | import android.content.Intent;
6 | import android.net.Uri;
7 | import android.provider.MediaStore;
8 | import android.support.annotation.NonNull;
9 |
10 | public class FilePickerCameraIntent {
11 | public static Intent cameraIntent(@NonNull Uri uri) {
12 | return new Intent(MediaStore.ACTION_IMAGE_CAPTURE)
13 | .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
14 | .putExtra(MediaStore.EXTRA_OUTPUT, uri);
15 | }
16 |
17 | public static Uri setUri(@NonNull Activity activity) {
18 | ContentValues contentValues = new ContentValues(1);
19 | contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpg");
20 | return activity.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/filepicker.example/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
9 |
10 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/filepicker.example/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.2"
6 |
7 | defaultConfig {
8 | applicationId "dk.nodes.filepickerexample"
9 | minSdkVersion 16
10 | targetSdkVersion 25
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
15 |
16 | }
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | }
24 |
25 | dependencies {
26 | compile project(':app')
27 | compile fileTree(dir: 'libs', include: ['*.jar'])
28 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
29 | exclude group: 'com.android.support', module: 'support-annotations'
30 | })
31 | compile 'com.android.support:appcompat-v7:25.1.0'
32 | testCompile 'junit:junit:4.12'
33 | compile 'com.github.bumptech.glide:glide:3.7.0'
34 | compile 'com.squareup.retrofit2:retrofit:2.1.0'
35 | compile 'com.squareup.retrofit2:converter-gson:2.1.0'
36 | }
37 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply from: 'maven-push.gradle'
3 |
4 | android {
5 | compileSdkVersion 25
6 | buildToolsVersion "25.0.2"
7 | defaultConfig {
8 | minSdkVersion 14
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
25 | exclude group: 'com.android.support', module: 'support-annotations'
26 | })
27 | compile 'com.android.support:appcompat-v7:25.1.0'
28 | testCompile 'junit:junit:4.12'
29 | }
30 |
31 | // right click function below to execute if you like me can never find them in that retarded sidepane
32 | task installArchives(type: Upload) {
33 | description "Installs the artifacts to the local Maven repository."
34 | repositories.mavenInstaller {
35 | configuration = configurations.default
36 | pom.groupId = 'dk.nodes.filepicker'
37 | pom.artifactId = 'filepicker'
38 | pom.version = '12-LOCAL'
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/dk/nodes/filepicker/FilePickerConstants.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker;
2 |
3 | public class FilePickerConstants {
4 |
5 | public final static String CHOOSER_TEXT = "CHOOSER_TEXT";
6 |
7 | public final static String URI = "URI";
8 | /**
9 | * INTENT EXTRAS
10 | */
11 | public final static String DOWNLOAD_IF_NON_LOCAL = "DOWNLOAD_IF_NON_LOCAL";
12 | public final static String CAMERA = "CAMERA";
13 | public final static String FILE = "FILE";
14 | public final static String TYPE = "TYPE";
15 | public final static String MULTIPLE_TYPES = "MULTIPLE_TYPES";
16 | /**
17 | * MIME IMAGE
18 | */
19 | public final static String MIME_IMAGE = "image/*";
20 | public final static String MIME_IMAGE_PNG = "image/png";
21 | public final static String MIME_IMAGE_BMP = "image/bmp";
22 | public final static String MIME_IMAGE_JPG = "image/jpg";
23 | public final static String MIME_IMAGE_GIF = "image/gif";
24 | /**
25 | * MIME VIDEO
26 | */
27 | public final static String MIME_VIDEO = "video/*";
28 | public final static String MIME_VIDEO_WAV = "video/wav";
29 | public final static String MIME_VIDEO_MP4 = "video/mp4";
30 | /**
31 | * MIME OTHERS
32 | */
33 | public final static String MIME_PDF = "application/pdf";
34 | public final static String MIME_TEXT_PLAIN = "text/plain";
35 | /**
36 | * Response codes
37 | */
38 | public static final int RESULT_CODE_FAILURE = 10;
39 | final static int REQUEST_CODE = 2;
40 | final static int PERMISSION_REQUEST_CODE = 3;
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/java/dk/nodes/filepicker/intentHelper/FilePickerChooserIntent.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker.intentHelper;
2 |
3 | import android.content.Intent;
4 | import android.net.Uri;
5 |
6 | public class FilePickerChooserIntent {
7 |
8 | public static Intent chooserIntent(String chooserText, Uri uri) {
9 | return Intent.createChooser(new Intent().setType("image/*").setAction(Intent.ACTION_GET_CONTENT)
10 | .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION), chooserText)
11 | .putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{FilePickerCameraIntent.cameraIntent(uri)});
12 | }
13 |
14 | public static Intent chooserSingleIntent(String chooserText, Uri uri, String type) {
15 | return Intent.createChooser(
16 | new Intent()
17 | .setType(type)
18 | .setAction(Intent.ACTION_GET_CONTENT)
19 | .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION), chooserText)
20 | .putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{FilePickerCameraIntent.cameraIntent(uri)});
21 | }
22 |
23 | public static Intent chooserMultiIntent(String chooserText, Uri uri, String[] types) {
24 | return Intent.createChooser(
25 | new Intent()
26 | .setType("*/*")
27 | .setAction(Intent.ACTION_GET_CONTENT)
28 | .putExtra(Intent.EXTRA_MIME_TYPES, types)
29 | .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION), chooserText)
30 | .putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{FilePickerCameraIntent.cameraIntent(uri)});
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/dk/nodes/filepicker/processors/GoogleMediaProcessor.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker.processors;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.net.Uri;
6 |
7 | import dk.nodes.filepicker.processors.tasks.GetFileTask;
8 | import dk.nodes.filepicker.utils.Logger;
9 |
10 | import static dk.nodes.filepicker.FilePickerConstants.URI;
11 |
12 | /**
13 | * Created by bison on 31/10/17.
14 | */
15 |
16 | public class GoogleMediaProcessor implements IUriProcessor {
17 | public static final String TAG = GoogleMediaProcessor.class.getSimpleName();
18 | UriProcessListener uriProcessListener;
19 |
20 | @Override
21 | public void process(Context context, Uri uri, final UriProcessListener uriProcessListener) {
22 | this.uriProcessListener = uriProcessListener;
23 | if(!isValidUri(uri))
24 | {
25 | if(uriProcessListener != null) {
26 | Logger.loge(TAG, "URI not recognized, bailing out");
27 | uriProcessListener.onProcessingFailure();
28 | return;
29 | }
30 | }
31 |
32 | final String mimeType = context.getContentResolver().getType(uri);
33 |
34 | new GetFileTask(context, uri, new GetFileTask.TaskListener() {
35 | @Override
36 | public void didSucceed(String newPath) {
37 | Intent intent = new Intent();
38 | if(mimeType != null)
39 | intent.putExtra("mimeType", mimeType);
40 | intent.putExtra(URI, newPath);
41 | if(uriProcessListener != null)
42 | uriProcessListener.onProcessingSuccess(intent);
43 | }
44 |
45 | @Override
46 | public void didFail() {
47 | if(uriProcessListener != null)
48 | uriProcessListener.onProcessingFailure();
49 | }
50 | }).execute();
51 | }
52 |
53 | private static boolean isValidUri(Uri uri) {
54 | return "com.google.android.apps.photos.contentprovider".equals(uri.getAuthority());
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/main/java/dk/nodes/filepicker/processors/GooglePhotosProcessor.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker.processors;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.net.Uri;
6 |
7 | import dk.nodes.filepicker.processors.tasks.GetPhotosTask;
8 | import dk.nodes.filepicker.utils.Logger;
9 |
10 | import static dk.nodes.filepicker.FilePickerConstants.URI;
11 |
12 | /**
13 | * Created by bison on 31/10/17.
14 | */
15 |
16 | public class GooglePhotosProcessor implements IUriProcessor {
17 | public static final String TAG = GooglePhotosProcessor.class.getSimpleName();
18 | UriProcessListener uriProcessListener;
19 |
20 | @Override
21 | public void process(Context context, Uri uri, final UriProcessListener uriProcessListener) {
22 | this.uriProcessListener = uriProcessListener;
23 | if(!isValidUri(uri))
24 | {
25 | if(uriProcessListener != null) {
26 | Logger.loge(TAG, "URI not recognized, bailing out");
27 | uriProcessListener.onProcessingFailure();
28 | return;
29 | }
30 | }
31 |
32 | final String mimeType = context.getContentResolver().getType(uri);
33 |
34 | new GetPhotosTask(context, uri, new GetPhotosTask.PhotosListener() {
35 | @Override
36 | public void didDownloadBitmap(String path) {
37 | Intent intent = new Intent();
38 | if(mimeType != null)
39 | intent.putExtra("mimeType", mimeType);
40 | intent.putExtra(URI, path);
41 | if(uriProcessListener != null)
42 | uriProcessListener.onProcessingSuccess(intent);
43 | }
44 |
45 | @Override
46 | public void didFail() {
47 | if(uriProcessListener != null)
48 | uriProcessListener.onProcessingFailure();
49 | return;
50 | }
51 | }).execute();
52 | }
53 |
54 | private static boolean isValidUri(Uri uri) {
55 | return "com.google.android.apps.photos.content".equals(uri.getAuthority());
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/main/java/dk/nodes/filepicker/bitmapHelper/FilePickerBitmapHelper.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker.bitmapHelper;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.net.Uri;
7 | import android.os.ParcelFileDescriptor;
8 | import android.support.annotation.NonNull;
9 |
10 | import java.io.File;
11 | import java.io.FileDescriptor;
12 | import java.io.FileNotFoundException;
13 | import java.io.FileOutputStream;
14 |
15 | public class FilePickerBitmapHelper {
16 |
17 | public static File writeBitmap(@NonNull Context context, @NonNull Bitmap bitmap, @NonNull Boolean externalStorage) throws Exception {
18 | return writeBitmap(context, bitmap, Bitmap.CompressFormat.PNG, externalStorage);
19 | }
20 |
21 | public static File writeBitmap(@NonNull Context context, @NonNull Bitmap bitmap, Bitmap.CompressFormat compressFormat, @NonNull Boolean externalStorage) throws Exception {
22 | File filesDir = externalStorage ? context.getExternalCacheDir() : context.getCacheDir();
23 | File file = new File(filesDir, "image.png");
24 | FileOutputStream fileOutputStream = new FileOutputStream(file);
25 | bitmap.compress(compressFormat, 90, fileOutputStream);
26 | return file;
27 | }
28 |
29 | public static BitmapFactory.Options getBitmapOptions(@NonNull Uri uri, @NonNull Context context) {
30 | BitmapFactory.Options o;
31 | o = new BitmapFactory.Options();
32 | o.inJustDecodeBounds = true;
33 | try {
34 | ParcelFileDescriptor parcelFileDescriptor =
35 | context.getContentResolver().openFileDescriptor(uri, "r");
36 | FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
37 | BitmapFactory.decodeFileDescriptor(fileDescriptor, null, o);
38 |
39 | // If the uri is a "file path", this will throw java.io.FileNotFoundException: No content provider
40 | // Try the old way
41 | } catch (FileNotFoundException fnfe) {
42 | File file = new File(uri.getPath());
43 | if (file.exists()) {
44 | BitmapFactory.decodeFile(uri.getPath(), o);
45 | }
46 | }
47 |
48 | return o;
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/java/dk/nodes/filepicker/processors/GoogleDriveProcessor.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker.processors;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.net.Uri;
6 |
7 | import dk.nodes.filepicker.processors.tasks.GetGoogleDriveFileTask;
8 | import dk.nodes.filepicker.utils.Logger;
9 |
10 | import static dk.nodes.filepicker.FilePickerConstants.URI;
11 |
12 | /**
13 | * Created by bison on 31/10/17.
14 | */
15 |
16 | public class GoogleDriveProcessor implements IUriProcessor {
17 | public static final String TAG = GoogleDriveProcessor.class.getSimpleName();
18 | UriProcessListener uriProcessListener;
19 |
20 | @Override
21 | public void process(Context context, Uri uri, final UriProcessListener uriProcessListener) {
22 | this.uriProcessListener = uriProcessListener;
23 | if(!isValidUri(uri))
24 | {
25 | if(uriProcessListener != null) {
26 | Logger.loge(TAG, "URI not recognized, bailing out");
27 | uriProcessListener.onProcessingFailure();
28 | return;
29 | }
30 | }
31 |
32 | final String mimeType = context.getContentResolver().getType(uri);
33 |
34 | new GetGoogleDriveFileTask(context, uri, new GetGoogleDriveFileTask.TaskListener() {
35 | @Override
36 | public void didSucceed(String newPath) {
37 | Intent intent = new Intent();
38 | if(mimeType != null)
39 | intent.putExtra("mimeType", mimeType);
40 | intent.putExtra(URI, newPath);
41 | if(uriProcessListener != null)
42 | uriProcessListener.onProcessingSuccess(intent);
43 | }
44 |
45 | @Override
46 | public void didFail() {
47 | if(uriProcessListener != null)
48 | uriProcessListener.onProcessingFailure();
49 | }
50 | }).execute();
51 | }
52 |
53 | private static boolean isValidUri(Uri uri) {
54 | return "com.google.android.apps.docs.storage".equals(uri.getAuthority())
55 | || "com.google.android.apps.docs.files".equals(uri.getAuthority())
56 | || "com.google.android.apps.docs.storage.legacy".equals(uri.getAuthority());
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/main/java/dk/nodes/filepicker/processors/UriProcessor.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker.processors;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.net.Uri;
6 | import android.util.Log;
7 |
8 | import java.util.ArrayList;
9 | import java.util.Iterator;
10 | import java.util.List;
11 |
12 | import dk.nodes.filepicker.utils.Logger;
13 |
14 | /**
15 | * Created by bison on 31/10/17.
16 | */
17 |
18 | public class UriProcessor implements UriProcessListener {
19 | private static final String TAG = UriProcessor.class.getSimpleName();
20 | private List processors = new ArrayList<>();
21 | private Iterator currentProcessorIt;
22 | private Uri uri;
23 | private Context context;
24 | private UriProcessListener listener;
25 |
26 | public UriProcessor() {
27 | // register URI processors
28 | processors.add(new GooglePhotosProcessor());
29 | processors.add(new GoogleDocumentsProcessor());
30 | processors.add(new GoogleMediaProcessor());
31 | processors.add(new GoogleDriveProcessor());
32 | processors.add(new GenericContentProviderProcessor());
33 | }
34 |
35 | public void process(Context context, Uri uri, UriProcessListener listener)
36 | {
37 | this.listener = listener;
38 | this.uri = uri;
39 | this.context = context;
40 | currentProcessorIt = processors.iterator();
41 | processNext(context, uri);
42 | }
43 |
44 | private void processNext(Context context, Uri uri)
45 | {
46 | if(currentProcessorIt.hasNext())
47 | {
48 | Logger.logd(TAG, "Processing next");
49 | IUriProcessor processor = currentProcessorIt.next();
50 | processor.process(context, uri, this);
51 | }
52 | else {
53 | Logger.loge(TAG, "No more processors to process, propagate failure back to caller");
54 | if(listener != null)
55 | listener.onProcessingFailure();
56 | }
57 | }
58 |
59 |
60 | @Override
61 | public void onProcessingSuccess(Intent intent) {
62 | Logger.logd(TAG, "onProcessingSuccess");
63 | if(listener != null)
64 | listener.onProcessingSuccess(intent);
65 |
66 | }
67 |
68 | @Override
69 | public void onProcessingFailure() {
70 | processNext(context, uri);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/filepicker.example/src/main/java/dk/nodes/filepickerexample/ImgurManager.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepickerexample;
2 |
3 | import android.util.Log;
4 |
5 | import java.io.File;
6 |
7 | import okhttp3.MediaType;
8 | import okhttp3.RequestBody;
9 | import retrofit2.Call;
10 | import retrofit2.Callback;
11 | import retrofit2.Response;
12 | import retrofit2.Retrofit;
13 | import retrofit2.converter.gson.GsonConverterFactory;
14 | import retrofit2.http.Headers;
15 | import retrofit2.http.Multipart;
16 | import retrofit2.http.POST;
17 | import retrofit2.http.Part;
18 |
19 | /**
20 | * Created by joso on 16/11/2016.
21 | */
22 |
23 | public class ImgurManager {
24 |
25 | ImgurService service;
26 |
27 | public ImgurManager() {
28 | Retrofit retrofit = new Retrofit.Builder()
29 | .baseUrl("https://api.imgur.com")
30 | .addConverterFactory(GsonConverterFactory.create())
31 | .build();
32 |
33 | service = retrofit.create(ImgurService.class);
34 | }
35 |
36 | public void uploadImage(File file, final UploadCallback callback) {
37 | RequestBody requestBody = RequestBody.create(MediaType.parse("image/*"), file);
38 |
39 | Call call = service.postImage(requestBody);
40 | call.enqueue(new Callback() {
41 | @Override
42 | public void onResponse(Call call, Response response) {
43 | if (response.isSuccessful() && callback != null) {
44 | callback.onUploaded(response.body());
45 | } else {
46 | Log.e("UploadImage", response.errorBody().toString());
47 | }
48 | }
49 |
50 | @Override
51 | public void onFailure(Call call, Throwable t) {
52 | Log.e("UploadImage", t.toString());
53 | }
54 | });
55 | }
56 |
57 |
58 | public interface ImgurService {
59 | @POST("3/image")
60 | @Multipart
61 | @Headers("Authorization: Client-ID c006fb01daee987")
62 | Call postImage(@Part("image") RequestBody image);
63 | }
64 |
65 | public interface UploadCallback {
66 | void onUploaded(ImageResponse response);
67 | }
68 |
69 | public class Image {
70 | public String id;
71 | public String title;
72 | public String link;
73 | }
74 |
75 | public class ImageResponse {
76 | public Image data;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/java/dk/nodes/filepicker/processors/tasks/GetPhotosTask.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker.processors.tasks;
2 |
3 |
4 | import android.content.Context;
5 | import android.net.Uri;
6 | import android.os.AsyncTask;
7 | import android.os.ParcelFileDescriptor;
8 | import android.util.Log;
9 |
10 | import java.io.BufferedInputStream;
11 | import java.io.BufferedOutputStream;
12 | import java.io.FileDescriptor;
13 | import java.io.FileInputStream;
14 | import java.io.FileOutputStream;
15 | import java.io.InputStream;
16 |
17 | import dk.nodes.filepicker.BuildConfig;
18 | import dk.nodes.filepicker.uriHelper.FilePickerUriHelper;
19 | import dk.nodes.filepicker.utils.Logger;
20 |
21 | /**
22 | * Created by joso on 26/03/15.
23 | */
24 | public class GetPhotosTask extends AsyncTask {
25 | public static final String TAG = GetPhotosTask.class.getSimpleName();
26 |
27 | private Context context;
28 | private Uri uri;
29 | private PhotosListener listener;
30 |
31 | public GetPhotosTask( Context context, Uri uri, PhotosListener listener ) {
32 | this.context = context;
33 | this.uri = uri;
34 | this.listener = listener;
35 | }
36 |
37 | @Override
38 | protected String doInBackground(Void... params) {
39 | try {
40 | ParcelFileDescriptor parcelFileDescriptor = context.getContentResolver()
41 | .openFileDescriptor(uri,"r");
42 |
43 | FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
44 | InputStream inputStream = new FileInputStream(fileDescriptor);
45 | BufferedInputStream reader = new BufferedInputStream(inputStream);
46 |
47 | String outputPath = FilePickerUriHelper.makeImageUri().toString();
48 |
49 | // Create an output stream to a file that you want to save to
50 | BufferedOutputStream outStream = new BufferedOutputStream(new FileOutputStream(outputPath));
51 | byte[] buf = new byte[2048];
52 | int len;
53 | while ((len = reader.read(buf)) > 0) {
54 | outStream.write(buf, 0, len);
55 | }
56 | return outputPath;
57 |
58 | } catch( Exception e ) {
59 | Logger.loge(TAG, e.toString());
60 | }
61 | return null;
62 | }
63 |
64 | @Override
65 | protected void onPostExecute(String s) {
66 | if( listener != null && context != null ) {
67 | if( s != null ) {
68 | listener.didDownloadBitmap(s);
69 | } else {
70 | listener.didFail();
71 | }
72 | }
73 | }
74 |
75 | public interface PhotosListener {
76 | public void didDownloadBitmap(String path);
77 | public void didFail();
78 | }
79 | }
80 |
81 |
--------------------------------------------------------------------------------
/app/maven-push.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'signing'
2 | apply plugin: 'maven'
3 | apply plugin: 'io.codearte.nexus-staging'
4 |
5 | // Username & password for Sonatype, stored in gradle.properties
6 | def _ossrhUsername = System.getenv('NEXUS_USERNAME')
7 | def _ossrhPassword = System.getenv('NEXUS_PASSWORD')
8 |
9 | allprojects { ext."signing.keyId" = System.getenv('keyId') }
10 | allprojects { ext."signing.secretKeyRingFile" = System.getenv('secretKeyRingFile') }
11 | allprojects { ext."signing.password" = System.getenv('password') }
12 |
13 | // Artifact settings
14 | def _group = 'dk.nodes.filepicker'
15 | def _version = '2.0.1'
16 | def _archivesBaseName = 'filepicker'
17 |
18 | nexusStaging {
19 | packageGroup = "dk.nodes"
20 | }
21 |
22 | def _name = 'FilePicker Library'
23 | def _description = 'Various tools used in android app development.'
24 |
25 |
26 | afterEvaluate { project ->
27 | uploadArchives {
28 | repositories {
29 | mavenDeployer {
30 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
31 |
32 | pom.groupId = _group
33 | pom.artifactId = _archivesBaseName
34 | pom.version = _version
35 |
36 | repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
37 | authentication(userName: _ossrhUsername, password: _ossrhPassword)
38 | }
39 |
40 | snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") {
41 | authentication(userName: _ossrhUsername, password: _ossrhPassword)
42 | }
43 |
44 | pom.project {
45 | name _name
46 | packaging 'aar'
47 | description _description
48 | url 'https://github.com/nodes-android/filepicker'
49 | inceptionYear '2016'
50 |
51 | scm {
52 | url 'https://github.com/nodes-android/filepicker'
53 | connection 'scm:https://github.com/nodes-android/filepicker.git'
54 | }
55 |
56 | licenses {
57 | license {
58 | name 'The Apache License, Version 2.0'
59 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
60 | }
61 | }
62 |
63 | developers {
64 | developer {
65 | id 'sanogueralorenzo'
66 | name 'Mario Sanoguera'
67 | email 'sanogueralorenzo@gmail.com'
68 | }
69 | }
70 |
71 | issueManagement {
72 | system 'GitHub issues'
73 | url 'https://github.com/nodes-android/filepicker/issues'
74 | }
75 | }
76 | }
77 | }
78 | }
79 |
80 | signing {
81 | required { gradle.taskGraph.hasTask("uploadArchives") }
82 | sign configurations.archives
83 | }
84 |
85 | task androidSourcesJar(type: Jar) {
86 | classifier = 'sources'
87 | from android.sourceSets.main.java.sourceFiles
88 | }
89 |
90 | artifacts {
91 | archives androidSourcesJar
92 | }
93 |
94 | }
--------------------------------------------------------------------------------
/filepicker.example/src/main/res/layout/activity_file_picker_example.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
17 |
18 |
23 |
24 |
30 |
31 |
36 |
37 |
42 |
43 |
48 |
49 |
54 |
55 |
56 |
57 |
62 |
63 |
68 |
69 |
74 |
75 |
76 |
77 |
82 |
83 |
88 |
89 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/app/src/main/java/dk/nodes/filepicker/processors/GoogleDocumentsProcessor.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker.processors;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.database.Cursor;
6 | import android.net.Uri;
7 | import android.os.Environment;
8 | import android.provider.MediaStore;
9 |
10 | import dk.nodes.filepicker.utils.Logger;
11 |
12 | import static dk.nodes.filepicker.FilePickerConstants.URI;
13 |
14 | /**
15 | * Created by bison on 31/10/17.
16 | */
17 |
18 | public class GoogleDocumentsProcessor implements IUriProcessor {
19 | public static final String TAG = GoogleDocumentsProcessor.class.getSimpleName();
20 | UriProcessListener uriProcessListener;
21 |
22 | @Override
23 | public void process(Context context, Uri uri, UriProcessListener uriProcessListener) {
24 | this.uriProcessListener = uriProcessListener;
25 | if(!isValidUri(uri))
26 | {
27 | if(uriProcessListener != null) {
28 | Logger.loge(TAG, "URI not recognized, bailing out");
29 | uriProcessListener.onProcessingFailure();
30 | return;
31 | }
32 | }
33 | final String mimeType = context.getContentResolver().getType(uri);
34 |
35 | String id = uri.getLastPathSegment().split(":")[1];
36 | boolean isVideo = uri.getLastPathSegment().split(":")[0].contains("video");
37 | final String[] imageColumns = {MediaStore.Images.Media.DATA};
38 | final String[] videoColumns = {MediaStore.Video.Media.DATA};
39 | final String imageOrderBy = null;
40 | Uri baseUri;
41 | String state = Environment.getExternalStorageState();
42 | if (! isVideo) {
43 | if (! state.equalsIgnoreCase(Environment.MEDIA_MOUNTED)) {
44 | baseUri = MediaStore.Images.Media.INTERNAL_CONTENT_URI;
45 | } else {
46 | baseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
47 | }
48 | } else {
49 | if (! state.equalsIgnoreCase(Environment.MEDIA_MOUNTED)) {
50 | baseUri = MediaStore.Video.Media.INTERNAL_CONTENT_URI;
51 | } else {
52 | baseUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
53 | }
54 | }
55 |
56 | String selectedPath = "path";
57 | Cursor cursor = null;
58 | if (!isVideo) {
59 | cursor = context.getContentResolver().query(baseUri, imageColumns,
60 | MediaStore.Images.Media._ID + "=" + id, null, imageOrderBy);
61 | } else {
62 | cursor = context.getContentResolver().query(baseUri, videoColumns,
63 | MediaStore.Video.Media._ID + "=" + id, null, imageOrderBy);
64 | }
65 |
66 | if(cursor == null)
67 | {
68 | Logger.loge(TAG, "cursor is null");
69 | if(uriProcessListener != null)
70 | uriProcessListener.onProcessingFailure();
71 | return;
72 | }
73 |
74 | if (cursor.moveToFirst()) {
75 | if (! isVideo) {
76 | selectedPath = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
77 | } else {
78 | selectedPath = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATA));
79 | }
80 | }
81 | cursor.close();
82 |
83 | Intent intent = new Intent();
84 | if(mimeType != null)
85 | intent.putExtra("mimeType", mimeType);
86 | intent.putExtra(URI, Uri.parse(selectedPath));
87 | if(uriProcessListener != null)
88 | uriProcessListener.onProcessingSuccess(intent);
89 | }
90 |
91 | private static boolean isValidUri(Uri uri) {
92 | return "com.google.android.providers.media.documents".equals(uri.getAuthority());
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/app/src/main/java/dk/nodes/filepicker/processors/tasks/GetFileTask.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker.processors.tasks;
2 |
3 | import android.content.Context;
4 | import android.net.Uri;
5 | import android.os.AsyncTask;
6 | import android.os.Build;
7 | import android.os.ParcelFileDescriptor;
8 | import android.util.Log;
9 |
10 | import java.io.File;
11 | import java.io.FileDescriptor;
12 | import java.io.FileInputStream;
13 | import java.io.FileNotFoundException;
14 | import java.io.FileOutputStream;
15 | import java.io.InputStream;
16 | import java.io.OutputStream;
17 | import java.net.URL;
18 | import java.text.SimpleDateFormat;
19 | import java.util.Date;
20 |
21 | import dk.nodes.filepicker.BuildConfig;
22 | import dk.nodes.filepicker.utils.Logger;
23 |
24 | /**
25 | * Created by Nicolaj on 15-10-2015.
26 | */
27 | public class GetFileTask extends AsyncTask {
28 | private static final String TAG = GetFileTask.class.getSimpleName();
29 |
30 | private Context context;
31 | private Uri uri;
32 | private TaskListener listener;
33 |
34 | public GetFileTask(Context context, Uri uri, TaskListener listener) {
35 | this.context = context;
36 | this.uri = uri;
37 | this.listener = listener;
38 | }
39 |
40 | @Override
41 | protected String doInBackground(Void... params) {
42 | File cacheDir = new File(android.os.Environment.getExternalStorageDirectory(), "eboks");
43 |
44 | if (! cacheDir.exists()) {
45 | cacheDir.mkdirs();
46 | }
47 |
48 | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd-HHmmss");
49 | String fileName = "IMG_" + dateFormat.format(new Date()) + ".jpg";
50 | File f = new File(cacheDir, fileName);
51 |
52 | try {
53 | InputStream is;
54 | if (uri.toString().startsWith("content://com.google.android")) {
55 | is = getSourceStream(uri, context);
56 | } else {
57 | is = new URL(uri.toString()).openStream();
58 | }
59 | OutputStream os = new FileOutputStream(f);
60 |
61 |
62 | copyStream(is, os);
63 | os.close();
64 | return Uri.fromFile(f).toString();
65 | } catch (Exception ex) {
66 | Logger.loge(TAG, ex.toString());
67 | return null;
68 | }
69 | }
70 |
71 | @Override
72 | protected void onPostExecute(String s) {
73 | if( listener != null && context != null ) {
74 | if( s != null ) {
75 | listener.didSucceed(s);
76 | } else {
77 | listener.didFail();
78 | }
79 | }
80 | }
81 |
82 | private void copyStream(InputStream is, OutputStream os) {
83 | final int bufferSize = 1024;
84 | try {
85 | byte[] bytes = new byte[bufferSize];
86 | while (true) {
87 | int count = is.read(bytes, 0, bufferSize);
88 | if (count == - 1) {
89 | break;
90 | }
91 | os.write(bytes, 0, count);
92 | }
93 | } catch (Exception ex) {
94 | Logger.loge(TAG, ex.toString());
95 | }
96 | }
97 |
98 | private InputStream getSourceStream(Uri u, Context context) throws FileNotFoundException {
99 | InputStream out;
100 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
101 | ParcelFileDescriptor parcelFileDescriptor =
102 | context.getContentResolver().openFileDescriptor(u, "r");
103 | FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
104 | out = new FileInputStream(fileDescriptor);
105 | } else {
106 | out = context.getContentResolver().openInputStream(u);
107 | }
108 | return out;
109 | }
110 |
111 | public interface TaskListener{
112 | public void didSucceed(String path);
113 | public void didFail();
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FilePicker
2 | The purpose of this library is taking whatever URI comes back from SAF (Storage Access Framework) or other android
3 | storage providers and turning it into an easy to read local file URI.
4 |
5 | ## Download
6 | Gradle:
7 | ```
8 | dependencies {
9 | compile 'dk.nodes.filepicker:filepicker:2.0.+'
10 | }
11 | ```
12 |
13 | The plus above might require you to upgrade the gradle plugin in AS:
14 | open $PROJECT_ROOT/gradle/wrapper/gradle-wrapper.properties and correct the line to:
15 |
16 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
17 |
18 | ## Wait, what is this?
19 |
20 | **FilePicker** Is an Android library that will get the Uri you need. It even has some Uri helper methods!
21 |
22 | ## How is that even possible?
23 |
24 | Calm down, **FilePicker** is just a transparent Activity that will handle all of the work related to getting an Uri from your phone, this includes taking a camera picture and in the next version even video recordings! Example Activity can be found [here](https://github.com/nodes-android/filepicker/blob/master/filepicker.example/src/main/java/dk/nodes/filepickerexample/FilePickerExampleActivity.java).
25 |
26 | **FilePicker** Is a translucent Activity so you won't have to worry about breaking the design. It uses the Android Native chooser so it will adapt to the different Android versions and OEMs! You will only get the Native chooser if you don't add any extras to the intent.
27 |
28 | 
29 |
30 | ## Usage
31 |
32 | In the Activity or Fragment class:
33 | ```
34 | Intent intent = new Intent(MainActivity.this, FilePickerActivity.class);
35 | startActivityForResult(intent, MY_REQUEST_CODE);
36 | ```
37 | This will prompt the native chooser with the options of Camera and File (only Images).
38 |
39 | Since its an activity you can configure everything via intent.putExtra();
40 |
41 | **To change the chooser text:**
42 |
43 | ```
44 | intent.putExtra(FilePickerActivity.CHOOSER_TEXT, "Please select an action");
45 | ```
46 |
47 | **To get the camera directly:**
48 |
49 | ```
50 | intent.putExtra(FilePickerConstants.CAMERA, true);
51 | ```
52 |
53 | **To get the file picker directly with only images:**
54 |
55 | ```
56 | intent.putExtra(FilePickerConstants.FILE, true);
57 | ```
58 |
59 |
60 | **To get the file picker directly with only 1 specific MIME type:**
61 | ```
62 | intent.putExtra(FilePickerConstants.FILE, true);
63 | intent.putExtra(FilePickerConstants.TYPE, FilePickerConstants.MIME_PDF);
64 | ```
65 |
66 | **To get the file picker with multiple MIME type just send a String Array:**
67 |
68 | ```
69 | intent.putExtra(FilePickerConstants.FILE, true);
70 | intent.putExtra(FilePickerConstants.MULTIPLE_TYPES, new String[]{FilePickerConstants.MIME_IMAGE, FilePickerConstants.MIME_PDF});
71 | ```
72 |
73 | Here is how you would handle the onActivityResult:
74 |
75 | ```
76 | @Override
77 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
78 | super.onActivityResult(requestCode, resultCode, data);
79 | if (requestCode == MY_REQUEST_CODE) {
80 | if (resultCode == RESULT_OK) {
81 | Toast.makeText(FilePickerExampleActivity.this, FilePickerUriHelper.getUriString(data), Toast.LENGTH_SHORT).show();
82 | //If its not an image we don't load any of the image views
83 | if (!isImage) {
84 | return;
85 | }
86 | //Imageview setImageUri with URI
87 | uriIv.setImageURI(FilePickerUriHelper.getUri(data));
88 | //Glide loading with URI
89 | Glide.with(this).load(FilePickerUriHelper.getUri(data)).into(glideIv);
90 | //Imageview setImageUri with URI from File
91 | fileIv.setImageURI(Uri.fromFile(FilePickerUriHelper.getFile(this, data)));
92 | } else if (resultCode == RESULT_CANCELED) {
93 | Toast.makeText(FilePickerExampleActivity.this, "User Canceled", Toast.LENGTH_SHORT).show();
94 | } else if (resultCode == RESULT_CODE_FAILURE) {
95 | Toast.makeText(FilePickerExampleActivity.this, "Failed", Toast.LENGTH_SHORT).show();
96 | }
97 | }
98 | }
99 | ```
100 |
101 |
102 | ### Please Note
103 |
104 | This Library will handle the permissions and (if the phone has apps that can deal with that MIME type) it will return the Uri.
105 |
106 | If you want some helper methods please feel free to use FilePickerUriHelper class.
107 |
108 | **How to retrieve the Uri String from the intent:**
109 | ```
110 | String uriString = FilePickerUriHelper.getUriString(intent);
111 | ```
112 |
113 | **How to retrieve the Uri from the intent:**
114 | ```
115 | Uri uri = FilePickerUriHelper.getUri(intent);
116 | ```
117 |
118 | **How to get the file from the intent:**
119 | ```
120 | File file = FilePickerUriHelper.getFile(context, intent);
121 | ```
122 |
123 | **How to load with Gliide from the parsed Uri:**
124 | ```
125 | Uri uri = FilePickerUriHelper.getUri(intent);
126 | Glide.with(this).load(uri).into(imageView);
127 | ```
128 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/app/src/main/java/dk/nodes/filepicker/processors/tasks/GetGoogleDriveFileTask.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker.processors.tasks;
2 |
3 | import android.content.Context;
4 | import android.database.Cursor;
5 | import android.net.Uri;
6 | import android.os.AsyncTask;
7 | import android.os.Environment;
8 | import android.provider.OpenableColumns;
9 | import android.util.Log;
10 |
11 | import java.io.File;
12 | import java.io.FileNotFoundException;
13 | import java.io.FileOutputStream;
14 | import java.io.IOException;
15 | import java.io.InputStream;
16 | import java.io.OutputStream;
17 |
18 | import dk.nodes.filepicker.BuildConfig;
19 | import dk.nodes.filepicker.uriHelper.FilePickerUriHelper;
20 | import dk.nodes.filepicker.utils.Logger;
21 |
22 | /**
23 | * Created by Nicolaj on 15-10-2015.
24 | */
25 | public class GetGoogleDriveFileTask extends AsyncTask {
26 | private static final String TAG = GetGoogleDriveFileTask.class.getSimpleName();
27 |
28 | private Context context;
29 | private Uri uri;
30 | private TaskListener listener;
31 |
32 | public GetGoogleDriveFileTask(Context context, Uri uri, TaskListener listener) {
33 | this.context = context;
34 | this.uri = uri;
35 | this.listener = listener;
36 | }
37 |
38 | @Override
39 | protected String doInBackground(Void... params) {
40 | Log.i(TAG, "Google drive: " + uri.toString());
41 |
42 | Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
43 | File outputFile = null;
44 | try {
45 | // moveToFirst() returns false if the cursor has 0 rows. Very handy for
46 | // "if there's anything to look at, look at it" conditionals.
47 | if (cursor != null && cursor.moveToFirst()) {
48 |
49 | // Note it's called "Display Name". This is
50 | // provider-specific, and might not necessarily be the file name.
51 | String displayName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
52 | Log.i(TAG, "Display Name: " + displayName);
53 | int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
54 | // If the size is unknown, the value stored is null. But since an
55 | // int can't be null in Java, the behavior is implementation-specific,
56 | // which is just a fancy term for "unpredictable". So as
57 | // a rule, check if it's null before assigning to an int. This will
58 | // happen often: The storage API allows for remote files, whose
59 | // size might not be locally known.
60 | String size = null;
61 | if (!cursor.isNull(sizeIndex)) {
62 | // Technically the column stores an int, but cursor.getString()
63 | // will do the conversion automatically.
64 | size = cursor.getString(sizeIndex);
65 | } else {
66 | size = "Unknown";
67 | }
68 | Log.i(TAG, "Size: " + size);
69 | //outputFile = new File(Environment.getExternalStorageDirectory(), displayName.replace(" ","_"));
70 |
71 | outputFile = new File(Environment.getExternalStorageDirectory(), displayName);
72 | OutputStream os = new FileOutputStream(outputFile);
73 |
74 | InputStream is = null;
75 | if(FilePickerUriHelper.isVirtualFile(context, uri))
76 | {
77 | Logger.logd(TAG, "File is virtual, commencing virtual file procedures");
78 | try {
79 | is = FilePickerUriHelper.getInputStreamForVirtualFile(context, uri, "*/*");
80 | }
81 | catch (IOException e)
82 | {
83 | e.printStackTrace();
84 | throw(new FileNotFoundException("Could not get file descriptor for virtual file"));
85 | }
86 | }
87 | else {
88 | Logger.loge(TAG, "Looks like we got ourselves a regular ole' file");
89 | is = context.getContentResolver().openInputStream(uri);
90 | }
91 |
92 | final int bufferSize = 1024;
93 | try {
94 | byte[] bytes = new byte[bufferSize];
95 | while (true) {
96 | int count = is.read(bytes, 0, bufferSize);
97 | if (count == - 1) {
98 | break;
99 | }
100 | os.write(bytes, 0, count);
101 | }
102 | } catch (Exception ex) {
103 | ex.printStackTrace();
104 | Logger.loge(TAG, ex.toString());
105 | }
106 |
107 | }
108 | } catch (FileNotFoundException e) {
109 | e.printStackTrace();
110 | Logger.loge(TAG, e.toString());
111 | } finally {
112 | if(cursor != null)
113 | cursor.close();
114 |
115 | }
116 | return Uri.fromFile(outputFile).toString();
117 | }
118 |
119 | @Override
120 | protected void onPostExecute(String s) {
121 | if( listener != null && context != null ) {
122 | if( s != null ) {
123 | listener.didSucceed(s);
124 | } else {
125 | listener.didFail();
126 | }
127 | }
128 | }
129 |
130 | public interface TaskListener{
131 | void didSucceed(String path);
132 | void didFail();
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/filepicker.example/src/main/java/dk/nodes/filepickerexample/FilePickerExampleActivity.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepickerexample;
2 |
3 | import android.content.Intent;
4 | import android.net.Uri;
5 | import android.os.Bundle;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.support.v7.widget.Toolbar;
8 | import android.view.View;
9 | import android.widget.Button;
10 | import android.widget.ImageView;
11 | import android.widget.Toast;
12 |
13 | import com.bumptech.glide.Glide;
14 |
15 | import java.io.File;
16 |
17 | import dk.nodes.filepicker.FilePickerActivity;
18 | import dk.nodes.filepicker.FilePickerConstants;
19 | import dk.nodes.filepicker.uriHelper.FilePickerUriHelper;
20 |
21 | import static dk.nodes.filepicker.FilePickerConstants.RESULT_CODE_FAILURE;
22 |
23 | public class FilePickerExampleActivity extends AppCompatActivity {
24 |
25 | public static int MY_REQUEST_CODE = 10;
26 | final String DEFAULT = "DEFAULT";
27 | final String CAMERA = "CAMERA";
28 | final String FILE_IMAGE = "FILE IMAGE";
29 | final String FILE_TYPE = "FILE TYPE";
30 | final String FILE_MULTIPLE_TYPES = "FILE MULTIPLE TYPES";
31 |
32 | final String DEFAULT_INTENT = "";
33 | final String CAMERA_INTENT = "intent.putExtra(FilePickerActivity.CAMERA, true);";
34 | final String FILE_IMAGE_INTENT = "intent.putExtra(FilePickerActivity.FILE, true);";
35 | final String FILE_TYPE_INTENT = "intent.putExtra(FilePickerActivity.FILE, true);\nintent.putExtra(FilePickerActivity.TYPE, FilePickerActivity.MIME_PDF);";
36 | final String FILE_MULTIPLE_TYPES_INTENT = "intent.putExtra(FilePickerActivity.FILE, true);\nintent.putExtra(FilePickerActivity.MULTIPLE_TYPES, new String[]{FilePickerActivity.MIME_IMAGE, FilePickerActivity.MIME_PDF});";
37 | Button typeBtn;
38 | Toolbar toolbar;
39 | ImageView uriIv, glideIv, fileIv;
40 | Button startActivityForResultBtn;
41 | Intent intent;
42 |
43 | @Override
44 | protected void onCreate(Bundle savedInstanceState) {
45 | super.onCreate(savedInstanceState);
46 | setContentView(R.layout.activity_file_picker_example);
47 | toolbar = (Toolbar) findViewById(R.id.toolbar);
48 | typeBtn = (Button) findViewById(R.id.type_btn);
49 | uriIv = (ImageView) findViewById(R.id.uri_iv);
50 | glideIv = (ImageView) findViewById(R.id.glide_iv);
51 | fileIv = (ImageView) findViewById(R.id.file_iv);
52 | startActivityForResultBtn = (Button) findViewById(R.id.start_activity_for_result_btn);
53 |
54 | toolbar.setTitle(DEFAULT);
55 | typeBtn.setText(DEFAULT_INTENT);
56 | newIntent();
57 |
58 | typeBtn.setOnClickListener(new View.OnClickListener() {
59 | @Override
60 | public void onClick(View view) {
61 | setButton();
62 | }
63 | });
64 |
65 | startActivityForResultBtn.setOnClickListener(new View.OnClickListener() {
66 | @Override
67 | public void onClick(View view) {
68 | startActivityForResult(intent, MY_REQUEST_CODE);
69 | }
70 | });
71 | }
72 |
73 | @Override
74 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
75 | super.onActivityResult(requestCode, resultCode, data);
76 | if (requestCode == MY_REQUEST_CODE) {
77 | if (resultCode == RESULT_OK) {
78 | Toast.makeText(FilePickerExampleActivity.this, FilePickerUriHelper.getUriString(data), Toast.LENGTH_SHORT).show();
79 | //If its not an image we don't load it
80 | if (toolbar.getTitle().toString().equals(FILE_TYPE) || toolbar.getTitle().toString().equals(FILE_MULTIPLE_TYPES)) {
81 | return;
82 | }
83 | uriIv.setImageURI(FilePickerUriHelper.getUri(data));
84 | Glide.with(this).load(FilePickerUriHelper.getUri(data)).into(glideIv);
85 | File file = FilePickerUriHelper.getFile(this, data);
86 | if (file == null) {
87 | return;
88 | }
89 | fileIv.setImageURI(Uri.fromFile(file));
90 | ImgurManager imgurManager = new ImgurManager();
91 | imgurManager.uploadImage(FilePickerUriHelper.getFile(FilePickerExampleActivity.this, data), new ImgurManager.UploadCallback() {
92 | @Override
93 | public void onUploaded(ImgurManager.ImageResponse response) {
94 | Toast.makeText(FilePickerExampleActivity.this, "Image Uploaded to Imgur", Toast.LENGTH_SHORT).show();
95 | }
96 | });
97 | } else if (resultCode == RESULT_CANCELED) {
98 | Toast.makeText(FilePickerExampleActivity.this, "User Canceled", Toast.LENGTH_SHORT).show();
99 | } else if (resultCode == RESULT_CODE_FAILURE) {
100 | Toast.makeText(FilePickerExampleActivity.this, "Failed", Toast.LENGTH_SHORT).show();
101 | }
102 | }
103 | }
104 |
105 | void newIntent() {
106 | intent = new Intent(FilePickerExampleActivity.this, FilePickerActivity.class);
107 | }
108 |
109 | void setButton() {
110 | newIntent();
111 | String buttonText = typeBtn.getText().toString();
112 | switch (buttonText) {
113 | case DEFAULT_INTENT:
114 | toolbar.setTitle(CAMERA);
115 | buttonText = CAMERA_INTENT;
116 | intent.putExtra(FilePickerConstants.CAMERA, true);
117 | break;
118 | case CAMERA_INTENT:
119 | toolbar.setTitle(FILE_IMAGE);
120 | buttonText = FILE_IMAGE_INTENT;
121 | intent.putExtra(FilePickerConstants.FILE, true);
122 | break;
123 | case FILE_IMAGE_INTENT:
124 | toolbar.setTitle(FILE_TYPE);
125 | buttonText = FILE_TYPE_INTENT;
126 | intent.putExtra(FilePickerConstants.FILE, true);
127 | intent.putExtra(FilePickerConstants.TYPE, FilePickerConstants.MIME_PDF);
128 | break;
129 | case FILE_TYPE_INTENT:
130 | toolbar.setTitle(FILE_MULTIPLE_TYPES);
131 | buttonText = FILE_MULTIPLE_TYPES_INTENT;
132 | intent.putExtra(FilePickerConstants.FILE, true);
133 | intent.putExtra(FilePickerConstants.MULTIPLE_TYPES, new String[]{FilePickerConstants.MIME_PDF, FilePickerConstants.MIME_VIDEO});
134 | break;
135 | case FILE_MULTIPLE_TYPES_INTENT:
136 | toolbar.setTitle(DEFAULT);
137 | buttonText = DEFAULT_INTENT;
138 | break;
139 | }
140 | typeBtn.setText(buttonText);
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/app/src/main/java/dk/nodes/filepicker/uriHelper/FilePickerUriHelper.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker.uriHelper;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.ContentResolver;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.database.Cursor;
8 | import android.net.Uri;
9 | import android.os.Build;
10 | import android.os.Environment;
11 | import android.provider.DocumentsContract;
12 | import android.provider.MediaStore;
13 | import android.support.annotation.NonNull;
14 | import android.util.Log;
15 | import android.webkit.MimeTypeMap;
16 |
17 | import java.io.File;
18 | import java.io.FileInputStream;
19 | import java.io.FileNotFoundException;
20 | import java.io.FileOutputStream;
21 | import java.io.IOException;
22 | import java.io.InputStream;
23 | import java.io.OutputStream;
24 | import java.text.SimpleDateFormat;
25 | import java.util.Date;
26 |
27 | import dk.nodes.filepicker.utils.Logger;
28 |
29 | import static dk.nodes.filepicker.FilePickerConstants.URI;
30 |
31 | public class FilePickerUriHelper {
32 | public static final String TAG = FilePickerUriHelper.class.getSimpleName();
33 |
34 | public static String getUriString(@NonNull Intent intent) {
35 | if(intent.getData() != null) {
36 | return intent.getData().toString();
37 | }
38 |
39 | return intent.getExtras().getString(URI);
40 | }
41 |
42 | public static Uri getUri(@NonNull Intent intent) {
43 | return Uri.parse(getUriString(intent));
44 | }
45 |
46 | public static File getFile(@NonNull Context context, @NonNull Intent intent) {
47 | return getFile(context, getUriString(intent));
48 | }
49 |
50 | public static File getFile(@NonNull Context context, @NonNull Uri uri) {
51 | return getFile(context, uri.toString());
52 | }
53 |
54 | @TargetApi(19)
55 | public static File getFile(@NonNull Context context, @NonNull String uriString) {
56 | String filePath = getFilePath(context, uriString);
57 | if (filePath == null) {
58 | return null;
59 | }
60 | return new File(filePath);
61 | }
62 |
63 | @TargetApi(19)
64 | private static String getFilePath(@NonNull Context context, @NonNull String uriString) {
65 | File fileCheck = new File(uriString);
66 | if(fileCheck.exists()) {
67 | return uriString;
68 | }
69 |
70 | String filePath = null;
71 | Uri uri = Uri.parse(uriString);
72 | if (uri == null) {
73 | return null;
74 | }
75 |
76 | if(new File(uri.getPath()).exists()) {
77 | return uri.getPath();
78 | }
79 |
80 | Cursor cursor = null;
81 | // Used the new photos app which uses a different API
82 | if (uriString.contains("providers.media.documents/")) {
83 | // Will return "image:x*"
84 | String wholeID = DocumentsContract.getDocumentId(uri);
85 | // Split at colon, use second item in the array
86 | String id = wholeID.split(":")[1];
87 | String[] column = null;
88 | if(wholeID.contains("image")) {
89 | String[] iColumn = {MediaStore.Images.Media.DATA};
90 | // where id is equal to
91 | String sel = MediaStore.Images.Media._ID + "=?";
92 | cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, iColumn, sel, new String[]{id}, null);
93 | column = iColumn;
94 | }
95 | else if(wholeID.contains("video")) {
96 | String[] vColumn = {MediaStore.Video.Media.DATA};
97 | // where id is equal to
98 | String videoSel = MediaStore.Video.Media._ID + "=?";
99 | cursor = context.getContentResolver().query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, vColumn, videoSel, new String[]{id}, null);
100 | column = vColumn;
101 | }
102 |
103 | if (cursor == null) { // nor video
104 | return null;
105 | }
106 | int columnIndex = cursor.getColumnIndex(column[0]);
107 | if (cursor.moveToFirst()) {
108 | filePath = cursor.getString(columnIndex);
109 | }
110 | cursor.close();
111 | } else {
112 | String[] filePathColumn = {MediaStore.Images.Media.DATA, MediaStore.MediaColumns.DATA};
113 | cursor = context.getContentResolver().query(uri, filePathColumn, null, null, null);
114 | if (cursor == null) {
115 | return null;
116 | }
117 | cursor.moveToFirst();
118 | int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
119 | filePath = cursor.getString(columnIndex);
120 | cursor.close();
121 | }
122 | return filePath;
123 | }
124 |
125 | public static Uri makeImageUri() {
126 | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd-HHmmss");
127 | String fileName = dateFormat.format(new Date()) + ".jpg";
128 | File photo = new File(Environment.getExternalStorageDirectory(), fileName);
129 | Uri outputUri = Uri.fromFile(photo);
130 | return outputUri;
131 | }
132 |
133 | public static String getFileType(Context context, Uri uri) {
134 | try {
135 | String mimeType = context.getContentResolver().getType(uri);
136 | String extension;
137 | Logger.logd(TAG, "uri: " + uri.toString());
138 | if(uri.getScheme() == null)
139 | {
140 | Logger.logd(TAG, "Uri.scheme == null");
141 | }
142 | if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
143 | Logger.loge(TAG, "mime.getExtensionFromMimeType: " + mimeType);
144 | // WORKAROUND: we got a device with a buggy cam app that sets the incorrect mimetype, correct it
145 | if("image/jpg".contentEquals(mimeType))
146 | {
147 | mimeType = "image/jpeg";
148 | }
149 | final MimeTypeMap mime = MimeTypeMap.getSingleton();
150 | extension = mime.getExtensionFromMimeType(mimeType);
151 | Logger.logd(TAG, "extension: " + extension);
152 | } else {
153 | Logger.logd(TAG, "MimeTypeMap.getFileExtensionFromUrl");
154 | extension = MimeTypeMap.getFileExtensionFromUrl(Uri.fromFile(new File(uri.getPath())).toString());
155 | }
156 | return extension;
157 | } catch (Exception e) {
158 | e.printStackTrace();
159 | return null;
160 | }
161 | }
162 |
163 | public boolean detectPDF(File file) {
164 | try {
165 | if (file == null)
166 | return false;
167 | InputStream is = new FileInputStream(file);
168 | byte[] buf = new byte[4];
169 |
170 | // 25 50 44 46
171 | if(is.read(buf, 0, 4) == 4)
172 | {
173 | if (buf[0] == 0x25 && buf[1] == 0x50 &&
174 | buf[2] == 0x44 && buf[3] == 0x46) {
175 | is.close();
176 | return true;
177 | }
178 | }
179 | is.close();
180 | return false;
181 | }
182 | catch(Exception e)
183 | {
184 | e.printStackTrace();
185 | return false;
186 | }
187 | }
188 |
189 | public static boolean isVirtualFile(Context context, Uri uri) {
190 | // virtual files is an android 7 thing
191 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
192 | {
193 | return false;
194 | }
195 |
196 | if (!DocumentsContract.isDocumentUri(context, uri)) {
197 | return false;
198 | }
199 |
200 | Cursor cursor = context.getContentResolver().query(
201 | uri,
202 | new String[] { DocumentsContract.Document.COLUMN_FLAGS },
203 | null, null, null);
204 |
205 | int flags = 0;
206 | if (cursor.moveToFirst()) {
207 | flags = cursor.getInt(0);
208 | }
209 | cursor.close();
210 |
211 | return (flags & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0;
212 | }
213 |
214 | public static InputStream getInputStreamForVirtualFile(Context context, Uri uri, String mimeTypeFilter) throws IOException
215 | {
216 | ContentResolver resolver = context.getContentResolver();
217 |
218 | String[] openableMimeTypes = resolver.getStreamTypes(uri, mimeTypeFilter);
219 |
220 | if (openableMimeTypes == null ||
221 | openableMimeTypes.length < 1) {
222 | throw new FileNotFoundException();
223 | }
224 |
225 | return resolver
226 | .openTypedAssetFileDescriptor(uri, openableMimeTypes[0], null)
227 | .createInputStream();
228 | }
229 | }
230 |
--------------------------------------------------------------------------------
/app/src/main/java/dk/nodes/filepicker/FilePickerActivity.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker;
2 |
3 | import android.Manifest;
4 | import android.content.Intent;
5 | import android.content.pm.PackageManager;
6 | import android.graphics.Bitmap;
7 | import android.net.Uri;
8 | import android.os.Build;
9 | import android.os.Bundle;
10 | import android.os.Handler;
11 | import android.support.annotation.NonNull;
12 | import android.support.v7.app.AppCompatActivity;
13 | import android.util.Log;
14 | import android.view.View;
15 | import android.widget.FrameLayout;
16 |
17 | import java.io.File;
18 |
19 | import dk.nodes.filepicker.bitmapHelper.FilePickerBitmapHelper;
20 | import dk.nodes.filepicker.intentHelper.FilePickerCameraIntent;
21 | import dk.nodes.filepicker.intentHelper.FilePickerChooserIntent;
22 | import dk.nodes.filepicker.intentHelper.FilePickerFileIntent;
23 | import dk.nodes.filepicker.processors.UriProcessListener;
24 | import dk.nodes.filepicker.processors.UriProcessor;
25 | import dk.nodes.filepicker.utils.Logger;
26 |
27 | import static dk.nodes.filepicker.BuildConfig.DEBUG;
28 | import static dk.nodes.filepicker.FilePickerConstants.CAMERA;
29 | import static dk.nodes.filepicker.FilePickerConstants.CHOOSER_TEXT;
30 | import static dk.nodes.filepicker.FilePickerConstants.DOWNLOAD_IF_NON_LOCAL;
31 | import static dk.nodes.filepicker.FilePickerConstants.FILE;
32 | import static dk.nodes.filepicker.FilePickerConstants.MULTIPLE_TYPES;
33 | import static dk.nodes.filepicker.FilePickerConstants.PERMISSION_REQUEST_CODE;
34 | import static dk.nodes.filepicker.FilePickerConstants.REQUEST_CODE;
35 | import static dk.nodes.filepicker.FilePickerConstants.RESULT_CODE_FAILURE;
36 | import static dk.nodes.filepicker.FilePickerConstants.TYPE;
37 | import static dk.nodes.filepicker.FilePickerConstants.URI;
38 | import static dk.nodes.filepicker.permissionHelper.FilePickerPermissionHelper.askPermission;
39 | import static dk.nodes.filepicker.permissionHelper.FilePickerPermissionHelper.requirePermission;
40 |
41 | public class FilePickerActivity extends AppCompatActivity implements UriProcessListener {
42 | public static final String TAG = FilePickerActivity.class.getSimpleName();
43 | Uri outputFileUri;
44 | FrameLayout rootFl;
45 | String chooserText = "Choose an action";
46 | private boolean download = true;
47 | UriProcessor uriProcessor;
48 | String uriString = null;
49 |
50 |
51 | @Override
52 | protected void onCreate(Bundle savedInstanceState) {
53 | super.onCreate(savedInstanceState);
54 | setContentView(R.layout.activity_file_picker);
55 | rootFl = (FrameLayout) findViewById(R.id.rootFl);
56 |
57 | uriProcessor = new UriProcessor();
58 |
59 | if (getIntent().getExtras() != null && getIntent().getExtras().containsKey(CHOOSER_TEXT)) {
60 | chooserText = getIntent().getStringExtra(CHOOSER_TEXT);
61 | }
62 | if (getIntent().getExtras() != null && getIntent().getExtras().containsKey(DOWNLOAD_IF_NON_LOCAL)) {
63 | download = getIntent().getBooleanExtra(DOWNLOAD_IF_NON_LOCAL, true);
64 | }
65 | if (requirePermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA)) {
66 | askPermission(this, PERMISSION_REQUEST_CODE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA);
67 | } else {
68 | start();
69 | }
70 | }
71 |
72 | @Override
73 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
74 | super.onRequestPermissionsResult(requestCode, permissions, grantResults);
75 | if (permissions.length == 0 || grantResults.length == 0) {
76 | setResult(RESULT_FIRST_USER);
77 | finish();
78 | return;
79 | }
80 | if (requestCode == PERMISSION_REQUEST_CODE) {
81 | if ((permissions[0].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE) && grantResults[0] == PackageManager.PERMISSION_GRANTED)
82 | && (permissions[1].equals(Manifest.permission.CAMERA) && grantResults[1] == PackageManager.PERMISSION_GRANTED)) {
83 | start();
84 | } else {
85 | setResult(RESULT_CANCELED);
86 | finish();
87 | }
88 | }
89 | }
90 |
91 | @Override
92 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
93 | if (resultCode == RESULT_OK) {
94 | if (requestCode == REQUEST_CODE) {
95 | if (data != null && data.getData() != null) {
96 | uriString = data.getData().toString();
97 | } else if (outputFileUri != null) {
98 | uriString = outputFileUri.toString();
99 | } else if (data != null && data.getExtras() != null && data.getExtras().get("data") != null) {
100 | uriString = data.getExtras().get("data").toString();
101 | try {
102 | File file = FilePickerBitmapHelper.writeBitmap(this, (Bitmap) data.getExtras().get("data"), false);
103 | uriString = Uri.fromFile(file).toString();
104 | } catch (Exception e) {
105 | Logger.loge(TAG, e.toString());
106 | }
107 | }
108 |
109 | if (uriString == null) {
110 | setResult(RESULT_FIRST_USER);
111 | finish();
112 | return;
113 | }
114 |
115 | Logger.loge(TAG, "Original URI = " + uriString);
116 |
117 | Uri uri = Uri.parse(uriString);
118 |
119 | // Android 4.4 throws:
120 | // java.lang.SecurityException: Permission Denial: opening provider com.android.providers.media.MediaDocumentsProvider
121 | // So we do this
122 | try {
123 | grantUriPermission(getPackageName(), uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
124 | final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION);
125 | if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
126 | getContentResolver().takePersistableUriPermission(uri, takeFlags);
127 | }
128 | } catch (Exception e) {
129 | Logger.loge(TAG, e.toString());
130 | }
131 | if(uri != null) {
132 | uriProcessor.process(getBaseContext(), uri, this);
133 | }
134 | else
135 | {
136 | Logger.loge(TAG, "Uri is NULL");
137 | setResult(RESULT_CODE_FAILURE);
138 | finish();
139 | }
140 | }
141 | } else if (resultCode == RESULT_CANCELED) {
142 | setResult(RESULT_CANCELED);
143 | finish();
144 | } else {
145 | setResult(RESULT_CODE_FAILURE);
146 | finish();
147 | }
148 | }
149 |
150 | void start() {
151 | final Intent intent;
152 | showProgress();
153 | if (getIntent().getBooleanExtra(CAMERA, false)) {
154 | outputFileUri = FilePickerCameraIntent.setUri(this);
155 | intent = FilePickerCameraIntent.cameraIntent(outputFileUri);
156 | } else if (getIntent().getBooleanExtra(FILE, false)) {
157 | //Only file
158 | intent = FilePickerFileIntent.fileIntent("image/*");
159 | if (null != getIntent().getStringArrayExtra(MULTIPLE_TYPES)) {
160 | //User can specify multiple types for the intent.
161 | FilePickerFileIntent.setTypes(intent, getIntent().getStringArrayExtra(MULTIPLE_TYPES));
162 | } else if (null != getIntent().getStringExtra(TYPE)) {
163 | //If no types defaults to image files, if just 1 type applies type
164 | FilePickerFileIntent.setType(intent, getIntent().getStringExtra(TYPE));
165 | }
166 | } else {
167 | //We assume its an image since developer didn't specify anything and we will show chooser with Camera, File explorers (including gdrive, dropbox...)
168 | outputFileUri = FilePickerCameraIntent.setUri(this);
169 |
170 | if (null != getIntent().getStringArrayExtra(MULTIPLE_TYPES)) {
171 | //User can specify multiple types for the intent.
172 | intent = FilePickerChooserIntent.chooserMultiIntent(chooserText, outputFileUri, getIntent().getStringArrayExtra(MULTIPLE_TYPES));
173 | } else if (null != getIntent().getStringExtra(TYPE)) {
174 | //If no types defaults to image files, if just 1 type applies type
175 | intent = FilePickerChooserIntent.chooserSingleIntent(chooserText, outputFileUri, getIntent().getStringExtra(TYPE));
176 | }
177 | else {
178 | intent = FilePickerChooserIntent.chooserIntent(chooserText, outputFileUri);
179 | }
180 | }
181 |
182 | if (intent.resolveActivity(getPackageManager()) != null) {
183 | new Handler().postDelayed(new Runnable() {
184 | @Override
185 | public void run() {
186 | startActivityForResult(intent, REQUEST_CODE);
187 | }
188 | }, 500);
189 |
190 | } else {
191 | setResult(RESULT_FIRST_USER);
192 | finish();
193 | }
194 | }
195 |
196 | void showProgress()
197 | {
198 | rootFl.setVisibility(View.VISIBLE);
199 | }
200 |
201 | void hideProgress()
202 | {
203 | rootFl.setVisibility(View.GONE);
204 | }
205 |
206 |
207 | @Override
208 | public void onProcessingSuccess(Intent intent) {
209 | hideProgress();
210 | setResult(RESULT_OK, intent);
211 | finish();
212 | }
213 |
214 | @Override
215 | public void onProcessingFailure() {
216 | hideProgress();
217 | if (uriString == null) { // this shouldn't be necessary since we already check before doing the URI processing
218 | setResult(RESULT_FIRST_USER);
219 | finish();
220 | }
221 | else {
222 | // if no processors worked, return original uri
223 | Intent intent = new Intent();
224 | intent.putExtra(URI, uriString);
225 | setResult(RESULT_OK, intent);
226 | finish();
227 | }
228 | }
229 | }
230 |
--------------------------------------------------------------------------------
/app/src/main/java/dk/nodes/filepicker/processors/GenericContentProviderProcessor.java:
--------------------------------------------------------------------------------
1 | package dk.nodes.filepicker.processors;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.ContentUris;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.database.Cursor;
8 | import android.database.DatabaseUtils;
9 | import android.net.Uri;
10 | import android.os.Environment;
11 | import android.provider.DocumentsContract;
12 | import android.provider.MediaStore;
13 | import android.provider.OpenableColumns;
14 | import android.util.Log;
15 |
16 | import java.io.File;
17 | import java.io.FileOutputStream;
18 | import java.io.InputStream;
19 | import java.text.SimpleDateFormat;
20 | import java.util.Date;
21 | import java.util.Locale;
22 | import java.util.Random;
23 |
24 | import dk.nodes.filepicker.uriHelper.FilePickerUriHelper;
25 | import dk.nodes.filepicker.utils.Logger;
26 |
27 | import static dk.nodes.filepicker.BuildConfig.DEBUG;
28 | import static dk.nodes.filepicker.FilePickerConstants.URI;
29 |
30 | /**
31 | * Created by bison on 31/10/17.
32 | */
33 |
34 | public class GenericContentProviderProcessor implements IUriProcessor {
35 | public static final String TAG = GenericContentProviderProcessor.class.getSimpleName();
36 | UriProcessListener uriProcessListener;
37 |
38 | @SuppressLint("NewApi")
39 | @Override
40 | public void process(Context context, Uri uri, final UriProcessListener uriProcessListener) {
41 | this.uriProcessListener = uriProcessListener;
42 | if(!isValidUri(uri))
43 | {
44 | if(uriProcessListener != null) {
45 | Logger.loge(TAG, "URI not recognized, bailing out");
46 | uriProcessListener.onProcessingFailure();
47 | return;
48 | }
49 | }
50 |
51 | final String mimeType = context.getContentResolver().getType(uri);
52 |
53 |
54 | try {
55 | String filename = null;
56 |
57 | if(isDropboxFilecache(uri))
58 | {
59 | Logger.logd(TAG, "Uri is dropbox filecache");
60 | filename = getSAFDisplayName(context, uri);
61 | }
62 |
63 | if(isExternalStorageDocument(uri) && (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) && filename == null)
64 | {
65 | Logger.logd(TAG, "isExternalStorageDocument");
66 | final String docId;
67 | docId = DocumentsContract.getDocumentId(uri);
68 | final String[] split = docId.split(":");
69 | final String type = split[0];
70 |
71 | if ("primary".equalsIgnoreCase(type)) {
72 | filename = getLastSegmentString(Environment.getExternalStorageDirectory() + "/" + split[1]);
73 | }
74 |
75 | }
76 |
77 | if(filename == null)
78 | filename = getFilenameFromMediaProvider(context, uri);
79 | // try alternative method
80 | if(filename == null)
81 | {
82 | String id = uri.getLastPathSegment();
83 | Logger.logd("TAG", "Trying mediastore id " + id);
84 | try {
85 | filename = getFilenameFromMediaStore(context, id);
86 | try {
87 | long ts = Long.parseLong(stripExtension(filename));
88 | filename = generateDCIMFilename("IMG", ts);
89 | }
90 | catch (Exception e)
91 | {
92 | e.printStackTrace();
93 | }
94 | }
95 | catch(Exception e)
96 | {
97 | e.printStackTrace();
98 | }
99 | }
100 | if(filename == null && isDownloadsDocument(uri))
101 | {
102 | String id = uri.getLastPathSegment();
103 | Logger.logd(TAG, "Trying public download id " + id);
104 | try {
105 | filename = getFilenameFromDownloadProvider(context, Long.parseLong(id));
106 | }
107 | catch(Exception e)
108 | {
109 | e.printStackTrace();
110 | }
111 | }
112 | if(filename == null)
113 | {
114 | filename = getSAFDisplayName(context, uri);
115 | }
116 | if(filename == null)
117 | {
118 | String last = uri.getLastPathSegment();
119 | if(last != null)
120 | {
121 | if(isValidFilename(last))
122 | {
123 | filename = last;
124 | }
125 | }
126 | }
127 | InputStream inputStream = context.getContentResolver().openInputStream(uri);
128 | String fileExtension = FilePickerUriHelper.getFileType(context, uri);
129 | Logger.logd(TAG, "fileExtension from contentprovider url is " + fileExtension);
130 | Logger.logd(TAG, "fileName: " + filename);
131 |
132 | File filesDir = context.getCacheDir();
133 | File file = null;
134 | if(filename == null) {
135 | if(fileExtension != null)
136 | file = new File(filesDir, "f" + String.format("%04d", new Random().nextInt(10000)) + "." + fileExtension);
137 | else
138 | file = new File(filesDir, "f" + String.format("%04d", new Random().nextInt(10000)));
139 | }
140 | else
141 | {
142 | if(!filename.contains(".") && fileExtension != null)
143 | file = new File(filesDir, filename + "." + fileExtension);
144 | else
145 | file = new File(filesDir, filename);
146 |
147 | }
148 | if (!file.exists()) {
149 | file.createNewFile();
150 | }
151 | FileOutputStream fileOutputStream = new FileOutputStream(file);
152 | int bufferSize = 1024;
153 | byte[] buffer = new byte[bufferSize];
154 | int len = 0;
155 | while ((len = inputStream.read(buffer)) != - 1) {
156 | fileOutputStream.write(buffer, 0, len);
157 | }
158 |
159 | Intent intent = new Intent();
160 | if(mimeType != null)
161 | intent.putExtra("mimeType", mimeType);
162 | intent.putExtra(URI, file.getAbsolutePath());
163 | if(uriProcessListener != null)
164 | uriProcessListener.onProcessingSuccess(intent);
165 |
166 | } catch (Exception e) {
167 | e.printStackTrace();
168 | Logger.loge(TAG, e.toString());
169 | if(uriProcessListener != null)
170 | uriProcessListener.onProcessingFailure();
171 | }
172 |
173 | }
174 |
175 | private static boolean isValidUri(Uri uri) {
176 | return "content".equalsIgnoreCase(uri.getScheme());
177 | }
178 |
179 | /**
180 | * Get the value of the data column for this Uri. This is useful for
181 | * MediaStore Uris, and other file-based ContentProviders.
182 | *
183 | * @param context The context.
184 | * @param uri The Uri to query.
185 | * @param selection (Optional) Filter used in the query.
186 | * @param selectionArgs (Optional) Selection arguments used in the query.
187 | * @return The value of the _data column, which is typically a file path.
188 | * @author paulburke
189 | */
190 | public static String getDataColumn(Context context, Uri uri, String selection,
191 | String[] selectionArgs) {
192 |
193 | Cursor cursor = null;
194 | final String column = "_data";
195 | final String[] projection = {
196 | column
197 | };
198 |
199 | try {
200 | cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
201 | null);
202 | if (cursor != null && cursor.moveToFirst()) {
203 | if (DEBUG)
204 | DatabaseUtils.dumpCursor(cursor);
205 |
206 | final int column_index = cursor.getColumnIndexOrThrow(column);
207 | return cursor.getString(column_index);
208 | }
209 | } finally {
210 | if (cursor != null)
211 | cursor.close();
212 | }
213 | return null;
214 | }
215 |
216 | private String generateDCIMFilename(String prefix, long timestamp)
217 | {
218 | try {
219 | Date d = new Date(timestamp);
220 | SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_hhmmss", Locale.US);
221 | String filename = prefix + "_" + sdf.format(d);
222 | return filename;
223 | }
224 | catch (Exception e)
225 | {
226 | e.printStackTrace();
227 | return null;
228 | }
229 | }
230 |
231 | final String[] ReservedChars = {"|", "\\", "?", "*", "<", "\"", ":", ">"};
232 | private boolean isValidFilename(String name)
233 | {
234 | for(String c :ReservedChars){
235 |
236 | if(name.indexOf(c) > 0)
237 | return false;
238 | }
239 | return true;
240 | }
241 |
242 | private String getFilenameFromDownloadProvider(Context context, long id)
243 | {
244 | try {
245 | final Uri contentUri = ContentUris.withAppendedId(
246 | Uri.parse("content://downloads/public_downloads"), id);
247 |
248 | String selectedPath = getDataColumn(context, contentUri, null, null);
249 | if (selectedPath != null) {
250 | if (selectedPath.contains("/")) {
251 | String[] parts = selectedPath.split("/");
252 | if (parts.length > 0) {
253 | selectedPath = parts[parts.length - 1];
254 | }
255 | }
256 | }
257 | return selectedPath;
258 | }
259 | catch (Exception e)
260 | {
261 | e.printStackTrace();
262 | return null;
263 | }
264 | }
265 |
266 |
267 | private String getFilenameFromMediaStore(Context context, String id)
268 | {
269 | try {
270 | if (id == null)
271 | return null;
272 | final String[] imageColumns = {MediaStore.Images.Media.DATA};
273 | final String imageOrderBy = null;
274 | Uri baseUri;
275 | String state = Environment.getExternalStorageState();
276 | if (!state.equalsIgnoreCase(Environment.MEDIA_MOUNTED)) {
277 | baseUri = MediaStore.Images.Media.INTERNAL_CONTENT_URI;
278 | } else {
279 | baseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
280 | }
281 |
282 | String selectedPath = null;
283 | Cursor cursor = null;
284 |
285 | cursor = context.getContentResolver().query(baseUri, imageColumns, MediaStore.Images.Media._ID + "=" + id, null, imageOrderBy);
286 |
287 | if (cursor.moveToFirst()) {
288 | selectedPath = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
289 | }
290 | cursor.close();
291 | if (selectedPath != null) {
292 | if (selectedPath.contains("/")) {
293 | String[] parts = selectedPath.split("/");
294 | if (parts.length > 0) {
295 | selectedPath = parts[parts.length - 1];
296 | }
297 | }
298 | }
299 | return selectedPath;
300 | }
301 | catch (Exception e)
302 | {
303 | e.printStackTrace();
304 | return null;
305 | }
306 | }
307 |
308 | private String getFilenameFromMediaProvider(Context context, Uri uri)
309 | {
310 | try {
311 | if (uri == null)
312 | return null;
313 | if (!uri.getLastPathSegment().contains(":"))
314 | return null;
315 | String id = uri.getLastPathSegment().split(":")[1];
316 | boolean isVideo = uri.getLastPathSegment().split(":")[0].contains("video");
317 | final String[] imageColumns = {MediaStore.Images.Media.DATA};
318 | final String[] videoColumns = {MediaStore.Video.Media.DATA};
319 | final String imageOrderBy = null;
320 | Uri baseUri;
321 | String state = Environment.getExternalStorageState();
322 | if (!isVideo) {
323 | if (!state.equalsIgnoreCase(Environment.MEDIA_MOUNTED)) {
324 | baseUri = MediaStore.Images.Media.INTERNAL_CONTENT_URI;
325 | } else {
326 | baseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
327 | }
328 | } else {
329 | if (!state.equalsIgnoreCase(Environment.MEDIA_MOUNTED)) {
330 | baseUri = MediaStore.Video.Media.INTERNAL_CONTENT_URI;
331 | } else {
332 | baseUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
333 | }
334 | }
335 |
336 | String selectedPath = null;
337 | Cursor cursor = null;
338 | if (!isVideo) {
339 | cursor = context.getContentResolver().query(baseUri, imageColumns,
340 | MediaStore.Images.Media._ID + "=" + id, null, imageOrderBy);
341 | } else {
342 | cursor = context.getContentResolver().query(baseUri, videoColumns,
343 | MediaStore.Video.Media._ID + "=" + id, null, imageOrderBy);
344 | }
345 |
346 | if (cursor.moveToFirst()) {
347 | if (!isVideo) {
348 | selectedPath = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
349 | } else {
350 | selectedPath = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATA));
351 | }
352 | }
353 | cursor.close();
354 | if (selectedPath != null) {
355 | if (selectedPath.contains("/")) {
356 | String[] parts = selectedPath.split("/");
357 | if (parts.length > 0) {
358 | selectedPath = parts[parts.length - 1];
359 | }
360 | }
361 | }
362 | return selectedPath;
363 | }
364 | catch (Exception e)
365 | {
366 | e.printStackTrace();
367 | return null;
368 | }
369 | }
370 |
371 | private String stripExtension(String selectedPath)
372 | {
373 | if(selectedPath != null)
374 | {
375 | if(selectedPath.contains("."))
376 | {
377 | String[] parts = selectedPath.split("\\.");
378 | if(parts.length > 0)
379 | {
380 | selectedPath = parts[0];
381 | }
382 | }
383 | }
384 | return selectedPath;
385 | }
386 |
387 | private String getLastSegmentString(String selectedPath)
388 | {
389 | if (selectedPath != null) {
390 | if (selectedPath.contains("/")) {
391 | String[] parts = selectedPath.split("/");
392 | if (parts.length > 0) {
393 | selectedPath = parts[parts.length - 1];
394 | }
395 | }
396 | }
397 | return selectedPath;
398 | }
399 |
400 | private String getSAFDisplayName(Context context, Uri uri) {
401 |
402 | if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN)
403 | return null;
404 | // The query, since it only applies to a single document, will only return
405 | // one row. There's no need to filter, sort, or select fields, since we want
406 | // all fields for one document.
407 | Cursor cursor = null;
408 | try {
409 | cursor = context.getContentResolver().query(uri, null, null, null, null, null);
410 | // moveToFirst() returns false if the cursor has 0 rows. Very handy for
411 | // "if there's anything to look at, look at it" conditionals.
412 | if (cursor != null && cursor.moveToFirst()) {
413 |
414 | // Note it's called "Display Name". This is
415 | // provider-specific, and might not necessarily be the file name.
416 | String displayName = cursor.getString(
417 | cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
418 | Logger.loge("DEBUG", "SAF Display Name: " + displayName);
419 | return displayName;
420 | }
421 | }
422 | catch(Exception e)
423 | {
424 | e.printStackTrace();
425 | }
426 | finally {
427 | if(cursor != null)
428 | cursor.close();
429 | }
430 | return null;
431 | }
432 |
433 | private boolean isExternalStorageDocument(Uri uri) {
434 | return "com.android.externalstorage.documents".equals(uri.getAuthority());
435 | }
436 |
437 | private boolean isDropboxFilecache(Uri uri)
438 | {
439 | return "com.dropbox.android.FileCache".equals(uri.getAuthority());
440 | }
441 |
442 | private boolean isGoogleDrive(Uri uri) {
443 | return "com.google.android.apps.docs.storage".equals(uri.getAuthority())
444 | || "com.google.android.apps.docs.files".equals(uri.getAuthority())
445 | || "com.google.android.apps.docs.storage.legacy".equals(uri.getAuthority())
446 | ;
447 |
448 | }
449 |
450 | /**
451 | * @param uri The Uri to check.
452 | * @return Whether the Uri authority is DownloadsProvider.
453 | * @author paulburke
454 | */
455 | private boolean isDownloadsDocument(Uri uri) {
456 | return "com.android.providers.downloads.documents".equals(uri.getAuthority());
457 | }
458 |
459 | }
460 |
--------------------------------------------------------------------------------