Skip to content

Commit 39b0cfa

Browse files
author
Dave Coffin
committed
major improvements
1 parent 4572707 commit 39b0cfa

File tree

8 files changed

+455
-163
lines changed

8 files changed

+455
-163
lines changed

apps/demo/src/plugin-demos/imagepicker.xml

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,29 @@
33
<ActionBar title="Image Picker" />
44
</Page.actionBar>
55
<GridLayout rows="*, auto, auto">
6-
<ListView visibility="{{ isSingleMode ? 'collapsed' : 'visible' }}" itemLoading="{{ onItemLoading }}" items="{{ imageAssets }}">
6+
<ListView visibility="{{ isSingleMode ? 'collapsed' : 'visible' }}" items="{{ imageAssets }}">
77
<ListView.itemTemplate>
8-
<GridLayout columns="auto, *">
9-
<Image width="80" height="80" src="{{ $value }}" stretch="aspectFill" />
10-
<Label class="m-10" id="imageLabel" col="1" />
8+
<GridLayout columns="auto, *" class="m-y-10">
9+
<Image width="80" height="80" src="{{ $value.thumbnail ? $value.thumbnail : $value.asset }}" stretch="aspectFill" />
10+
<StackLayout col="1">
11+
<Label class="m-x-10 m-b-10" style="font-weight: bold; font-size: 18;" text="{{ 'Media Type: ' + $value.type }}" />
12+
<Label class="m-x-10 m-b-5 c-gray t-12" text="{{ 'Filename: ' + $value.filename }}" />
13+
<Label class="m-x-10 m-b-5 c-gray t-12" text="{{ 'File Size: ' + $value.filesize }}" />
14+
<Label class="m-x-10 m-b-5 c-gray t-12" text="{{ 'Path: ' + $value.path }}" textWrap="true" />
15+
<Label class="m-x-10 m-b-5 c-gray t-12" text="{{ 'Duration: ' + $value.duration }}" hidden="{{ !$value.duration }}" />
16+
</StackLayout>
17+
1118
</GridLayout>
1219
</ListView.itemTemplate>
1320
</ListView>
14-
<Image src="{{ imageSrc }}" visibility="{{ isSingleMode ? 'visible' : 'collapsed' }}" width="{{ previewSize }}" height="{{ previewSize }}" stretch="aspectFit" />
21+
<StackLayout visibility="{{ isSingleMode && selection ? 'visible' : 'collapsed' }}">
22+
<Image src="{{ selection.thumbnail || selection.asset }}" width="{{ previewSize }}" height="{{ previewSize }}" stretch="aspectFit" />
23+
<Label class="m-x-10 m-b-10" style="font-weight: bold; font-size: 18;" text="{{ 'Media Type: ' + selection.type }}" />
24+
<Label class="m-x-10 m-b-5 c-gray t-12" text="{{ 'Filename: ' + selection.filename }}" />
25+
<Label class="m-x-10 m-b-5 c-gray t-12" text="{{ 'File Size: ' + selection.filesize }}" />
26+
<Label class="m-x-10 m-b-5 c-gray t-12" text="{{ 'Path: ' + selection.path }}" textWrap="true" />
27+
<Label class="m-x-10 m-b-5 c-gray t-12" text="{{ 'Duration: ' + selection.duration }}" hidden="{{ !selection.duration }}" />
28+
</StackLayout>
1529
<Button row="1" text="Pick Single" tap="{{ onSelectSingleTap }}" horizontalAlignment="center" />
1630
<Button row="2" text="Pick Multiple" tap="{{ onSelectMultipleTap }}" horizontalAlignment="center" />
1731
</GridLayout>

packages/imagepicker/README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
# @nativescript/imagepicker
22

