Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Picking files #53

Merged
merged 72 commits into from
Mar 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
d85f797
Abstract picking files out of picking images
JM-Agrimap Dec 20, 2016
77ffb71
Attempt saving files
JM-Agrimap Jan 11, 2017
2221bc0
Pass through FileData instead of URI
JM-Agrimap Jan 12, 2017
776f0e0
Add maven installer
JM-Agrimap Dec 20, 2016
e81932a
Tidy up
JM-Agrimap Jan 13, 2017
3d2bfec
Tidy up
JM-Agrimap Jan 13, 2017
307ea60
Change takeImage(s) to single and multiple
JM-Agrimap Jan 15, 2017
a09cc31
Remove double saving of camera file
JM-Agrimap Jan 15, 2017
398abf9
Only add image file extension after cropping image
JM-Agrimap Jan 16, 2017
84e06f2
Move mime type and openable config into Config object
JM-Agrimap Jan 16, 2017
b6d97d5
Do not fail if cropping fails
JM-Agrimap Jan 16, 2017
5865fb6
Pass through mime type and title in FileData
JM-Agrimap Jan 16, 2017
92ec038
Make mime scanning a configuration option
JM-Agrimap Jan 16, 2017
024492f
Bump up memory requirements for DEXing
JM-Agrimap Jan 16, 2017
40135cf
Rename builders
JM-Agrimap Jan 16, 2017
6de5339
Combined file picker and gallery
JM-Agrimap Jan 16, 2017
a61916a
Fix filename
JM-Agrimap Jan 17, 2017
7f2361f
Fix picking of google docs on > Kitkat
JM-Agrimap Jan 17, 2017
241f573
Fix maven version name
JM-Agrimap Jan 17, 2017
23cc148
Fix file path in sample activity
JM-Agrimap Jan 19, 2017
20b7196
Fix cancelling of select / crop in fragments
JM-Agrimap Jan 19, 2017
6c0724e
Do not pass back input url if user has cancelled crop
JM-Agrimap Jan 19, 2017
af6a785
Use concatMap instead of flatMap to accumulate cropping and saving ac…
JM-Agrimap Jan 19, 2017
d79e964
Fix null pointer issue when saving files
JM-Agrimap Jan 19, 2017
3552358
Improve test code reuse and add test cases for file pickers
JM-Agrimap Jan 19, 2017
29a2835
Check whether file is image before attempting crop
JM-Agrimap Jan 19, 2017
4915252
Add filename, mimetype and title to samples.
JM-Agrimap Jan 19, 2017
9ce2dcd
Fail crop by default if supplied file is not an image when either cho…
JM-Agrimap Jan 20, 2017
eb0bf44
Rename DownloadImage to DownloadFile
JM-Agrimap Jan 20, 2017
df981ca
Improve readability of cropping conditional logic
JM-Agrimap Jan 23, 2017
ff06db5
Improve filename resolving for Android v19+
JM-Agrimap Jan 23, 2017
a21e6f3
Improve filename from primary storage
JM-Agrimap Jan 24, 2017
468f879
Improve legacy file filename
JM-Agrimap Jan 24, 2017
84016da
Improve default photo filename
JM-Agrimap Jan 24, 2017
a90ed7b
Code tidy
JM-Agrimap Jan 24, 2017
7db25be
Fix photo saving and tidy get path code
JM-Agrimap Jan 24, 2017
96b386f
Remove hardcoding of file provider
JM-Agrimap Jan 24, 2017
2f1c269
Factor out file provider settings into configuration
JM-Agrimap Jan 24, 2017
ef115af
Code tidy
JM-Agrimap Jan 25, 2017
6da6692
Only scale images
JM-Agrimap Jan 25, 2017
44703b5
Add save instance state to sample activity and fragment for rotation
JM-Agrimap Jan 25, 2017
cc1f023
Fix file extension bug
JM-Agrimap Jan 25, 2017
eab35e4
Improve code reuse in builder pattern
JM-Agrimap Jan 25, 2017
7062700
Flag whether the File contained in FileData is transient
JM-Agrimap Jan 26, 2017
818fa89
Improve cropping to only create new file if required
JM-Agrimap Jan 26, 2017
7c11b0c
Only remove transient files
JM-Agrimap Jan 26, 2017
75a5a88
Improve method name
JM-Agrimap Jan 26, 2017
918cd18
Improve recycler view testing
JM-Agrimap Jan 31, 2017
7dcc46a
Improve SaveFile observables
JM-Agrimap Jan 31, 2017
3e34d81
Add config to switch between GET_CONTENT and OPEN_DOCUMENT intent act…
JM-Agrimap Jan 31, 2017
61350a8
Update README
JM-Agrimap Feb 1, 2017
d41b5dd
Remove automatic backup
JM-Agrimap Feb 1, 2017
0d45d82
Improve test layout
JM-Agrimap Feb 1, 2017
c57829e
Speed improvement by removing extra resolving and duplication of file…
JM-Agrimap Feb 1, 2017
72a8361
Guard against null intents when picking files
JM-Agrimap Feb 1, 2017
2aafa20
Guard against activity context being destroyed
JM-Agrimap Feb 1, 2017
fe700de
Fix documentation
JM-Agrimap Feb 1, 2017
ec1e1ab
Use context instead of activity
JM-Agrimap Feb 1, 2017
f754b35
Fix permission issue when selecting only a single file when in multi-…
JM-Agrimap Feb 1, 2017
e8a7867
Add leak canary to sample app
JM-Agrimap Feb 7, 2017
1fad221
Improve documentation around file provider
JM-Agrimap Feb 7, 2017
4aa9517
Create intent to invoke media scanning
JM-Agrimap Feb 7, 2017
eeff169
Improve readability of timestamped filenames
JM-Agrimap Feb 8, 2017
2f4610f
Replace print stack trace with android logging
JM-Agrimap Feb 8, 2017
ca88f76
Simplified test fragment / activity layout
JM-Agrimap Feb 8, 2017
2d4e32e
Add in maximum file size handling
JM-Agrimap Feb 8, 2017
3e16b28
Improve tests to not throw unintended exceptions if file size limit i…
JM-Agrimap Feb 8, 2017
fdf5e90
Improve file dimension checks in test cases
JM-Agrimap Feb 8, 2017
b9dc5c4
Make dimensions serializable
JM-Agrimap Feb 8, 2017
6a44611
Fix serialisation
JM-Agrimap Feb 9, 2017
e5f5099
Cleanup after failure from file being too large
JM-Agrimap Feb 9, 2017
2ca449c
Added device profile and revised wait times to speed up tests
JM-Agrimap Feb 16, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 116 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
[![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-RxPaparazzo-brightgreen.svg?style=flat)](http://android-arsenal.com/details/1/3523)

RxJava extension for Android to access camera and gallery to take images.
RxJava extension for Android to take photos using the camera, select files or photos from the device and optionally crop or rotate any selected images.

# RxPaparazzo

What is RX?

> Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM.

What is a Paparazzo?

> A freelance photographer who aggressively pursues celebrities for the purpose of taking candid photographs.

This library does that. Not really. But it was a funny name, thought. Was it?
This library does that (well not really). But it is a cool name.


## Features:

- Runtime permissions. Not worries about the tricky Android runtime permissions system. RxPaparazzo relies on [RxPermissions](https://github.com/tbruyelle/RxPermissions) to deal with that.
- Take a photo using the built-in camera.
- Access to gallery.
- Crop images. RxPaparazzo relies on [UCrop](https://github.com/Yalantis/uCrop) to perform beautiful cuts to any face, body or place.
- Takes a photo using the built-in camera.
- Access to gallery and other sources of photos.
- Access to files and documents stored locally and on the cloud.
- Crop and rotate images. RxPaparazzo relies on [UCrop](https://github.com/Yalantis/uCrop) to perform beautiful cuts to any face, body or place.
- Honors the observable chain (it means you can go crazy chaining operators). [RxOnActivityResult](https://github.com/VictorAlbertos/RxActivityResult) allows RxPaparazzo to transform every intent into an observable for a wonderful chaining process.


Expand All @@ -31,7 +36,7 @@ allprojects {
}
```

And add next dependencies in the build.gradle of the module:
Add dependencies in the build.gradle of the module:
```gradle
dependencies {
compile "com.github.miguelbcr:RxPaparazzo:0.4.4"
Expand All @@ -50,7 +55,7 @@ allprojects {
}
```

And add next dependencies in the build.gradle of the module:
Add dependencies in the build.gradle of the module:
```gradle
dependencies {
compile "com.github.miguelbcr:RxPaparazzo:0.4.4-2.x"
Expand All @@ -62,7 +67,7 @@ dependencies {
## Usage
Because RxPaparazzo uses RxActivityResult to deal with intent calls, all its requirements and features are inherited too.

Before attempting to use RxPaparazzo, you need to call `RxPaparazzo.register` in your Android `Application` class, supplying as parameter the current instance.
Before attempting to use RxPaparazzo, you need to call `RxPaparazzo.register` in your Android Application's `onCreate` supplying the current Application instance.

```java
public class SampleApp extends Application {
Expand All @@ -74,19 +79,53 @@ public class SampleApp extends Application {
}
```

Every feature RxPaparazzo exposes can be accessed from both, an `activity` or a `fragment` instance.
You will need to also add a FileProvider named `android.support.v4.content.FileProvider` to your `AndroidManifest.xml` and create a paths xml file in your src/main/res/xml directory.

```xml
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.file_provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider_paths"/>
</provider>
```

If you set the provider `android:authorities` attribute to a value other than `${applicationId}.file_provider` name you must set the configuration it using `RxPaparazzo.Builder.setFileProviderAuthority(String authority)`

Example: file_provider_paths.xml
```xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path name="RxPaparazzoImages" path="RxPaparazzo/"/>
</paths>
```

The `file_provider_paths.xml` is where files are exposed in the FileProvider.
If you set the files-path `path` attribute to a value other than `RxPaparazzo/` you must set the configuration using `RxPaparazzo.Builder.setFileProviderDirectory(String authority)`

**Limitation:**: Your fragments need to extend from `android.support.v4.app.Fragment` instead of `android.app.Fragment`, otherwise they won't be notified.
All features RxPaparazzo exposes can be accessed from both, an `activity` or a `fragment` instance.

The generic type of the `observable` returned by RxPaparazzo when subscribing to any of its features is always an instance of [Response](https://github.com/miguelbcr/RxPaparazzo/blob/master/rx_paparazzo/src/main/java/com/miguelbcr/ui/rx_paparazzo2/entities/Response.java) class.
**Limitation:**: Your fragments need to extend from `android.support.v4.app.Fragment` instead of `android.app.Fragment`, otherwise they won't be notified.

The generic type of the `observable` returned by RxPaparazzo when subscribing to any of its features is always an instance of [Response](https://github.com/miguelbcr/RxPaparazzo/blob/master/rx_paparazzo/src/main/java/com/miguelbcr/ui/rx_paparazzo2/entities/Response.java) class.

This instance holds a reference to the current Activity/Fragment, accessible calling `targetUI()` method. Because the original one may be recreated it would be unsafe calling it. Instead, you must call any method/variable of your Activity/Fragment from this instance encapsulated in the `response` instance.

Also, this instance holds a reference to the data as the appropriate response, as such as the result code of the specific operation.


### Saving files

By default, the image / file is saved in a directory the same as the app name on the root of the external storage. You can choose to save the images in internal storage by using `.useInternalStorage()`

The `response` in the callback function supplied to the `subscribe()` method holds a reference to the path where the image was persisted.

### Calling built-in camera to take a photo.
```java
RxPaparazzo.takeImage(activityOrFragment)
RxPaparazzo.single(activityOrFragment)
.usingCamera()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
Expand All @@ -101,18 +140,48 @@ RxPaparazzo.takeImage(activityOrFragment)
});
```

The `response` instance holds a reference to the path where the image was persisted.
### Calling the file picker to retrieve a file.
```java
RxPaparazzo.single(activityOrFragment)
.usingFile()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> {
// See response.resultCode() doc
if (response.resultCode() != RESULT_OK) {
response.targetUI().showUserCanceled();
return;
}

response.targetUI().loadImage(response.data());
});
```

By default, the path is under app name folder on the root of the external storage, but you can save the images in internal storage by using `.useInternalStorage()`
### Calling the file picker to retrieve multiple files
```java
RxPaparazzo.multiple(activityOrFragment)
.usingFiles()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> {
// See response.resultCode() doc
if (response.resultCode() != RESULT_OK) {
response.targetUI().showUserCanceled();
return;
}

if (response.data().size() == 1) response.targetUI().loadImage(response.data().get(0));
else response.targetUI().loadImages(response.data());
});
```

### Calling the gallery to retrieve an image.
```java
RxPaparazzo.takeImage(activityOrFragment)
RxPaparazzo.single(activityOrFragment)
.usingGallery()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> {
.subscribe(response -> {
// See response.resultCode() doc
if (response.resultCode() != RESULT_OK) {
response.targetUI().showUserCanceled();
Expand All @@ -123,11 +192,9 @@ RxPaparazzo.takeImage(activityOrFragment)
});
```

The `response` instance holds a reference to the path where the image was persisted. Same as the previous example.

### Calling the gallery to retrieve multiple image
### Calling the gallery to retrieve multiple image
```java
RxPaparazzo.takeImages(activityOrFragment)
RxPaparazzo.multiple(activityOrFragment)
.usingGallery()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
Expand All @@ -143,12 +210,10 @@ RxPaparazzo.takeImages(activityOrFragment)
});
```

The `response` instance holds a reference to the paths where the images were persisted.

**Note**: if the level Android api device is minor than 18, only one image will be retrieved.

## Customizations
When asking RxPaparazzo for an image -whether it was retrieved using the built-in camera or via gallery, it's possible to apply some configurations to the action.
When asking RxPaparazzo for an photo / image / file it's possible to apply some configurations to the action.

### Size options
[Size](https://github.com/miguelbcr/RxPaparazzo/blob/master/rx_paparazzo/src/main/java/com/miguelbcr/ui/rx_paparazzo2/entities/size/Size.java) values can be used to set the size of the image to retrieve. There are 4 options:
Expand All @@ -161,7 +226,7 @@ When asking RxPaparazzo for an image -whether it was retrieved using the built-i
[ScreenSize](https://github.com/miguelbcr/RxPaparazzo/blob/master/rx_paparazzo/src/main/java/com/miguelbcr/ui/rx_paparazzo2/entities/size/ScreenSize.java) value will be set as default.

```java
RxPaparazzo.takeImages(activityOrFragment)
RxPaparazzo.multiple(activityOrFragment)
.size(new ScreenSize())
.usingGallery()
```
Expand All @@ -170,29 +235,39 @@ RxPaparazzo.takeImages(activityOrFragment)
This feature is available thanks to the amazing library [uCrop](https://github.com/Yalantis/uCrop) authored by [Yalantis](https://github.com/Yalantis) group.

```java
RxPaparazzo.takeImages(activityOrFragment)
RxPaparazzo.multiple(activityOrFragment)
.crop()
```

By calling `crop()` method when building the observable instance, all they images retrieved will be able to be cropped, regardless if the images were retrieved using the built-in camera or gallery, even if multiple images were requested in a single call using `takeImages()` approach.
By calling `crop()` method when building the observable instance, all they images retrieved will be able to be cropped, regardless if the images were retrieved using the built-in camera or gallery, even if multiple images were requested in a single call using `single()` approach.
Because uCrop Yalantis library exposes some configuration in order to customize the crop screen, RxPaparazzo exposes an overloaded method of `crop(UCrop.Options)` which allow to pass an instance of [UCrop.Options](https://github.com/Yalantis/uCrop/blob/master/ucrop/src/main/java/com/yalantis/ucrop/UCrop.java#L211).
If you need to configure the aspect ratio, the max result size or using the source image aspect ratio, you must pass an instance of [Options](https://github.com/miguelbcr/RxPaparazzo/blob/master/rx_paparazzo/src/main/java/com/miguelbcr/ui/rx_paparazzo2/entities/Options.java) class, which extends from `UCrop.Options` and adds the three missing properties.

```java
UCrop.Options options = new UCrop.Options();
options.setToolbarColor(ContextCompat.getColor(getActivity(), R.color.colorPrimaryDark));

RxPaparazzo.takeImage(activityOrFragment).crop(options)
RxPaparazzo.single(activityOrFragment).crop(options)
```

```java
Options options = new Options();
options.setToolbarColor(ContextCompat.getColor(getActivity(), R.color.colorPrimaryDark));
options.setAspectRatio(25, 50);

RxPaparazzo.takeImage(activityOrFragment)
RxPaparazzo.single(activityOrFragment)
.crop(options)
```

### Media scanning

To send files to the media scanner so that they can be indexed and available in applications such as the Gallery use `sendToMediaScanner()`. If you are using `useInternalStorage()` then the media scanner will not be able to access the file.

### Picking files

If you wish to limit the type of images or files then use `setMimeType(String mimeType)` to specify a specific mime type for the Intent.
By default `Intent.ACTION_GET_CONTENT` is used to request images and files. If you wish to edit the original file call `useDocumentPicker()`, this will allow greater, possiblty persistent access to the source file.

## Proguard

```
Expand All @@ -208,6 +283,13 @@ RxPaparazzo.takeImage(activityOrFragment)
long consumerNode;
}
```
## Testing

Testing has been done using the following Genymotion devices:

* Genymotion - Google Nexus 5 5.0.0 API 21 1080x1920 480dpi
* Genymotion - Google Nexus 7 5.1.0 API 22 800x1280 213dpi


## Credits
* Runtime permissions: [RxPermissions](https://github.com/tbruyelle/RxPermissions)
Expand All @@ -226,8 +308,13 @@ RxPaparazzo.takeImage(activityOrFragment)
* <https://es.linkedin.com/in/miguelbcr>
* <https://github.com/miguelbcr>

**James McIntosh**

* <https://www.linkedin.com/in/james-mcintosh>
* <https://github.com/jamesmcintosh>


## Another author's libraries using RxJava:
* [RxCache](https://github.com/VictorAlbertos/RxCache): Reactive caching library for Android and Java.
* [RxGcm](https://github.com/VictorAlbertos/RxGcm): RxJava extension for Gcm which acts as an architectural approach to easily satisfy the requirements of an android app when dealing with push notifications.
* [RxActivityResult](https://github.com/VictorAlbertos/RxActivityResult): A reactive-tiny-badass-vindictive library to break with the OnActivityResult implementation as it breaks the observables chain.
* [RxActivityResult](https://github.com/VictorAlbertos/RxActivityResult): A tiny reactive library to break with the OnActivityResult implementation as it breaks the observables chain.
2 changes: 2 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ dependencies {
compile 'com.squareup.picasso:picasso:2.5.2'
compile project(':rx_paparazzo')

debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'

androidTestCompile ("com.android.support.test:runner:0.4.1") {
exclude module: 'support-annotations'
Expand Down
Loading