Upload Images to Serial Number - acumatica

I have created a custom NOTEID field in InItemLotserial Table to upload images for each serial number in a custom page. It works fine without any issue.
I have wrote a processing page to update the images for existing serial numbers.
protected void AttachImage(InfoINItemLotSerialImport row, INItemLotSerial ser, InventoryItem itm)
{
string imageurl = row.ImageSourceUrl;
string imageType = row.ImageType;
string sRetVal = string.Empty;
if (itm != null)
{
if (ser != null)
{
try
{
WebClient wc = new WebClient();
byte[] buffer = wc.DownloadData(imageurl);
//MemoryStream ms = new MemoryStream(bytes);
string fileName = Path.GetFileName(imageurl);
fileName = string.Format("Serial Number attribute ({0} {1})\\{2}", itm.InventoryID, ser.LotSerialNbr, fileName);
PX.SM.FileInfo fileinfo = null;
PX.SM.UploadFileMaintenance filegraph = PXGraph.CreateInstance<PX.SM.UploadFileMaintenance>();
fileinfo = new PX.SM.FileInfo(fileName, null, buffer);
fileinfo.IsPublic = true;
if (filegraph.SaveFile(fileinfo))
{
ItemLotSerialMnt oLotSerMnt = PXGraph.CreateInstance<ItemLotSerialMnt>();
oLotSerMnt.lotserailrecdata.Current = ser;
oLotSerMnt.lotserial.Current = ser;
PXNoteAttribute.SetFileNotes(oLotSerMnt.lotserailrecdata.Cache, oLotSerMnt.lotserailrecdata.Current, fileinfo.UID.Value);
PXNoteAttribute.SetNote(oLotSerMnt.lotserailrecdata.Cache, oLotSerMnt.lotserailrecdata.Current, "");
sRetVal = fileinfo.Name;
}
}
catch
{
}
}
}
}
It uploads the data into UploadFile table and entry looks fine. I can access the image using the URL, but the same is not reflecting in file section of serial page.
How acumatica links the file with the screen?
Update 1
#region UsrNoteID
public abstract class usrNoteID : PX.Data.IBqlField
{
}
protected Guid? _UsrNoteID;
[PXNote()]
public virtual Guid? UsrNoteID
{
get
{
return this._UsrNoteID;
}
set
{
this._UsrNoteID = value;
}
}
#endregion

Accumatica support helped me to fix the issue. I have missed firing persist event to save.
if (filegraph.SaveFile(fileinfo))
{
ItemLotSerialMnt oLotSerMnt = PXGraph.CreateInstance<ItemLotSerialMnt>();
oLotSerMnt.lotserailrecdata.Current = ser;
oLotSerMnt.lotserial.Current = ser;
PXNoteAttribute.SetFileNotes(oLotSerMnt.lotserailrecdata.Cache, oLotSerMnt.lotserailrecdata.Current, fileinfo.UID.Value);
PXNoteAttribute.SetNote(oLotSerMnt.lotserailrecdata.Cache, oLotSerMnt.lotserailrecdata.Current, "");
sRetVal = fileinfo.Name;
oLotSerMnt.Persist();
}
oLotSerMnt.Persist();

Related

Unable to open SO screen SO301000 using Action from Quote screen CR304500

