mirror of
https://github.com/apache/cordova-plugin-camera.git
synced 2025-01-31 12:42:50 +08:00
Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/cordova-plugin-camera
This commit is contained in:
commit
d44138e7b0
11
plugin.xml
11
plugin.xml
@ -52,5 +52,16 @@ id="org.apache.cordova.core.CameraLauncher"
|
|||||||
<source-file src="src/ios/CDVJpegHeaderWriter.m" />
|
<source-file src="src/ios/CDVJpegHeaderWriter.m" />
|
||||||
<header-file src="src/ios/CDVExif.h" />
|
<header-file src="src/ios/CDVExif.h" />
|
||||||
</platform>
|
</platform>
|
||||||
|
|
||||||
|
<!-- wp7 -->
|
||||||
|
<platform name="wp7">
|
||||||
|
<config-file target="config.xml" parent="/*">
|
||||||
|
<feature name="Camera">
|
||||||
|
<param name="wp-package" value="Camera"/>
|
||||||
|
</feature>
|
||||||
|
</config-file>
|
||||||
|
|
||||||
|
<source-file src="src/wp7/Camera.cs" />
|
||||||
|
</platform>
|
||||||
|
|
||||||
</plugin>
|
</plugin>
|
||||||
|
490
src/wp7/Camera.cs
Normal file
490
src/wp7/Camera.cs
Normal file
@ -0,0 +1,490 @@
|
|||||||
|
/*
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Documents;
|
||||||
|
using System.Windows.Ink;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Media.Animation;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.Phone.Tasks;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.IsolatedStorage;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
using Microsoft.Phone;
|
||||||
|
using Microsoft.Xna.Framework.Media;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace WPCordovaClassLib.Cordova.Commands
|
||||||
|
{
|
||||||
|
public class Camera : BaseCommand
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return base64 encoded string
|
||||||
|
/// </summary>
|
||||||
|
private const int DATA_URL = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return file uri
|
||||||
|
/// </summary>
|
||||||
|
private const int FILE_URI = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Choose image from picture library
|
||||||
|
/// </summary>
|
||||||
|
private const int PHOTOLIBRARY = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Take picture from camera
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
|
private const int CAMERA = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Choose image from picture library
|
||||||
|
/// </summary>
|
||||||
|
private const int SAVEDPHOTOALBUM = 2;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Take a picture of type JPEG
|
||||||
|
/// </summary>
|
||||||
|
private const int JPEG = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Take a picture of type PNG
|
||||||
|
/// </summary>
|
||||||
|
private const int PNG = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Folder to store captured images
|
||||||
|
/// </summary>
|
||||||
|
private const string isoFolder = "CapturedImagesCache";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents captureImage action options.
|
||||||
|
/// </summary>
|
||||||
|
[DataContract]
|
||||||
|
public class CameraOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Source to getPicture from.
|
||||||
|
/// </summary>
|
||||||
|
[DataMember(IsRequired = false, Name = "sourceType")]
|
||||||
|
public int PictureSourceType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Format of image that returned from getPicture.
|
||||||
|
/// </summary>
|
||||||
|
[DataMember(IsRequired = false, Name = "destinationType")]
|
||||||
|
public int DestinationType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Quality of saved image
|
||||||
|
/// </summary>
|
||||||
|
[DataMember(IsRequired = false, Name = "quality")]
|
||||||
|
public int Quality { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Controls whether or not the image is also added to the device photo album.
|
||||||
|
/// </summary>
|
||||||
|
[DataMember(IsRequired = false, Name = "saveToPhotoAlbum")]
|
||||||
|
public bool SaveToPhotoAlbum { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ignored
|
||||||
|
/// </summary>
|
||||||
|
[DataMember(IsRequired = false, Name = "correctOrientation")]
|
||||||
|
public bool CorrectOrientation { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ignored
|
||||||
|
/// </summary>
|
||||||
|
[DataMember(IsRequired = false, Name = "allowEdit")]
|
||||||
|
public bool AllowEdit { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Height in pixels to scale image
|
||||||
|
/// </summary>
|
||||||
|
[DataMember(IsRequired = false, Name = "encodingType")]
|
||||||
|
public int EncodingType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Height in pixels to scale image
|
||||||
|
/// </summary>
|
||||||
|
[DataMember(IsRequired = false, Name = "mediaType")]
|
||||||
|
public int MediaType { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Height in pixels to scale image
|
||||||
|
/// </summary>
|
||||||
|
[DataMember(IsRequired = false, Name = "targetHeight")]
|
||||||
|
public int TargetHeight { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Width in pixels to scale image
|
||||||
|
/// </summary>
|
||||||
|
[DataMember(IsRequired = false, Name = "targetWidth")]
|
||||||
|
public int TargetWidth { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates options object with default parameters
|
||||||
|
/// </summary>
|
||||||
|
public CameraOptions()
|
||||||
|
{
|
||||||
|
this.SetDefaultValues(new StreamingContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes default values for class fields.
|
||||||
|
/// Implemented in separate method because default constructor is not invoked during deserialization.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
[OnDeserializing()]
|
||||||
|
public void SetDefaultValues(StreamingContext context)
|
||||||
|
{
|
||||||
|
PictureSourceType = CAMERA;
|
||||||
|
DestinationType = FILE_URI;
|
||||||
|
Quality = 80;
|
||||||
|
TargetHeight = -1;
|
||||||
|
TargetWidth = -1;
|
||||||
|
SaveToPhotoAlbum = false;
|
||||||
|
CorrectOrientation = true;
|
||||||
|
AllowEdit = false;
|
||||||
|
MediaType = -1;
|
||||||
|
EncodingType = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to open photo library
|
||||||
|
/// </summary>
|
||||||
|
PhotoChooserTask photoChooserTask;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to open camera application
|
||||||
|
/// </summary>
|
||||||
|
CameraCaptureTask cameraTask;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Camera options
|
||||||
|
/// </summary>
|
||||||
|
CameraOptions cameraOptions;
|
||||||
|
|
||||||
|
public void takePicture(string options)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
|
||||||
|
// ["quality", "destinationType", "sourceType", "targetWidth", "targetHeight", "encodingType",
|
||||||
|
// "mediaType", "allowEdit", "correctOrientation", "saveToPhotoAlbum" ]
|
||||||
|
this.cameraOptions = new CameraOptions();
|
||||||
|
this.cameraOptions.Quality = int.Parse(args[0]);
|
||||||
|
this.cameraOptions.DestinationType = int.Parse(args[1]);
|
||||||
|
this.cameraOptions.PictureSourceType = int.Parse(args[2]);
|
||||||
|
this.cameraOptions.TargetWidth = int.Parse(args[3]);
|
||||||
|
this.cameraOptions.TargetHeight = int.Parse(args[4]);
|
||||||
|
this.cameraOptions.EncodingType = int.Parse(args[5]);
|
||||||
|
this.cameraOptions.MediaType = int.Parse(args[6]);
|
||||||
|
this.cameraOptions.AllowEdit = bool.Parse(args[7]);
|
||||||
|
this.cameraOptions.CorrectOrientation = bool.Parse(args[8]);
|
||||||
|
this.cameraOptions.SaveToPhotoAlbum = bool.Parse(args[9]);
|
||||||
|
|
||||||
|
//this.cameraOptions = String.IsNullOrEmpty(options) ?
|
||||||
|
// new CameraOptions() : JSON.JsonHelper.Deserialize<CameraOptions>(options);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO Check if all the options are acceptable
|
||||||
|
|
||||||
|
|
||||||
|
if (cameraOptions.PictureSourceType == CAMERA)
|
||||||
|
{
|
||||||
|
cameraTask = new CameraCaptureTask();
|
||||||
|
cameraTask.Completed += onCameraTaskCompleted;
|
||||||
|
cameraTask.Show();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((cameraOptions.PictureSourceType == PHOTOLIBRARY) || (cameraOptions.PictureSourceType == SAVEDPHOTOALBUM))
|
||||||
|
{
|
||||||
|
photoChooserTask = new PhotoChooserTask();
|
||||||
|
photoChooserTask.Completed += onPickerTaskCompleted;
|
||||||
|
photoChooserTask.Show();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DispatchCommandResult(new PluginResult(PluginResult.Status.NO_RESULT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCameraTaskCompleted(object sender, PhotoResult e)
|
||||||
|
{
|
||||||
|
if (e.Error != null)
|
||||||
|
{
|
||||||
|
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (e.TaskResult)
|
||||||
|
{
|
||||||
|
case TaskResult.OK:
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string imagePathOrContent = string.Empty;
|
||||||
|
|
||||||
|
if (cameraOptions.DestinationType == FILE_URI)
|
||||||
|
{
|
||||||
|
// Save image in media library
|
||||||
|
if (cameraOptions.SaveToPhotoAlbum)
|
||||||
|
{
|
||||||
|
MediaLibrary library = new MediaLibrary();
|
||||||
|
Picture pict = library.SavePicture(e.OriginalFileName, e.ChosenPhoto); // to save to photo-roll ...
|
||||||
|
}
|
||||||
|
|
||||||
|
int orient = ImageExifHelper.getImageOrientationFromStream(e.ChosenPhoto);
|
||||||
|
int newAngle = 0;
|
||||||
|
switch (orient)
|
||||||
|
{
|
||||||
|
case ImageExifOrientation.LandscapeLeft:
|
||||||
|
newAngle = 90;
|
||||||
|
break;
|
||||||
|
case ImageExifOrientation.PortraitUpsideDown:
|
||||||
|
newAngle = 180;
|
||||||
|
break;
|
||||||
|
case ImageExifOrientation.LandscapeRight:
|
||||||
|
newAngle = 270;
|
||||||
|
break;
|
||||||
|
case ImageExifOrientation.Portrait:
|
||||||
|
default: break; // 0 default already set
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream rotImageStream = ImageExifHelper.RotateStream(e.ChosenPhoto, newAngle);
|
||||||
|
|
||||||
|
// we should return stream position back after saving stream to media library
|
||||||
|
rotImageStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
WriteableBitmap image = PictureDecoder.DecodeJpeg(rotImageStream);
|
||||||
|
|
||||||
|
imagePathOrContent = this.SaveImageToLocalStorage(image, Path.GetFileName(e.OriginalFileName));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (cameraOptions.DestinationType == DATA_URL)
|
||||||
|
{
|
||||||
|
imagePathOrContent = this.GetImageContent(e.ChosenPhoto);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: shouldn't this happen before we launch the camera-picker?
|
||||||
|
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Incorrect option: destinationType"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchCommandResult(new PluginResult(PluginResult.Status.OK, imagePathOrContent));
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error retrieving image."));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TaskResult.Cancel:
|
||||||
|
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection cancelled."));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection did not complete!"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPickerTaskCompleted(object sender, PhotoResult e)
|
||||||
|
{
|
||||||
|
if (e.Error != null)
|
||||||
|
{
|
||||||
|
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (e.TaskResult)
|
||||||
|
{
|
||||||
|
case TaskResult.OK:
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string imagePathOrContent = string.Empty;
|
||||||
|
|
||||||
|
if (cameraOptions.DestinationType == FILE_URI)
|
||||||
|
{
|
||||||
|
WriteableBitmap image = PictureDecoder.DecodeJpeg(e.ChosenPhoto);
|
||||||
|
imagePathOrContent = this.SaveImageToLocalStorage(image, Path.GetFileName(e.OriginalFileName));
|
||||||
|
}
|
||||||
|
else if (cameraOptions.DestinationType == DATA_URL)
|
||||||
|
{
|
||||||
|
imagePathOrContent = this.GetImageContent(e.ChosenPhoto);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: shouldn't this happen before we launch the camera-picker?
|
||||||
|
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Incorrect option: destinationType"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchCommandResult(new PluginResult(PluginResult.Status.OK, imagePathOrContent));
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error retrieving image."));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TaskResult.Cancel:
|
||||||
|
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection cancelled."));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection did not complete!"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns image content in a form of base64 string
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">Image stream</param>
|
||||||
|
/// <returns>Base64 representation of the image</returns>
|
||||||
|
private string GetImageContent(Stream stream)
|
||||||
|
{
|
||||||
|
int streamLength = (int)stream.Length;
|
||||||
|
byte[] fileData = new byte[streamLength + 1];
|
||||||
|
stream.Read(fileData, 0, streamLength);
|
||||||
|
|
||||||
|
//use photo's actual width & height if user doesn't provide width & height
|
||||||
|
if (cameraOptions.TargetWidth < 0 && cameraOptions.TargetHeight < 0)
|
||||||
|
{
|
||||||
|
stream.Close();
|
||||||
|
return Convert.ToBase64String(fileData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// resize photo
|
||||||
|
byte[] resizedFile = ResizePhoto(stream, fileData);
|
||||||
|
stream.Close();
|
||||||
|
return Convert.ToBase64String(resizedFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resize image
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">Image stream</param>
|
||||||
|
/// <param name="fileData">File data</param>
|
||||||
|
/// <returns>resized image</returns>
|
||||||
|
private byte[] ResizePhoto(Stream stream, byte[] fileData)
|
||||||
|
{
|
||||||
|
int streamLength = (int)stream.Length;
|
||||||
|
int intResult = 0;
|
||||||
|
|
||||||
|
byte[] resizedFile;
|
||||||
|
|
||||||
|
stream.Read(fileData, 0, streamLength);
|
||||||
|
|
||||||
|
BitmapImage objBitmap = new BitmapImage();
|
||||||
|
MemoryStream objBitmapStream = new MemoryStream(fileData);
|
||||||
|
MemoryStream objBitmapStreamResized = new MemoryStream();
|
||||||
|
WriteableBitmap objWB;
|
||||||
|
objBitmap.SetSource(stream);
|
||||||
|
objWB = new WriteableBitmap(objBitmap);
|
||||||
|
|
||||||
|
// resize the photo with user defined TargetWidth & TargetHeight
|
||||||
|
Extensions.SaveJpeg(objWB, objBitmapStreamResized, cameraOptions.TargetWidth, cameraOptions.TargetHeight, 0, cameraOptions.Quality);
|
||||||
|
|
||||||
|
//Convert the resized stream to a byte array.
|
||||||
|
streamLength = (int)objBitmapStreamResized.Length;
|
||||||
|
resizedFile = new Byte[streamLength]; //-1
|
||||||
|
objBitmapStreamResized.Position = 0;
|
||||||
|
//for some reason we have to set Position to zero, but we don't have to earlier when we get the bytes from the chosen photo...
|
||||||
|
intResult = objBitmapStreamResized.Read(resizedFile, 0, streamLength);
|
||||||
|
|
||||||
|
return resizedFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves captured image in isolated storage
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="imageFileName">image file name</param>
|
||||||
|
/// <returns>Image path</returns>
|
||||||
|
private string SaveImageToLocalStorage(WriteableBitmap image, string imageFileName)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (image == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("imageBytes");
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
|
||||||
|
|
||||||
|
if (!isoFile.DirectoryExists(isoFolder))
|
||||||
|
{
|
||||||
|
isoFile.CreateDirectory(isoFolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
string filePath = System.IO.Path.Combine("///" + isoFolder + "/", imageFileName);
|
||||||
|
|
||||||
|
using (var stream = isoFile.CreateFile(filePath))
|
||||||
|
{
|
||||||
|
// resize image if Height and Width defined via options
|
||||||
|
if (cameraOptions.TargetHeight > 0 && cameraOptions.TargetWidth > 0)
|
||||||
|
{
|
||||||
|
image.SaveJpeg(stream, cameraOptions.TargetWidth, cameraOptions.TargetHeight, 0, cameraOptions.Quality);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
image.SaveJpeg(stream, image.PixelWidth, image.PixelHeight, 0, cameraOptions.Quality);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Uri(filePath, UriKind.Relative).ToString();
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
//TODO: log or do something else
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user