33
Imagepicker plugin supporting both single and multiple selection.
4+
As of 2.0, it returns rich information about your selection including filesize, path, video duration and thumbnails etc.
45
<br />Plugin supports **iOS8+** and uses [QBImagePicker](https://github.com/questbeat/QBImagePicker) cocoapod.
56
<br />For **Android** it uses [Intents](https://developer.android.com/reference/android/content/Intent) to open the stock images or file pickers. For Android 6 (API 23) and above, the permissions to read file storage should be explicitly required.
67

8+
---
9+
**Note: Version 2.0 contains breaking changes. In order supply more information about your selection, the ImageSource asset is nested in the response so you'll need to update your code to use `result.asset` instead of `result` as your src for your Images.**
10+
11+
712
## Table of Contents
813
* [Installation](#installation)
914
* [Required Permissions](#required-permissions)
@@ -134,6 +139,22 @@ Creates an instance of the ImagePicker class.
134139
- _Optional_: `options` - The ImagePicker instance settings. See [Options](#options)
135140
- _Optional_: (`iOS-only`) `hostView` - Can be set to the view that hosts the image picker. Intended to be used when opening the picker from a modal page.
136141

142+
### Response
143+
As of version 2.0, imagepicker returns more information about your selection.
144+
```ts
145+
[
146+
{
147+
asset: {}, // ImageSource. this is what you'll use to set the src of an Image for example.
148+
type: 'image', // either 'video' or 'image'
149+
filename: 'mycoolfile-0.jpg', // the filename
150+
originalFilename: 'IMG1001.JPG', // the original filename (may be useful if you changed the filename when copying)
151+
path: '/myapp/media/mycoolfile-0.jpg', // the path where the file is
152+
duration: 100, // video only, the duration in seconds.
153+
thumbnail: {} // ImageSource, video only. This is what you'll use to set the src of an Image for example.
154+
}
155+
]
156+
```
157+
137158
---
138159
### Options
139160

@@ -147,6 +168,8 @@ Creates an instance of the ImagePicker class.
147168
| `numberOfColumnsInPortrait` | `number` | `4` | _Optional_: (`iOS-only`) Sets the number of columns in Portrait orientation |
148169
| `numberOfColumnsInLandscape` | `number` | `7` | _Optional_: (`iOS-only`) Sets the number of columns in Landscape orientation. |
149170
| `mediaType` | [ImagePickerMediaType](#imagepickermediatype) | `Any` |_Optional_: The type of media asset to pick whether to pick Image/Video/Any type of assets. |
171+
| `copyToAppFolder` | `string` | `undefined` | _Optional_: If passed, a new folder will be created in your applications folder and the asset will be copied there. |
172+
| `renameFileTo` | `string` | `undefined` | _Optional_: If passed, the copied file will be named what you choose. If you select multiple, -index will be appended. |
150173
| `showAdvanced ` | `boolean` | `false` | _Optional_:(`Android-only`) Show internal and removable storage options on Android (**WARNING**: [not supported officially](https://issuetracker.google.com/issues/72053350)). |
151174
| `android` | `{read_external_storage: string;}`| _Optional_: (`Android-only`) Provides a reason for permission request to access external storage on API level above 23.
152175

packages/imagepicker/common.ts

Lines changed: 111 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,118 @@
1+
import { ImageAsset } from '@nativescript/core';
2+
13
export enum ImagePickerMediaType {
2-
Any = 0,
3-
Image = 1,
4-
Video = 2
4+
Any = 0,
5+
Image = 1,
6+
Video = 2,
7+
}
8+
9+
export interface ImagePickerSelection {
10+
/**
11+
* The selected image source
12+
*/
13+
asset: ImageAsset;
14+
15+
/**
16+
* The size in bytes of the selected file
17+
*/
18+
filesize: number;
19+
20+
/**
21+
* The type of file, either image or video.
22+
*/
23+
type: 'image' | 'video';
24+
25+
/**
26+
* The name of the file.
27+
*/
28+
filename: string;
29+
30+
/**
31+
* The original name of the file, could be useful if the file was renamed.
32+
*/
33+
originalFilename: string;
34+
35+
/**
36+
* The path to the file.
37+
*/
38+
path: string;
39+
40+
/**
41+
* The duration of the video. Only passed if type is 'video'
42+
*/
43+
duration?: string;
44+
45+
/**
46+
* An image to use for the video thumbnail. Only passed if type is 'video'
47+
*/
48+
thumbnail?: ImageAsset;
549
}
650

751
/**
852
* Provide options for the image picker.
953
*/
1054
export interface Options {
11-
/**
12-
* Set the picker mode. Supported modes: "single" or "multiple" (default).
13-
*/
14-
mode?: string;
15-
16-
/**
17-
* Set the minumum number of selected assets in iOS
18-
*/
19-
minimumNumberOfSelection?: number;
20-
21-
/**
22-
* Set the maximum number of selected assets in iOS
23-
*/
24-
maximumNumberOfSelection?: number;
25-
26-
/**
27-
* Display the number of selected assets in iOS
28-
*/
29-
showsNumberOfSelectedAssets?: boolean;
30-
31-
/**
32-
* Display prompt text when selecting assets in iOS
33-
*/
34-
prompt?: string;
35-
36-
/**
37-
* Set the number of columns in Portrait in iOS
38-
*/
39-
numberOfColumnsInPortrait?: number;
40-
41-
/**
42-
* Set the number of columns in Landscape in iOS
43-
*/
44-
numberOfColumnsInLandscape?: number;
45-
46-
/**
47-
* Set the media type (image/video/any) to pick
48-
*/
49-
mediaType?: ImagePickerMediaType;
50-
51-
/**
52-
* Show internal and removable storage options on Android.
53-
* Not supported officially, see https://issuetracker.google.com/issues/72053350 |
54-
*/
55-
showAdvanced?: boolean;
56-
57-
android?: {
58-
/**
59-
* Provide a reason for permission request to access external storage on api levels above 23.
60-
*/
61-
read_external_storage?: string;
62-
};
63-
}
55+
/**
56+
* Set the picker mode. Supported modes: "single" or "multiple" (default).
57+
*/
58+
mode?: string;
59+
60+
/**
61+
* Set the minumum number of selected assets in iOS
62+
*/
63+
minimumNumberOfSelection?: number;
64+
65+
/**
66+
* Set the maximum number of selected assets in iOS
67+
*/
68+
maximumNumberOfSelection?: number;
69+
70+
/**
71+
* Display the number of selected assets in iOS
72+
*/
73+
showsNumberOfSelectedAssets?: boolean;
74+
75+
/**
76+
* Display prompt text when selecting assets in iOS
77+
*/
78+
prompt?: string;
79+
80+
/**
81+
* Set the number of columns in Portrait in iOS
82+
*/
83+
numberOfColumnsInPortrait?: number;
84+
85+
/**
86+
* Set the number of columns in Landscape in iOS
87+
*/
88+
numberOfColumnsInLandscape?: number;
89+
90+
/**
91+
* Set the media type (image/video/any) to pick
92+
*/
93+
mediaType?: ImagePickerMediaType;
94+
95+
/**
96+
* An option to copy the image or video to your apps folder. If not passed, it wont copy
97+
* If you pass a string, it will be used as the folder name to copy the file to.
98+
*/
99+
copyToAppFolder?: string;
100+
101+
/**
102+
* If you want to rename the file when copying it to your app folder, you can pass a string here.
103+
*/
104+
renameFileTo?: string;
105+
106+
/**
107+
* Show internal and removable storage options on Android.
108+
* Not supported officially, see https://issuetracker.google.com/issues/72053350 |
109+
*/
110+
showAdvanced?: boolean;
111+
112+
android?: {
113+
/**
114+
* Provide a reason for permission request to access external storage on api levels above 23.
115+
*/
116+
read_external_storage?: string;
117+
};
118+
}

packages/imagepicker/index.android.ts

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,25 @@
1-
import { ImageAsset, Application, AndroidApplication, Utils } from '@nativescript/core';
1+
import { ImageAsset, Application, AndroidApplication, Utils, File, knownFolders } from '@nativescript/core';
22
import * as permissions from 'nativescript-permissions';
33

44
import { ImagePickerMediaType, Options } from './common';
55
export * from './common';
6-
6+
let copyToAppFolder;
7+
let renameFileTo;
8+
let fileMap = {};
9+
let videoFiles = {
10+
mp4: true,
11+
mov: true,
12+
avi: true,
13+
mkv: true,
14+
wmv: true,
15+
flv: true,
16+
m4v: true,
17+
'3gp': true,
18+
'3g2': true,
19+
mpeg: true,
20+
mpeg4: true,
21+
mpeg2: true,
22+
};
723
class UriHelper {
824
public static _calculateFileUri(uri: android.net.Uri) {
925
let DocumentsContract = (<any>android.provider).DocumentsContract;
@@ -140,6 +156,8 @@ export class ImagePicker {
140156

141157
constructor(options: Options) {
142158
this._options = options;
159+
copyToAppFolder = options.copyToAppFolder;
160+
renameFileTo = options.renameFileTo;
143161
}
144162

145163
get mode(): string {
@@ -202,6 +220,47 @@ export class ImagePicker {
202220
let resultCode = args.resultCode;
203221
let data = args.intent;
204222

223+
const handle = (selectedAsset, i?) => {
224+
const file = File.fromPath(selectedAsset.android);
225+
let copiedFile: any = false;
226+
227+
let item: any = {
228+
asset: selectedAsset,
229+
filename: file.name,
230+
originalFilename: file.name,
231+
type: videoFiles[file.extension.replace('.', '')] ? 'video' : 'image',
232+
path: file.path,
233+
};
234+
if (copyToAppFolder) {
235+
let extension = file.name.split('.').pop();
236+
let filename = file.name;
237+
if (renameFileTo) {
238+
if (i || i === 0) {
239+
filename = renameFileTo + '-' + i + '.' + extension;
240+
} else {
241+
filename = renameFileTo + '.' + extension;
242+
}
243+
item.filename = filename;
244+
}
245+
let newPath = knownFolders.documents().path + '/' + copyToAppFolder + '/' + filename;
246+
copiedFile = File.fromPath(newPath);
247+
item.path = newPath;
248+
item.asset.android = item.path;
249+
copiedFile.writeSync(file.readSync());
250+
item.filesize = new java.io.File(item.path).length();
251+
}
252+
if (item.type == 'video') {
253+
let thumb = android.media.ThumbnailUtils.createVideoThumbnail(copiedFile ? copiedFile.path : file.path, android.provider.MediaStore.Video.Thumbnails.MINI_KIND);
254+
let retriever = new android.media.MediaMetadataRetriever();
255+
retriever.setDataSource(item.path);
256+
item.thumbnail = thumb;
257+
let time = retriever.extractMetadata(android.media.MediaMetadataRetriever.METADATA_KEY_DURATION);
258+
let duration = parseInt(time) / 1000;
259+
item.duration = duration;
260+
}
261+
return item;
262+
};
263+
205264
if (requestCode === RESULT_CODE_PICKER_IMAGES) {
206265
if (resultCode === android.app.Activity.RESULT_OK) {
207266
try {
@@ -217,15 +276,17 @@ export class ImagePicker {
217276
if (uri) {
218277
const val = useHelper ? UriHelper._calculateFileUri(uri) : uri.toString();
219278
const selectedAsset = new ImageAsset(val);
220-
results.push(selectedAsset);
279+
let item = handle(selectedAsset, i);
280+
results.push(item);
221281
}
222282
}
223283
}
224284
} else {
225285
const uri = data.getData();
226286
const val = useHelper ? UriHelper._calculateFileUri(uri) : uri.toString();
227287
const selectedAsset = new ImageAsset(val);
228-
results.push(selectedAsset);
288+
let item = handle(selectedAsset);
289+
results.push(item);
229290
}
230291

231292
Application.android.off(AndroidApplication.activityResultEvent, onResult);

0 commit comments

Comments
 (0)