I have created a Action called CREATE SO in Sales Quote screen to create Sales order.
I am unable to open the sales order screen using this action. though the sales order is getting created
but the SO screen is not opening while creating the SO. I am not sure where i am making mistake in my code. Please suggest. Thanks.
#region Create Sales Order
public PXAction<CRQuote> createSalesOrder;
[PXUIField(DisplayName = "Create SO", MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Update)]
[PXProcessButton(CommitChanges = true)]
public IEnumerable CreateSalesOrder(PXAdapter adapter)
{
QuoteMaint graphObject = PXGraph.CreateInstance<QuoteMaint>();
foreach (CRQuote quote in adapter.Get())
{
//Create resultset for Quote Details
PXResultset<CROpportunityProducts> PXSetLine = PXSelect<CROpportunityProducts,
Where<CROpportunityProducts.quoteID,
Equal<Required<CROpportunityProducts.quoteID>>>>.Select(this.Base, quote.QuoteID);
List<CROpportunityProducts> QuoteList = new List<CROpportunityProducts>();
foreach (CROpportunityProducts line in PXSetLine)
{
QuoteList.Add(line);
}
PXLongOperation.StartOperation(this, () => CreateSalesOrderMethod(quote, QuoteList));
yield return quote;
}
}
//Private Method for Create Sales Order
public virtual void CreateSalesOrderMethod(CRQuote quote, List<CROpportunityProducts> QuoteList)
{
//Base.Save.Press();
bool var_orderCreated = false;
bool erroroccured = false;
string ErrMsg = "";
SOOrderEntry orderGraphObjet = PXGraph.CreateInstance<SOOrderEntry>();
SOOrder orderHeaderObject = new SOOrder();
QuoteMaint currGRPH = PXGraph.CreateInstance<QuoteMaint>();
BAccount customer = PXSelect<BAccount, Where<BAccount.bAccountID, Equal<Current<CRQuote.bAccountID>>>>.Select(this.Base, quote.BAccountID);
if (customer.Type == "CU")
{
orderHeaderObject.CustomerID = quote.BAccountID;
}
else
{
throw new PXException("Business Account not converted to Customer yet"); // THIS ERROR IS ALSO NOT SHOWING WHILE ENCOUNTERING.
}
orderHeaderObject.CuryOrderTotal = quote.CuryProductsAmount;
orderHeaderObject.CuryTaxTotal = quote.CuryTaxTotal;
orderHeaderObject.OrderDesc = quote.Subject;
orderHeaderObject = orderGraphObjet.CurrentDocument.Insert(orderHeaderObject);
orderGraphObjet.CurrentDocument.Current = orderHeaderObject;
orderGraphObjet.Actions.PressSave();
orderHeaderObject = orderGraphObjet.CurrentDocument.Current;
foreach (CROpportunityProducts tran in QuoteList)
{
SOLine transline = new SOLine(); //EMPTY DAC OBJECT
transline.OrderNbr = orderHeaderObject.OrderNbr;
transline.BranchID = orderHeaderObject.BranchID;
transline.InventoryID = tran.InventoryID;
transline.TranDesc = tran.Descr;
transline.UOM = tran.UOM;
transline.OrderQty = tran.Quantity;
transline.SiteID = tran.SiteID;
transline.CuryUnitPrice = tran.CuryUnitPrice;
transline.CuryExtPrice = tran.CuryExtPrice;
orderGraphObjet.Transactions.Insert(transline); //INSERT DAC INTO DATAVIEW
CROpportunityProductsExt xOppProductExt = PXCache<CROpportunityProducts>.GetExtension<CROpportunityProductsExt>(tran);
SOLineExt _soLext = PXCache<SOLine>.GetExtension<SOLineExt>(transline); // GET DAC ENTENSION
_soLext.UsrXSeqID = xOppProductExt.UsrXSequenceID;
_soLext.UsrXGroupID = xOppProductExt.UsrXGroupID;
_soLext.UsrInternalRemk = xOppProductExt.UsrInternalRemk; // ASSIGN CUSTOM FIELDS
orderGraphObjet.Transactions.Update(transline); // UPDATE DAC OBJECT IN DATAVIEW
}
orderGraphObjet.Actions.PressSave();
var_orderCreated = true;
//if (orderGraphObjet != null && orderHeaderObject != null)
if (var_orderCreated)
{
orderGraphObjet.Document.Current = orderHeaderObject; // HERE I AM GETTING THE OrderType as well as OrderNbr to open the Document.
throw new PXRedirectRequiredException(orderGraphObjet, "Document") { Mode = PXBaseRedirectException.WindowMode.NewWindow };
}
}
#endregion
}
}
The issue is that the redirection is in the PXLongOperation. A PXLongOperation starts a separate thread that is not related with the UI. In order to solve this you can use the PXCustomInfo structure to talk back with the UI thread after the long operation finishes and thus be able to redirect to the so screen.
// PXCustomInfoDefinition
public sealed class SORedirectionCustomInfo: IPXCustomInfo
{
private PXGraph _OrderGraph;
public SORedirectionCustomInfo(PXGraph orderGraph)
{
_OrderGraph = orderGraph;
}
public void Complete(PXLongRunStatus status, PXGraph graph)
{
if (status == PXLongRunStatus.Completed && graph is <YourQuoteGraphType>)
{
throw new PXRedirectRequiredException(orderGraphObjet, "Document") { Mode = PXBaseRedirectException.WindowMode.NewWindow };
}
}
}
// Long Operation Method
public virtual void CreateSalesOrderMethod(CRQuote quote, List<CROpportunityProducts> QuoteList)
{
// Your code to create SO....
if (var_orderCreated)
{
PXLongOperation.SetCustomInfo(new SORedirectionCustomInfo(orderGraphObjet));
}
}
This should be able to redirect successfully once the long operation is completed and the order is created.

Filechooser selecting a word file downloaded from GMail crashes app

