Since ALAssetLibrary Api is deprecated we are using PHPhotoLibrary to save images to PhotoAlbum
If we use ALAssetLibrary,we can able to get assetUri of the saved image, but we cannot able to get the same if we use PHPhotoLibrary .
Example For assetUri-
/Library/Developer/CoreSimulator/Devices/1ABD9386-B868-4B8D-9670-3F260D574569/data/Media/DCIM/100APPLE/
Is there another way to get the same path as mentioned above using PHPhotoLibrary?
Thanks in Advance
AssetUri is not available with PHPhotoLibrary .
We can use request.PlaceholderForCreatedAsset.LocalIdentifier instead.
UIImagePickerController controller = new UIImagePickerController {
SourceType = UIImagePickerControllerSourceType.SavedPhotosAlbum,
ImagePickerControllerDelegate = new PickerDelegate(),
MediaTypes = new string[] { UTType.Image }
};
this.PresentViewController(controller, true, null);
public class PickerDelegate: UIImagePickerControllerDelegate
{
public override void FinishedPickingImage(UIImagePickerController picker, UIImage image, NSDictionary editingInfo)
{
PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(
() => {
PHAssetChangeRequest request = PHAssetChangeRequest.FromImage(image);
string id = request.PlaceholderForCreatedAsset.LocalIdentifier;
},
(bool success, NSError error) => {
}
);
}
}
Refer to
How to get the URL of an image just added in PHPhotoLibrary
Get URL of a newly created asset, iOS 9 style
Related
I followed this tutorial to implement Push-Notifications in my Xamarin-Forms App (especially the iOS part). Now my problem is, when I press the register-button, I get the error message "Unable to resolve token for APNS".
Stepping through the code in debug mode I could verify, that the Token property in DeviceInstallationService is indeed null.
So I've gone one step back, and identified that the Token is set only via RegisteredForRemoteNotification in AppDelegate.cs, but this method is never called when I run the App.
Here is some code: App-Delegate
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Foundation;
using Notes.iOS.Extensions;
using Notes.iOS.Services;
using Notes.Services;
using UIKit;
using UserNotifications;
using Xamarin.Essentials;
using System.Collections.Generic;
using System.Linq;
using Syncfusion.SfCalendar.XForms.iOS;
namespace Notes.iOS
{
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
IPushDemoNotificationActionService _notificationActionService;
INotificationRegistrationService _notificationRegistrationService;
IDeviceInstallationService _deviceInstallationService;
IPushDemoNotificationActionService NotificationActionService
=> _notificationActionService ??
(_notificationActionService =
ServiceContainer.Resolve<IPushDemoNotificationActionService>());
INotificationRegistrationService NotificationRegistrationService
=> _notificationRegistrationService ??
(_notificationRegistrationService =
ServiceContainer.Resolve<INotificationRegistrationService>());
IDeviceInstallationService DeviceInstallationService
=> _deviceInstallationService ??
(_deviceInstallationService =
ServiceContainer.Resolve<IDeviceInstallationService>());
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
Bootstrap.Begin(() => new DeviceInstallationService());
if (DeviceInstallationService.NotificationsSupported)
{
UNUserNotificationCenter.Current.RequestAuthorization(
UNAuthorizationOptions.Alert |
UNAuthorizationOptions.Badge |
UNAuthorizationOptions.Sound,
(approvalGranted, error) =>
{
if (approvalGranted && error == null)
RegisterForRemoteNotifications();
});
}
LoadApplication(new App());
using (var userInfo = options?.ObjectForKey(
UIApplication.LaunchOptionsRemoteNotificationKey) as NSDictionary)
ProcessNotificationActions(userInfo);
return base.FinishedLaunching(app, options);
}
void RegisterForRemoteNotifications()
{
MainThread.BeginInvokeOnMainThread(() =>
{
var pushSettings = UIUserNotificationSettings.GetSettingsForTypes(
UIUserNotificationType.Alert |
UIUserNotificationType.Badge |
UIUserNotificationType.Sound,
new NSSet());
UIApplication.SharedApplication.RegisterUserNotificationSettings(pushSettings);
UIApplication.SharedApplication.RegisterForRemoteNotifications();
});
}
Task CompleteRegistrationAsync(NSData deviceToken)
{
DeviceInstallationService.Token = deviceToken.ToHexString();
return NotificationRegistrationService.RefreshRegistrationAsync();
}
void ProcessNotificationActions(NSDictionary userInfo)
{
if (userInfo == null)
return;
try
{
var actionValue = userInfo.ObjectForKey(new NSString("action")) as NSString;
if (!string.IsNullOrWhiteSpace(actionValue?.Description))
NotificationActionService.TriggerAction(actionValue.Description);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
=> CompleteRegistrationAsync(deviceToken).ContinueWith((task)
=> { if (task.IsFaulted) throw task.Exception; });
public override void ReceivedRemoteNotification(
UIApplication application,
NSDictionary userInfo)
=> ProcessNotificationActions(userInfo);
public override void FailedToRegisterForRemoteNotifications(
UIApplication application,
NSError error)
=> Debug.WriteLine(error.Description);
}
}
DeviceInstallationService:
using System;
using Notes.Models;
using Notes.Services;
using UIKit;
namespace Notes.iOS.Services
{
public class DeviceInstallationService : IDeviceInstallationService
{
const int SupportedVersionMajor = 13;
const int SupportedVersionMinor = 0;
public string Token { get; set; }
public bool NotificationsSupported
=> UIDevice.CurrentDevice.CheckSystemVersion(SupportedVersionMajor, SupportedVersionMinor);
public string GetDeviceId()
=> UIDevice.CurrentDevice.IdentifierForVendor.ToString();
public DeviceInstallation GetDeviceInstallation(params string[] tags)
{
if (!NotificationsSupported)
throw new Exception(GetNotificationsSupportError());
if (string.IsNullOrWhiteSpace(Token))
throw new Exception("Unable to resolve token for APNS");
var installation = new DeviceInstallation
{
InstallationId = GetDeviceId(),
Platform = "apns",
PushChannel = Token
};
installation.Tags.AddRange(tags);
return installation;
}
string GetNotificationsSupportError()
{
if (!NotificationsSupported)
return $"This app only supports notifications on iOS {SupportedVersionMajor}.{SupportedVersionMinor} and above. You are running {UIDevice.CurrentDevice.SystemVersion}.";
if (Token == null)
return $"This app can support notifications but you must enable this in your settings.";
return "An error occurred preventing the use of push notifications";
}
}
}
As you can see this is really 1:1 the example code, the only difference is that my project is called Notes.
I skipped the Firebase and Android-Part as I only need push-notifications for iOS so far and as far as I underestood these are not necessary for iOS only.
Thanks your help!
Some points to check if RegisteredForRemoteNotification not called:
Open Entitlements.plist and ensure that Enable Push Notifications is checked when viewed in the Entitlements tab. Then, ensure the APS Environment setting is set to development when viewed in the Source tab.
Make sure that you are testing the remote-notification in a real device instead of a simulator. A simulator does not support remote-notification.
Make sure that you agreed receiving notification permission.
Make sure the certification you use has enabled the push notification ability.
Refer: configuring-the-remote-notifications-environment
You can look at the message returned from the following function in your App Delegate AppDelegate.cs
public override void FailedToRegisterForRemoteNotifications(
UIApplication application,
NSError error)
For instance
no valid “aps-environment” entitlement string found for application
I know how to fetch data from a custom Generic Inquiry using standard soap / page-based web services.
Here's my code for standard web services to get the results from a custom GI:
static void Main(string[] args)
{
GI000081.Screen context = new GI000081.Screen();
context.Url = "http://localhost/AcumaticaDB181000062/(W(6))/Soap/GI000081.asmx";
context.CookieContainer = new System.Net.CookieContainer();
LoginResult loginResult = context.Login("admin", "Passw0rd");
if (loginResult.Code != ErrorCode.OK)
{
throw new Exception(loginResult.Message);
}
GI000081.Content GI000081Content;
GI000081Content = context.GetSchema(); //.IN202500GetSchema();
//Here's the code to obtain the GI data:
string[][] GI000081Data = context.Export
(new Command[] {
GI000081Content.Result.AccountID,
GI000081Content.Result.Address,
GI000081Content.Result.CustomerID,
GI000081Content.Result.AccountName
},
null, //This is the filter - none here, so null..
0,
false,
false
);
}
My request is, can I get an example of C# code for how to do this using the Contract-based web services. I know how to extend the endpoint and get the wsdl file / service reference to my custom Generic Inquiry, but I don't know the syntax to make the actual call.
Thanks in advance...
Just to make sure that you create the entity in the endpoint properly, make sure that the top level entity contain only the Parameters and that it has a sub entity of type details contain all the results. If there is no parameter then it is fine for the top level entity to be empty.
Here is the code sample that I used
class Program
{
static void Main(string[] args)
{
DefaultSoapClient client = new DefaultSoapClient();
client.Login("admin", "admin", null, null, null);
try
{
BatchPaymentsInq batch = new BatchPaymentsInq
{
Result = new BatchPaymentsInqResult[]
{
new BatchPaymentsInqResult { ReturnBehavior = ReturnBehavior.All }
}
};
var result = client.Get(batch);
}
catch(Exception ex)
{
}
finally
{
client.Logout();
}
}
}
Edit:
Here is how I extended my endpoint in order to use it with the Contract Based SOAP API
So the main entity named BatchPaymentsInq is pointing to the Generic Inquiry screen and will not have any field in it as you have mentioned that there is no parameter.
The sub entity Result is an array of BatchPaymentsInqResult an object created for containing the fields in the result grid of the inquiry.
I have an extremely odd error and wondered if anyone knew the reason for this.
When I create a new DataObject and TableController called Content and ContentController respectively, it doesn't register the tablecontroller and the help documentation it automatically generates has lost its styling.
I can't connect to the controller at all but all other controllers work as expected.
If I just rename it to DataController and that's just the name of the controller, not the dataobject everything works perfectly.
Is ContentController a reserved word of some kind or is this just specifically happening on my machine?
public class DataController : TableController<Content>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
MobileContext context = new MobileContext();
DomainManager = new EntityDomainManager<Content>(context, Request, Services);
}
// GET tables/Content
public IQueryable<Content> GetAllContent()
{
return Query();
}
// GET tables/Content/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<Content> GetContent(string id)
{
return Lookup(id);
}
// PATCH tables/Content/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task<Content> PatchContent(string id, Delta<Content> patch)
{
return UpdateAsync(id, patch);
}
// POST tables/Content/48D68C86-6EA6-4C25-AA33-223FC9A27959
public async Task<IHttpActionResult> PostContent(Content item)
{
Content current = await InsertAsync(item);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
// DELETE tables/Content/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task DeleteContent(string id)
{
return DeleteAsync(id);
}
}
An MVC project will create an application directory called Content. This will override your route mapping to the ContentController.
You can get around this if desired through changing RouteMaps and other trickery although probably the simpliest answer is to change the name of the controller...
I'm creating my own IntelliSense Presenter, since Visual Studio2012 support change theme, so I want my background color of the presenter can be auto-changed when the theme been changed. Is there a way to track the theme changes event, or get the current color theme of the Visual Studio?
Yes, this is possible. I had to solve a similiar issue with one of my extensions...
The current theme is stored in the Windows Registry; so I implemented the following utility class.
public enum VsTheme
{
Unknown = 0,
Light,
Dark,
Blue
}
public class ThemeUtil
{
private static readonly IDictionary<string, VsTheme> Themes = new Dictionary<string, VsTheme>()
{
{ "de3dbbcd-f642-433c-8353-8f1df4370aba", VsTheme.Light },
{ "1ded0138-47ce-435e-84ef-9ec1f439b749", VsTheme.Dark },
{ "a4d6a176-b948-4b29-8c66-53c97a1ed7d0", VsTheme.Blue }
};
public static VsTheme GetCurrentTheme()
{
string themeId = GetThemeId();
if (string.IsNullOrWhiteSpace(themeId) == false)
{
VsTheme theme;
if (Themes.TryGetValue(themeId, out theme))
{
return theme;
}
}
return VsTheme.Unknown;
}
public static string GetThemeId()
{
const string CategoryName = "General";
const string ThemePropertyName = "CurrentTheme";
string keyName = string.Format(#"Software\Microsoft\VisualStudio\11.0\{0}", CategoryName);
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(keyName))
{
if (key != null)
{
return (string)key.GetValue(ThemePropertyName, string.Empty);
}
}
return null;
}
}
Okay; this just helps to figur out the current settings... listening for the theme changed notification is a bit trickier. After your package is loaded, you must obtain an IVsShell instance via the DTE; once you have this object you can utilize the AdviceBroadcastMessages method to subscribe for event notifications. You have to provide an object whose type implements the IVsBroadcastMessageEvents interface...
I don´t want to post the whole implementation, but the following lines might illustrate the key scenario...
class VsBroadcastMessageEvents : IVsBroadcastMessageEvent
{
int IVsBroadcastMessageEvent.OnBroadcastMessage(uint msg, IntPtr wParam, IntPtr lParam)
{
const uint WM_SYSCOLORCHANGE = 0x15;
if (msg == WM_SYSCOLORCHANGE)
{
// obtain current theme from the Registry and update any UI...
}
}
}
Consider implementing IDisposable on that type as well, in order to be able to unsubscribe from the event source, when the package gets unloaded.
This is how I subscribe for event notifications...
class ShellService
{
private readonly IVsShell shell;
private bool advised;
public ShellService(IVsShell shellInstance)
{
this.shell = shellInstance;
}
public void AdviseBroadcastMessages(IVsBroadcastMessageEvents broadcastMessageEvents, out uint cookie)
{
cookie = 0;
try
{
int r = this.shell.AdviseBroadcastMessages(broadcastMessageEvents, out cookie);
this.advised = (r == VSConstants.S_OK);
}
catch (COMException) { }
catch (InvalidComObjectException) { }
}
public void UnadviseBroadcastMessages(uint cookie)
{
...
}
}
Keep the value of the cookie parameter; you´ll need it to successfully unsubscribe.
Hope that helps (-:
Just wanted to put an update just in case anyone else comes along.. #Matze and #Frank are totally right.. However in VS 2015.. they added a easy way to detect the theme change. So you need to include PlatformUI an dyou get a super easy event
using Microsoft.VisualStudio.PlatformUI;
....
//Then you get an event
VSColorTheme.ThemeChanged += VSColorTheme_ThemeChanged;
You should make sure your control is disposable so you can unsubscribe from the event...
BONUS!
It also give you easy access to the colors.. even if the user has changed them from the default .. so you can do stuff like this in when set your colors
var defaultBackground = VSColorTheme.GetThemedColor(EnvironmentColors.ToolWindowBackgroundColorKey);
var defaultForeground = VSColorTheme.GetThemedColor(EnvironmentColors.ToolWindowTextColorKey);
For VS 2015 this has changed, the solution #Matze has still works but you need to update the GetThemeId() function to check for the version and if it's 14.0 (VS2015) look in a different place in the registry. The way the value is stored has changed also, it's still a string but now contains other values seperated by a '*'. The theme guid is the last value in the list.
if (version == "14.0")
{
string keyName = string.Format(#"Software\Microsoft\VisualStudio\{0}\ApplicationPrivateSettings\Microsoft\VisualStudio", version);
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(keyName))
{
if (key != null)
{
var keyText = (string)key.GetValue("ColorTheme", string.Empty);
if (!string.IsNullOrEmpty(keyText))
{
var keyTextValues = keyText.Split('*');
if (keyTextValues.Length > 2)
{
return keyTextValues[2];
}
}
}
}
return null;
}
We're making an ASP.Net MVC app that needs to be able to generate a PDF and display it to the screen or save it somewhere easy for the user to access. We're using PdfSharp to generate the document. Once it's finished, how do we let the user save the document or open it up in a reader? I'm especially confused because the PDF is generated server-side but we want it to show up client-side.
Here is the MVC controller to create the report that we have written so far:
public class ReportController : ApiController
{
private static readonly string filename = "report.pdf";
[HttpGet]
public void GenerateReport()
{
ReportPdfInput input = new ReportPdfInput()
{
//Empty for now
};
var manager = new ReportPdfManagerFactory().GetReportPdfManager();
var documentRenderer = manager.GenerateReport(input);
documentRenderer.PdfDocument.Save(filename); //Returns a PdfDocumentRenderer
Process.Start(filename);
}
}
When this runs, I get an UnauthorizedAccessException at documentRenderer.PdfDocument.Save(filename); that says, Access to the path 'C:\Program Files (x86)\Common Files\Microsoft Shared\DevServer\10.0\report.pdf' is denied. I'm also not sure what will happen when the line Process.Start(filename); is executed.
This is the code in manager.GenerateReport(input):
public class ReportPdfManager : IReportPdfManager
{
public PdfDocumentRenderer GenerateReport(ReportPdfInput input)
{
var document = CreateDocument(input);
var renderer = new PdfDocumentRenderer(true, PdfSharp.Pdf.PdfFontEmbedding.Always);
renderer.Document = document;
renderer.RenderDocument();
return renderer;
}
private Document CreateDocument(ReportPdfInput input)
{
//Put content into the document
}
}
Using Yarx's suggestion and PDFsharp Team's tutorial, this is the code we ended up with:
Controller:
[HttpGet]
public ActionResult GenerateReport(ReportPdfInput input)
{
using (MemoryStream stream = new MemoryStream())
{
var manager = new ReportPdfManagerFactory().GetReportPdfManager();
var document = manager.GenerateReport(input);
document.Save(stream, false);
return File(stream.ToArray(), "application/pdf");
}
}
ReportPdfManager:
public PdfDocument GenerateReport(ReportPdfInput input)
{
var document = CreateDocument(input);
var renderer = new PdfDocumentRenderer(true,
PdfSharp.Pdf.PdfFontEmbedding.Always);
renderer.Document = document;
renderer.RenderDocument();
return renderer.PdfDocument;
}
private Document CreateDocument(ReportPdfInput input)
{
//Creates a Document and puts content into it
}
I'm not familar with PDF sharp but for MVC is mostly done via built in functionality. You need to get your pdf document represented as an array of bytes. Then you'd simply use MVC's File method to return it to the browser and let it handle the download. Are there any methods on their class to do that?
public class PdfDocumentController : Controller
{
public ActionResult GenerateReport(ReportPdfInput input)
{
//Get document as byte[]
byte[] documentData;
return File(documentData, "application/pdf");
}
}