I am using a file chooser to pick a WORD file downloaded from GMail, it causes the app crashed.Here's my code segment:
== file chooser code ==
Intent intent = new Intent();
intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
intent.addCategory(Intent.CATEGORY_OPENABLE);
//sets the select file to all types of files
String [] mimeTypes = {"application/pdf", "application/msword",
"application/vnd.openxmlformats
officedocument.wordprocessingml.document"};
intent.setType("*/*");
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
intent.putExtra("android.content.extra.SHOW_ADVANCED", true);
activity.startActivityForResult(Intent.createChooser(intent, "Select
File"), PICK_FILE_REQUEST);
== onActivityResult ==
Uri selectedFileUri = data.getData();
String selectedFilePath = FilePath.getPath(getActivity(),
selectedFileUri);
== FilePath.getPath() ==
...
// DownloadsProvider
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri =
ContentUris.withAppendedId(Uri.parse("content://downloads
/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
== getDataColumn() ==
public static String getDataColumn(Context context, Uri uri,
String selection, String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = { column };
try {
cursor = context.getContentResolver().query(uri, projection,
selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
The selectedFilePath has this value: content://com.android.providers.downloads.documents/document/164, the contentUri in FilePath.getPath() also has this value. When it goes in getDataColumn() method, the cursor is null after executing "query()".
Tried those: 1) If I put the same WORD file direct to the "Download" folder and pick it from "Downloads" link from the file chooser, I have no issue. It seems that somehow going through GMail and downloading from GMail causes problem. 2) File still downloaded from GMail and in Download folder, if I pick it through Internal storage->Download (i.e, absolute path), it works since the code goes through different flow (which is not shown above).
I am wondering where I missed in the code to handle the file downloaded from GMail?
Thanks in advance!
The Phone is Galaxy S9, android 9.
After some search and experiment, it ends up retrieving file name and content from InputStream instead of trying to get the path of the file. Here's the code segment:
1) get file details:
Uri selectedFileUri = data.getData();
FileDetail fileDetail = FilePath.getFileDetailFromUri(getActivity(), selectedFileUri);
String fileName = fileDetail.getFileName();
int fileSize = (int)fileDetail.getFileSize();
Here's the FileDetail.java:
public class FileDetail {
// fileSize.
public String fileName;
// fileSize in bytes.
public long fileSize;
public FileDetail() {
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public long getFileSize() {
return fileSize;
}
public void setFileSize(long fileSize) {
this.fileSize = fileSize;
}
}
In FilePath.java:
public static FileDetail getFileDetailFromUri(Context context, Uri uri) {
FileDetail fileDetail = null;
if (uri != null) {
fileDetail = new FileDetail();
// File Scheme.
if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
File file = new File(uri.getPath());
fileDetail.setFileName(file.getName());
fileDetail.setFileSize(file.length());
}
// Content Scheme.
else if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
Cursor returnCursor =
context.getContentResolver().query(uri, null, null, null, null);
if (returnCursor != null && returnCursor.moveToFirst()) {
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
fileDetail.setFileName(returnCursor.getString(nameIndex));
fileDetail.setFileSize(returnCursor.getLong(sizeIndex));
returnCursor.close();
}
}
}
return fileDetail;
}
2) get the file content:
InputStream is = cr.openInputStream(selectedFileUri);
byte[] fileContent = new byte[fileSize];
is.read(fileContent);
I found those codes from internet and didn't keep track of where they are. With Filename & filesize in FileDetail.java and the file content in "filecontent" byte arrays, I have all information I need now.

how to attach an XML file in the "Files" tab of an action

I have a big question, you can attach XML through an action, when you import the XML and also save it in the "Files" tab, the question is. if it can be attached?.
Here is an example image:
what is missing is that I automatically attach in the "Files" tab, when I put upload
Here is my code where, I attached the XML
public PXAction<ARInvoice> CargarXML;
[PXUIField(DisplayName = "Carga de código hash")]
[PXButton()]
public virtual void cargarXML()
{
var Result = NewFilePanel.AskExt(true);
if (Result == WebDialogResult.OK)
{
PX.SM.FileInfo fileInfo = PX.Common.PXContext.SessionTyped<PXSessionStatePXData>().FileInfo["CargaSessionKey"];
string result = System.Text.Encoding.UTF8.GetString(fileInfo.BinData);
ARInvoice ari = Base.Document.Current;
xtFECodHashEntry graph2 = PXGraph.CreateInstance<xtFECodHashEntry>();
var pchExt = ari.GetExtension<ARRegisterExt>();
string Valor = "";
XmlDocument xm = new XmlDocument();
xm.LoadXml(result);
XmlNodeList elemList = xm.GetElementsByTagName("ds:DigestValue");
for (int i = 0; i < elemList.Count;)
{
Valor = (elemList[i].InnerXml);
break;
}
PXLongOperation.StartOperation(Base, delegate ()
{
xtFECodHash dac = new xtFECodHash();
dac.RefNbr = ari.RefNbr;
dac.DocType = ari.DocType;
dac.Nrocomprobante = ari.InvoiceNbr;
dac.Hash = Valor;
dac.Tipo = pchExt.UsrTDocSunat;
graph2.xtFECodHashs.Insert(dac);
graph2.Actions.PressSave();
});
}
}
If you have the FileInfo object which it seems like you do I have used something like this:
protected virtual void SaveFile(FileInfo fileInfo, PXCache cache, object row)
{
if (fileInfo == null)
{
return;
}
var fileGraph = PXGraph.CreateInstance<UploadFileMaintenance>();
if (!fileGraph.SaveFile(fileInfo, FileExistsAction.CreateVersion))
{
return;
}
if (!fileInfo.UID.HasValue)
{
return;
}
PXNoteAttribute.SetFileNotes(cache, row, fileInfo.UID.Value);
}
So in your example you could call this method as shown above using the following:
SaveFile(fileInfo, Base.Document.Cache, Base.Document.Current);

Xamarin IOS file picker : unable to get selected file from icloud drive

I've installed filepicker control from Nuget and added tried adding reference from MonoTouch10 folder and later from github to my xamarin.ios project.
FileData file = await CrossFilePicker.Current.PickFile();
if (file != null) { }
this is the code i added to my browse button, after selecting a file from iCloud drive, control never comes to "if condition".
and again when i click on browse button for second time, app crashes saying "only one operation can be active at a time".
Modifying the source code of the FilePickerImplementation plugin for the iOS platform worked, in this way:
using Foundation;
using MobileCoreServices;
using Plugin.FilePicker.Abstractions;
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using UIKit;
using System.Diagnostics;
namespace Plugin.FilePicker
{
/// <summary>
/// Implementation for FilePicker
/// </summary>
public class FilePickerImplementation : NSObject, IUIDocumentMenuDelegate, IFilePicker
{
private int _requestId;
private TaskCompletionSource<FileData> _completionSource;
/// <summary>
/// Event which is invoked when a file was picked
/// </summary>
public EventHandler<FilePickerEventArgs> Handler
{
get;
set;
}
private void OnFilePicked(FilePickerEventArgs e)
{
Handler?.Invoke(null, e);
}
public void DidPickDocumentPicker(UIDocumentMenuViewController documentMenu, UIDocumentPickerViewController documentPicker)
{
documentPicker.DidPickDocument += DocumentPicker_DidPickDocument;
documentPicker.WasCancelled += DocumentPicker_WasCancelled;
documentPicker.DidPickDocumentAtUrls += DocumentPicker_DidPickDocumentAtUrls;
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(documentPicker, true, null);
}
private void DocumentPicker_DidPickDocumentAtUrls(object sender, UIDocumentPickedAtUrlsEventArgs e)
{
var control = (UIDocumentPickerViewController)sender;
foreach (var url in e.Urls)
DocumentPicker_DidPickDocument(control, new UIDocumentPickedEventArgs(url));
control.Dispose();
}
private void DocumentPicker_DidPickDocument(object sender, UIDocumentPickedEventArgs e)
{
var securityEnabled = e.Url.StartAccessingSecurityScopedResource();
var doc = new UIDocument(e.Url);
var data = NSData.FromUrl(e.Url);
var dataBytes = new byte[data.Length];
System.Runtime.InteropServices.Marshal.Copy(data.Bytes, dataBytes, 0, Convert.ToInt32(data.Length));
string filename = doc.LocalizedName;
string pathname = doc.FileUrl?.ToString();
// iCloud drive can return null for LocalizedName.
if (filename == null)
{
// Retrieve actual filename by taking the last entry after / in FileURL.
// e.g. /path/to/file.ext -> file.ext
// filesplit is either:
// 0 (pathname is null, or last / is at position 0)
// -1 (no / in pathname)
// positive int (last occurence of / in string)
var filesplit = pathname?.LastIndexOf('/') ?? 0;
filename = pathname?.Substring(filesplit + 1);
}
OnFilePicked(new FilePickerEventArgs(dataBytes, filename, pathname));
}
/// <summary>
/// Handles when the file picker was cancelled. Either in the
/// popup menu or later on.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void DocumentPicker_WasCancelled(object sender, EventArgs e)
{
{
var tcs = Interlocked.Exchange(ref _completionSource, null);
tcs.SetResult(null);
}
}
/// <summary>
/// Lets the user pick a file with the systems default file picker
/// For iOS iCloud drive needs to be configured
/// </summary>
/// <returns></returns>
public async Task<FileData> PickFile()
{
var media = await TakeMediaAsync();
return media;
}
private Task<FileData> TakeMediaAsync()
{
var id = GetRequestId();
var ntcs = new TaskCompletionSource<FileData>(id);
if (Interlocked.CompareExchange(ref _completionSource, ntcs, null) != null)
throw new InvalidOperationException("Only one operation can be active at a time");
var allowedUtis = new string[] {
UTType.UTF8PlainText,
UTType.PlainText,
UTType.RTF,
UTType.PNG,
UTType.Text,
UTType.PDF,
UTType.Image,
UTType.UTF16PlainText,
UTType.FileURL
};
var importMenu =
new UIDocumentMenuViewController(allowedUtis, UIDocumentPickerMode.Import)
{
Delegate = this,
ModalPresentationStyle = UIModalPresentationStyle.Popover
};
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(importMenu, true, null);
var presPopover = importMenu.PopoverPresentationController;
if (presPopover != null)
{
presPopover.SourceView = UIApplication.SharedApplication.KeyWindow.RootViewController.View;
presPopover.PermittedArrowDirections = UIPopoverArrowDirection.Down;
}
Handler = null;
Handler = (s, e) => {
var tcs = Interlocked.Exchange(ref _completionSource, null);
tcs?.SetResult(new FileData(e.FilePath, e.FileName, () => { var url = new Foundation.NSUrl(e.FilePath); return new FileStream(url.Path, FileMode.Open, FileAccess.Read); }));
};
return _completionSource.Task;
}
public void WasCancelled(UIDocumentMenuViewController documentMenu)
{
var tcs = Interlocked.Exchange(ref _completionSource, null);
tcs?.SetResult(null);
}
private int GetRequestId()
{
var id = _requestId;
if (_requestId == int.MaxValue)
_requestId = 0;
else
_requestId++;
return id;
}
public async Task<bool> SaveFile(FileData fileToSave)
{
try
{
var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var fileName = Path.Combine(documents, fileToSave.FileName);
File.WriteAllBytes(fileName, fileToSave.DataArray);
return true;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
return false;
}
}
public void OpenFile(NSUrl fileUrl)
{
var docControl = UIDocumentInteractionController.FromUrl(fileUrl);
var window = UIApplication.SharedApplication.KeyWindow;
var subViews = window.Subviews;
var lastView = subViews.Last();
var frame = lastView.Frame;
docControl.PresentOpenInMenu(frame, lastView, true);
}
public void OpenFile(string fileToOpen)
{
var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var fileName = Path.Combine(documents, fileToOpen);
if (NSFileManager.DefaultManager.FileExists(fileName))
{
var url = new NSUrl(fileName, true);
OpenFile(url);
}
}
public async void OpenFile(FileData fileToOpen)
{
var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var fileName = Path.Combine(documents, fileToOpen.FileName);
if (NSFileManager.DefaultManager.FileExists(fileName))
{
var url = new NSUrl(fileName, true);
OpenFile(url);
}
else
{
await SaveFile(fileToOpen);
OpenFile(fileToOpen);
}
}
}
}
To answer my own question, i customized in plugin, like
Used DocumentPicker_DidPickDocumentAtUrls event instead of DocumentPicker_DidPickDocument.
while returning selected file used
new FileData(e.FilePath, e.FileName, () =>
{
var url = new Foundation.NSUrl(e.FilePath);
return new FileStream(url.Path, FileMode.Open, FileAccess.Read);
})
This solved my issue. Thanks.
There are several forks of the FilePicker Xamarin plugin. I recommend the following project, since it's the most actively maintained one:
https://github.com/jfversluis/FilePicker-Plugin-for-Xamarin-and-Windows (note: I'm one of the contributors to the project).
With this version of the plugin file picking should work. The example code from sandeep's answer was already incorporated into the latest version of the plugin. Be sure to read the README.md's Troubleshooting page in case you're getting problems.

Need help to deal with Photo for Xamarin forms

I am using xamarin forms. I want to pick photo from gallery for my iphone app and want to save it in Azure DB. Is there any solution available for xamarin forms. Or Is there any plugin available to deal with Photo, Document, or Audio. Any help is appreciated.
Using dependency service you can take or pick photos from Android / iPhone :-
Please refer to code below and try to implement the similar code:-
This is the interface in PCL:-
public interface IGalleryProvider
{
Task<List<AttachmentMediaFile>> PickPhotoAsync();
Task<List<AttachmentMediaFile>> PickAudioAsync();
Task<List<AttachmentMediaFile>> PickDocumentAsync();
Task<AttachmentMediaFile> PickProfilePhotoAsync();
Task SaveToGalleryAsync(AttachmentMediaFile file);
}
Below is the code using which you can pick or take photos from iPhone only:-
using AssetsLibrary;
using AVFoundation;
using ELCImagePicker;
using Foundation;
using MediaPlayer;
using MobileCoreServices;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UIKit;
[assembly: Xamarin.Forms.Dependency(typeof(GalleryProvider))]
namespace SanketSample.MobileApp.Sample.iOS.Common
{
public class GalleryProvider : IGalleryProvider
{
private TaskCompletionSource<List<AttachmentMediaFile>> _audioPickedTask;
public async Task<List<AttachmentMediaFile>> PickAudioAsync()
{
_audioPickedTask = new TaskCompletionSource<List<AttachmentMediaFile>>();
var picker = new MPMediaPickerController();
ShowViewController(picker);
picker.ItemsPicked += OnAudioPicked;
picker.DidCancel += OnCancel;
var media = await _audioPickedTask.Task;
return media;
}
private void OnCancel(object sender, EventArgs e)
{
var picker = sender as MPMediaPickerController;
picker.DidCancel -= OnCancel;
picker.DismissViewController(true, null);
_audioPickedTask.TrySetResult(new List<AttachmentMediaFile>());
}
private void OnAudioPicked(object sender, ItemsPickedEventArgs e)
{
var media = new List<AttachmentMediaFile>();
var picker = sender as MPMediaPickerController;
picker.ItemsPicked -= OnAudioPicked;
picker.DismissViewController(true, null);
if (e.MediaItemCollection.Items != null)
{
foreach (var item in e.MediaItemCollection.Items)
{
//var vm1 = (new ViewModelLocator()).AttachmentsVM.SelectedAttachments.Add();
if (!item.IsCloudItem)
{
try
{
//var error = new NSError();
//var asset = new AVUrlAsset(item.AssetURL);
//var exporter = new AVAssetExportSession(asset, item.Title);
//exporter.OutputFileType = "com.apple.m4a-audio";
//AVAssetExportSession session = new AVAssetExportSession(asset, "");
//var reader = new AVAssetReader(asset, out error);
//var settings = new NSDictionary();
//Func<byte[]> bytesGetter = e.MediaItemCollection
//TODO item.Title, item.Title SSSanket,
//var _asset = AVAsset.FromUrl(NSUrl.FromFilename(item.AssetURL.ToString()));
//var _exportSession = new AVAssetExportSession(_asset, AVAssetExportSession.PresetPassthrough);
//_exportSession.OutputFileType = AVFileType.Aiff;
// media.Add(new AttachmentMediaFile(item.AssetURL.AbsoluteString, AttachmentMediaFileType.Audio, null , item.Title));
}
catch (Exception ex)
{
// throw ;
}
}
}
}
_audioPickedTask.TrySetResult(media);
}
public async Task<List<AttachmentMediaFile>> PickDocumentAsync()
{
var task = new TaskCompletionSource<List<AttachmentMediaFile>>();
var allowedUTIs = new string[]
{
UTType.UTF8PlainText,
UTType.PlainText,
UTType.RTF,
UTType.Text,
UTType.PDF,
"com.microsoft.word.doc",
"com.microsoft.excel.xls"
};
var pickerMenu = new UIDocumentMenuViewController(allowedUTIs, UIDocumentPickerMode.Open);
pickerMenu.DidPickDocumentPicker += (sender, args) =>
{
args.DocumentPicker.DidPickDocument += (sndr, pArgs) =>
{
var securityEnabled = pArgs.Url.StartAccessingSecurityScopedResource();
NSError err;
var fileCoordinator = new NSFileCoordinator();
var docs = new List<AttachmentMediaFile>();
// Read bytes.
fileCoordinator.CoordinateRead(pArgs.Url, 0, out err, (NSUrl newUrl) =>
{
NSData data = NSData.FromUrl(newUrl);
docs.Add(new AttachmentMediaFile(pArgs.Url.AbsoluteString, AttachmentMediaFileType.Doc, data.ToArray(),null));
task.TrySetResult(docs);
});
};
ShowViewController(args.DocumentPicker);
};
ShowViewController(pickerMenu);
return await task.Task;
}
public async Task<List<AttachmentMediaFile>> PickPhotoAsync()
{
var media = new List<AttachmentMediaFile>();
var picker = ELCImagePickerViewController.Instance;
picker.MaximumImagesCount = 15;
ShowViewController(picker);
await picker.Completion.ContinueWith(result =>
{
picker.BeginInvokeOnMainThread(() =>
{
picker.DismissViewController(true, null);
if (!result.IsCanceled && result.Exception == null)
{
var imageEditor = new ImageEditor();
var items = result.Result as List<AssetResult>;
foreach (var item in items)
{
var bbytes= imageEditor.ResizeImage(item.Image, 1024, 1024);
media.Add(new AttachmentMediaFile(item.Path, AttachmentMediaFileType.Photo, bbytes, item.Name));
}
}
});
});
return media;
}
public async Task<AttachmentMediaFile> PickProfilePhotoAsync()
{
AttachmentMediaFile selectMediaFile = null;
var picker = ELCImagePickerViewController.Instance;
picker.MaximumImagesCount = 1;
ShowViewController(picker);
await picker.Completion.ContinueWith(result =>
{
picker.BeginInvokeOnMainThread(() =>
{
picker.DismissViewController(true, null);
if (!result.IsCanceled && result.Exception == null)
{
var imageEditor = new ImageEditor();
var items = result.Result as List<AssetResult>;
foreach (var item in items)
{
var bbytes = imageEditor.ResizeImage(item.Image, 1024, 1024);
selectMediaFile = new AttachmentMediaFile(item.Path, AttachmentMediaFileType.Photo, bbytes, item.Name);
}
}
});
});
return selectMediaFile;
}
public async Task SaveToGalleryAsync(AttachmentMediaFile file)
{
var bytes = file.GetBytes();
var originalImage = ImageEditor.ImageFromByteArray(bytes);
var library = new ALAssetsLibrary();
var orientation = (ALAssetOrientation)originalImage.Orientation;
var nsUrl = await library.WriteImageToSavedPhotosAlbumAsync(originalImage.CGImage, orientation);
}
private void ShowViewController(UIViewController controller)
{
var topController = UIApplication.SharedApplication.KeyWindow.RootViewController;
while (topController.PresentedViewController != null)
{
topController = topController.PresentedViewController;
}
topController.PresentViewController(controller, true, null);
}
}
}
Below are useful classes :-
public class AttachmentMediaFile
{
private readonly Func<byte[]> _bytesGetter;
public string LocalPath { get; private set; }
public string Name { get; private set; }
public AttachmentMediaFileType Type { get; private set; }
public AttachmentMediaFile(string localPath, AttachmentMediaFileType type, byte[] bytesGetter, string name = null)
{
LocalPath = localPath;
Type = type;
_bytesGetter = () =>
{
return bytesGetter;
};
if (string.IsNullOrEmpty(name))
{
Name = FileNameHelper.PrepareName(localPath);
}
else
{
Name = name;
}
}
public byte[] GetBytes()
{
return _bytesGetter();
}
}
public enum AttachmentMediaFileType
{
Photo = 0,
Audio = 1,
Doc = 2,
Video = 3,
}
public static class FileNameHelper
{
private const string Prefix = "IMG";
public static string PrepareName(string localPath)
{
var name = string.Empty;
if (!string.IsNullOrEmpty(localPath))
{
name = localPath.Split('/').Last();
}
return name;
}
public static string GenerateUniqueFileName(Extension extension)
{
var format = ".jpg";
var fileName = string.Concat(Prefix, '_', DateTime.UtcNow.Ticks, format);
return fileName;
}
public enum Extension
{
JPG
}
}
Now if you want to store your data to Azure Server Table so you are already using Azure mobile service client SDK similarly you need Blob nuget from Azure using which you can save your photos by making blob objects to Azure server :-
use blob helper nuget from manage nuget package install Microsoft.WindowsAzure.Storage.Auth;
Microsoft.WindowsAzure.Storage.Blob;
this and try to implement the code similarly I given bellow:-
using Acr.UserDialogs;
using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure.Storage.Blob;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using SanketSample.MobileApp.sample.Business.Azure;
using SanketSample.MobileApp.sample.Business.Interfaces;
using SanketSample.MobileApp.sample.Models;
using SanketSample.MobileApp.sample.Models.AzureTables;
using SanketSample.MobileApp.sample.Models.Media;
using SanketSample.MobileApp.sample.Utils;
using Xamarin.Forms;
namespace SanketSample.MobileApp.Sanket.Common.Media
{
public class BlobHelper
{
private const string ContainerName = "attachments";
private Dictionary<string, TaskCompletionSource<bool>> _tasks;
private IHttpService _httpservice { get; set; }
#region Singleton Implementation
private static readonly Lazy<BlobHelper> lazyInstance = new Lazy<BlobHelper>(() => new BlobHelper(), true);
private BlobHelper()
{
_tasks = new Dictionary<string, TaskCompletionSource<bool>>();
}
public static BlobHelper Instance
{
get { return lazyInstance.Value; }`enter code here`
}
#endregion Singleton Implementation
public async Task UploadAttachments(IList<AttachmentFile> attachments, long associatedRecordId, string category)
{
foreach (var attachment in attachments)
{
await UploadAttachment(attachment, associatedRecordId, category);
}
}
public async Task UploadAttachment(AttachmentFile attachment, long associatedRecordId, string category)
{
try
{
CommonHelper commonHelper = new CommonHelper();
attachment.ContainerName = ContainerName;
attachment.AssociatedRecordId = associatedRecordId;
//attachment.RecordId = commonHelper.GenerateRecordId();
if (attachment.FileExtension == null)
{
attachment.FileExtension = ConvertType(attachment.MediaFile);
}
attachment.Category = category;
var taskCompletionSource = new TaskCompletionSource<bool>();
if (!_tasks.ContainsKey(attachment.Name))
{ _tasks.Add(attachment.Name, taskCompletionSource); }
else
{
_tasks[attachment.Name] = taskCompletionSource;
}
// _tasks.Add(attachment.Name, taskCompletionSource);
var attachmentsTableOnline = AzureServiceProvider.Instance.GetRemoteTable<AttachmentFile>();
if (CheckInternetConnection.IsConnected())
{
await attachmentsTableOnline.InsertAsync(attachment);
}
var attachmentsTableOffline = AzureServiceProvider.Instance.GetLocalTable<AttachmentFile>();
await attachmentsTableOffline.InsertAsync(attachment);
if (!string.IsNullOrEmpty(attachment.SasQueryString))
{
var credentials = new StorageCredentials(attachment.SasQueryString);
var imageUri = new Uri(attachment.Uri);
var container = new CloudBlobContainer(new Uri(string.Format("https://{0}/{1}",
imageUri.Host, attachment.ContainerName)), credentials);
var blobFromSASCredential = container.GetBlockBlobReference(attachment.Name);
try
{
var bytes = attachment.MediaFile.GetBytes();
await blobFromSASCredential.UploadFromByteArrayAsync(bytes, 0, bytes.Length);
if (CheckInternetConnection.IsConnected())
{
await attachmentsTableOnline.UpdateAsync(attachment);
}
await attachmentsTableOffline.UpdateAsync(attachment);
taskCompletionSource.TrySetResult(true);
}
catch (Microsoft.WindowsAzure.Storage.StorageException ex)
{
// Throws from UploadFromByteArrayAsync, but image uploaded.
System.Diagnostics.Debug.WriteLine($"BlobHelper: {ex}");
taskCompletionSource.TrySetResult(true);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"BlobHelper: {ex}");
taskCompletionSource.TrySetResult(false);
}
}
}
catch (Exception ca)
{
//throw ca;
}
}
/// <summary>
/// Downloads Blob Data boject and returns the Byts[] data
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public async Task<byte[]> DownloadAttachment(AttachmentFile file)
{
byte[] bytes = null;
var fileContainer = file.Uri.Replace(file.Name, string.Empty);
var container = new CloudBlobContainer(new Uri(fileContainer));
var blob = container.GetBlockBlobReference(file.Name);
using (var stream = new MemoryStream())
{
var isExist = await blob.ExistsAsync();
if (isExist)
{
await blob.DownloadToStreamAsync(stream);
bytes = stream.ToArray();
}
}
return bytes;
}
/// <summary>
/// Updates the Attachments Byts in the Azure Local Tables.
/// </summary>
/// <param name="AttachmentFileRecordId">Attachments Byte[] Data.</param>
/// <returns></returns>
public async Task<byte[]> DownloadAttachmentFileDetails(long? AttachmentFileRecordId, IHttpService service)
{
_httpservice = service;
try
{
ResponseWrapper<AttachmentFileDetail> result = new ResponseWrapper<AttachmentFileDetail>();
if (AttachmentFileRecordId != null)
{
var request = Constants.API_BASE_URL + string.Format(Constants.API_ATTACHMENTS_PARAMETERS, AttachmentFileRecordId);
var response = await _httpservice.SendRequestAsync(HttpMethod.Get, request);
result.Status = response.Status;
if (response.IsSuccess)
{
result.Result = JsonConvert.DeserializeObject<AttachmentFileDetail>(response.Result);
if (result.Result == null)
{
result.Status = System.Net.HttpStatusCode.InternalServerError;
}
else
{
var output = result.Result;
var data = new List<AttachmentFileDetail>() { output };
await AzureServiceProvider.Instance.DatabaseService.InsertDataToLocalDB<AttachmentFileDetail>(data);
return result.Result.FileByteArray;
}
}
}
}
catch (Exception ex)
{
////throw ex;
}
finally
{
}
return null;
}
private string ConvertType(AttachmentMediaFile file)
{
switch (file.Type)
{
case AttachmentMediaFileType.Doc:
return "doc";
case AttachmentMediaFileType.Audio:
return "mp3";
}
return "jpeg";
}
}
}
media plugin on github
works pretty well for me.

Resources