I'm experimenting with the new WinRT Appointments API in Windows 8.1, based on a sample provided on the MSDN website of Microsoft: http://code.msdn.microsoft.com/windowsapps/Appointments-API-sample-2b55c76e
It works great and I can add appointments without a hassle, but there's always a confirmation by the user involved when using the method ShowAddAppointmentAsync from the Windows.ApplicationModel.Appointments.AppointmentManager namespace, which shows the Appointments provider Add Appointment UI.
I'm looking for a solution to add a larger collection of appointments in the default Windows 8 calendar, WITHOUT the confirmation for each individual appointment in the collection. Is there a way to get around this and bulk insert appointments? Maybe the Windows Live SDK?
Its true that the API prompts the user before saving, but there is a provision to achieve this.
var appointment = new Windows.ApplicationModel.Appointments.Appointment();
appointment.details = "This is a dummy appointment";
appointment.reminder = 15000;
appointment.subject = "TEST APPPOINTMENT";
var x = new Windows.ApplicationModel.Appointments.AppointmentManager.requestStoreAsync(Windows.ApplicationModel.Appointments.AppointmentStoreAccessType.appCalendarsReadWrite).done(function (apppointmentStore) {
apppointmentStore.createAppointmentCalendarAsync("TEST CALENDAR").done(function (calendar) {
calendar.saveAppointmentAsync(appointment);
});
})
Here you're an example to do it using C#
private AppointmentCalendar currentAppCalendar;
private AsyncLazy<AppointmentStore> lazyAppointmentStore = new AsyncLazy<AppointmentStore>(async () =>
{
var appStore = await AppointmentManager.RequestStoreAsync(AppointmentStoreAccessType.AppCalendarsReadWrite);
return appStore;
});
private AppointmentStore AppStore { get { return lazyAppointmentStore.Value.Result; } }
public AppointmentService()
{
}
public async Task CreateCalendar()
{
IReadOnlyList<AppointmentCalendar> appCalendars =
await AppStore.FindAppointmentCalendarsAsync(FindAppointmentCalendarsOptions.IncludeHidden);
AppointmentCalendar appCalendar = null;
// Apps can create multiple calendars. Here app creates only one.
if (appCalendars.Count == 0)
{
appCalendar = await AppStore.CreateAppointmentCalendarAsync(Constants.CalendarName);
}
else
{
appCalendar = appCalendars[0];
}
appCalendar.OtherAppReadAccess = AppointmentCalendarOtherAppReadAccess.Full;
appCalendar.OtherAppWriteAccess = AppointmentCalendarOtherAppWriteAccess.SystemOnly;
// This app will show the details for the appointment. Use System to let the system show the details.
appCalendar.SummaryCardView = AppointmentSummaryCardView.App;
await appCalendar.SaveAsync();
currentAppCalendar = appCalendar;
}
public async Task<bool> CreateNewAppointment(Data.Schemas.Task task)
{
if (null == task)
throw new ArgumentNullException("task");
Appointment newAppointment = new Appointment();
this.SaveAppointmentData(task, newAppointment);
try
{
// Show system calendar to the user to be edited
string appointmentId = await AppointmentManager.ShowAddAppointmentAsync(newAppointment, Windows.Foundation.Rect.Empty);
return ! string.IsNullOrWhiteSpace(appointmentId);
// Just save the appointment
// await currentAppCalendar.SaveAppointmentAsync(newAppointment);
// return true;
}
catch
{
return false;
}
}
Check my post, to know more about AsyncLazy.
I hope this help you.
Regards.
Juanlu
This is not possible, by using the WinRT appointments API.
A user interaction is always required. It was a design decision by MS that some actions require user interaction and this is one of it.
As stated by #Ken Tucker, you can use the windows live api to create appointments but this requires the user of your app to sing in to windows live and grat it the required permissions.
Related
I've tried various techniques but can't work out why this code always results in a valid(active?) license, even if the user hasn't bought the app add-on.
The function initialises when the app starts.
public async void GetLicenseInfo()
{
if (context == null)
{
context = StoreContext.GetDefault();
// If your app is a desktop app that uses the Desktop Bridge, you
// may need additional code to configure the StoreContext object.
// For more info, see https://aka.ms/storecontext-for-desktop.
}
workingProgressRing.IsActive = true;
StoreAppLicense appLicense = await context.GetAppLicenseAsync();
workingProgressRing.IsActive = false;
if (appLicense == null)
{
messagetextblock.Text = "An error occurred while retrieving the license.";
return;
}
// Use members of the appLicense object to access license info...
// the customer can' t access this feature
messagetextblock.Text = "customer hasn't bought the addon";
// Access the valid licenses for durable add-ons for this app.
foreach (KeyValuePair<string, StoreLicense> item in appLicense.AddOnLicenses)
{
StoreLicense addOnLicense = item.Value;
// Use members of the addOnLicense object to access license info
// for the add-on.
// Specify the kinds of add-ons to retrieve.
string[] productKinds = { "Durable", "Consumable", "UnmanagedConsumable" };
List<String> filterList = new List<string>(productKinds);
StoreProductQueryResult queryResult = await context.GetAssociatedStoreProductsAsync(filterList);
if (addOnLicense.IsActive)
{
// the customer can access this feature
messagetextblock.Text = "customer has bought the addon";
}
}
}
It's hard to tell exactly what could be wrong with your above example. However, I have an app in the market that uses two different types of in app purchases. One to remove ads and one that gets a premium license.
The difference between how you're doing it and I'm doing it is I'm looking for subscription store id in my license skues that are returned with the license.
have you tried this method yet?
public static void CheckForPremiumStatus()
{
#if DEBUG
if (LicenseInformation.ProductLicenses["PremiumStatus"].IsActive)
{
IsPremium = RemoveAds = true;
}
#else
var subscriptionStoreId = "9PMT47KC5W6C";
foreach (var addOnLicense in _appLicense.AddOnLicenses)
{
StoreLicense license = addOnLicense.Value;
if (license.SkuStoreId.StartsWith(subscriptionStoreId))
{
if (license.IsActive)
{
IsPremium = RemoveAds = true;
return;
}
else
{
break;
}
}
}
IsPremium = RemoveAds = false;
#endif
}
You can see the full example here - feel free to copy and paste it if you want. Just swap out the addon store id's
I'm trying to implement Keychain from start to finish for my Xamarin Application. At the moment I have the initial bit that will display the native prompt `Touch ID to Use Password'. That works successfully using the bit of code below.
public class TouchId : ITouchID
{
public Task<bool> AuthenticateUserIDWithTouchID()
{
var taskSource = new TaskCompletionSource<bool>();
var context = new LAContext();
if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out NSError AuthError))
{
var replyHandler = new LAContextReplyHandler((success, error) => {
taskSource.SetResult(success);
});
context.EvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, "Logging with touch ID", replyHandler);
};
return taskSource.Task;
}
}
My question is how do I display the native prompt asking if I would like to save to Keychain.
I've created a basic one using xamarin's Display Alert, but it doesn't quite look as nice as the native.
I am trying to observe if a screenshot is taken while using my App on Iphone. If a screenshot is taken while using my App, I would like that screenshot to be deleted.
I also understand that during deletion, user needs to give permission for deletion.
I used an Observer method successfully to check if a screenshot is taken while using my app.
I am stuck at a point where I need to access that screenshot and delete it, of course with user permission.
```public override void OnActivated(UIApplication application)
{
try
{
// Start observing screenshot notification
if (_screenshotNotification == null)
{
_screenshotNotification = NSNotificationCenter.DefaultCenter.AddObserver(UIApplication.UserDidTakeScreenshotNotification,
(NSNotification n) => {
Console.WriteLine("UserTookScreenshot");
var photosOptions = new PHFetchOptions();
photosOptions.SortDescriptors = new NSSortDescriptor[] { new
NSSortDescriptor("creationDate", false) };
photosOptions.FetchLimit = 1;
var photo = PHAsset.FetchAssets(photosOptions);
Console.WriteLine(photo);
var filePath = photo.Path;
System.IO.File.Delete(filePath);
n.Dispose();
}
);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
}```
I know the above code does not work with deleting the current screenshot taken while using my App. It gives a general idea on what I want to achieve.
How can I delete the screenshot taken while using my APP from Iphone instantly (with user permission)? I would also like to know if it is possible.
Yes it is possible, you obviously have to properly manage the permissions for the image. In order to do this, first you have to add an observer to detect the screenshot as shown here
First: declare an NSObject variable on your AppDelegate. In the example below I added _screenshotNotification.
Second: On the AppDelegate’s OnActivated (app moving to active state), add code to start observing the UIApplication.UserDidTakeScreenshotNotification and then do something once the notification is posted.
Third: On the AppDelegate’s OnResignActivation (app moving to inactive state), add code to remove the observer.
Then you have to actually find the file & delete, so you in the page you are trying to do it, just add the Foundation & Photos using statements and then try this. (I converted the Swift from here but haven't tested it)
var fetchOptions = new PHFetchOptions();
fetchOptions.SortDescriptors[0] = new Foundation.NSSortDescriptor("creationDate", true);
var fetchResult = PHAsset.FetchAssets(PHAssetMediaType.Image, fetchOptions);
if (fetchResult != null)
{
var lastAsset = (fetchResult.LastObject as PHAsset);
var arrayToDelete = new PHAsset[1] { lastAsset };
PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(() => { PHAssetChangeRequest.DeleteAssets(arrayToDelete); },
async (bool success, NSError errorMessage) => { }); //Handle Completion Here Appropriately
}
I don't think it is possible.
From iOS 11, when you take a screenshot, the snap minimizes itself in the bottom left corner of the screen. From here, save or delete the screenshot is decided by the user.(as the image below)
You can read more information form this article: how-take-screenshot-iphone-or-ipad-ios-11
Here comes the problem how do you know whether user has save the screenshot or not?
If the user saved, you can delete the screenShot form the photoLibrary.
While if the user dose not save the screenshot, what your deleted is not the screenshot.
Try this:
func didTakeScreenshot() {
self.perform(#selector(deleteAppScreenShot), with: nil, afterDelay: 1, inModes: [])
}
#objc func deleteAppScreenShot() {
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors?[0] = Foundation.NSSortDescriptor(key: "creationDate", ascending: true)
let fetchResult = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: fetchOptions)
guard let lastAsset = fetchResult.lastObject else { return }
PHPhotoLibrary.shared().performChanges {
PHAssetChangeRequest.deleteAssets([lastAsset] as NSFastEnumeration)
} completionHandler: { (success, errorMessage) in
if !success, let errorMessage = errorMessage {
print(errorMessage.localizedDescription)
}
}
}
I was searching for the Xamarin implementation of How to open settings programmatically
Vito-ziv answered it for objective C - what is the correct way to do this in C# for iOS in Xamarin Studio?
For current devices this is only possible in ios8 (ios9 not available at time of writing) (It used to be possible before ios5 apparently - see this blog post from Adrian Stevens at Xamarin - shout out to him for the inspiration for this answer)
To do it in ios8, I did it like this:
var settingsString = UIKit.UIApplication.OpenSettingsUrlString;
var url = new NSUrl (settingsString);
UIApplication.SharedApplication.OpenUrl (url);
Where the above code was called from a click event via delegate class in a UIAlertView click.
Since I am supporting ios7 too, to handle ios7 devices I did this, where the HandleLocationAuthorisation method is called when deciding whether to present a view controller - the user on ios8 and above can choose to go to the settings directly, whereas the user on ios7 has to go there manually.
This example below is checking for location services, but with trivial changes could easily be changed to check for other types of settings.
public bool HandleLocationAuthorisation ()
{
if (CLLocationManager.Status == CLAuthorizationStatus.AuthorizedAlways) {
return true;
} else {
UIAlertView uiAlert;
//iOS 8 and above can redirect to settings from within the app
if (UIDevice.CurrentDevice.CheckSystemVersion(8,0)) {
uiAlert = new UIAlertView
("Location Services Required",
"",
null,
"Return To App","Open Settings");
uiAlert.Delegate = new OpenSettingsFromUiAlertViewDelegate();
uiAlert.Message = "Authorisation to use your location is required to use this feature of the app.";
//ios7 and below has to go there manually
} else {
uiAlert = new UIAlertView
("Location Services Required",
"Authorisation to use your location is required to use this feature of the app. To use this feature please go to the settings app and enable location services",
null,
"Ok");
}
uiAlert.Show ();
return false;
}
}
For completeness, here is the code for the event delgate referenced above:
public class OpenSettingsFromUiAlertViewDelegate : UIAlertViewDelegate {
public override void Clicked (UIAlertView alertview, nint buttonIndex)
{
if (buttonIndex == 1) {
var settingsString = UIKit.UIApplication.OpenSettingsUrlString;
var url = new NSUrl (settingsString);
UIApplication.SharedApplication.OpenUrl (url);
}
}
}
Hope this will help you. This is working in iPhone not sure about working on iPad.
var url = new NSUrl("prefs:root=Settings");
UIApplication.SharedApplication.OpenUrl(url);
I have successfully added single appointment using this code now i want to add multiple appointment pro grammatically in a single loop.for example i want to add 5 appointment at a time using loop where dates for every appointment is available in a List.
Thanks in advance :)
SaveAppointmentTask saveAppointmentTask = new SaveAppointmentTask();
saveAppointmentTask.StartTime = nearestDate;
saveAppointmentTask.EndTime = nearestDate.AddMinutes(3) ;
saveAppointmentTask.Subject = "Meet Ali"; // appointment subject
saveAppointmentTask.Location = "In Office"; // appointment location
saveAppointmentTask.Details = "Meet Ali to discuss product launch";//appointment details
saveAppointmentTask.IsAllDayEvent = false;
saveAppointmentTask.Reminder = Microsoft.Phone.Tasks.Reminder.FifteenMinutes;
saveAppointmentTask.AppointmentStatus = Microsoft.Phone.UserData.AppointmentStatus.OutOfOffice;
saveAppointmentTask.Show();
use this code as Navigated To event is all the time called when you land up in the page
private SaveAppointmentTask saveAppointmentTask;
private List<int> listMinutes = new List<int>();
// Constructor
public MainPage()
{
InitializeComponent();
for (int i = 0; i < 10; i++) {
listMinutes.Add(i);
}
}
int countAdded = 0;
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
if (countAdded < 10)
{
saveAppointmentTask = new SaveAppointmentTask();
saveAppointmentTask.StartTime = DateTime.Now.AddMinutes(listMinutes[countAdded]);
saveAppointmentTask.EndTime = saveAppointmentTask.StartTime.Value.AddMinutes(2);
saveAppointmentTask.Subject = "Meet Ali"; // appointment subject
saveAppointmentTask.Location = "In Office"; // appointment location
saveAppointmentTask.Details = "Meet Ali to discuss product launch";//appointment details
saveAppointmentTask.IsAllDayEvent = false;
saveAppointmentTask.Reminder = Microsoft.Phone.Tasks.Reminder.FifteenMinutes;
saveAppointmentTask.AppointmentStatus = Microsoft.Phone.UserData.AppointmentStatus.OutOfOffice;
countAdded++;
saveAppointmentTask.Show();
}
else {
// do not add anything
}
}
save i that is the count in some application state or a tokes so that you can know if you have added the events :) Appplication.Current.Resources.Add("token", "number added")
Thanks
You cannot automatically save all appointments. You would have to launch the task in each iteration, and user interaction would be nedded in each.
A possible option is to use the Live Connect API. That would allow you to programatically create apointments in user's Live calendar:
Interacting with calendars (Live Connect API)
This way you don't interact with the phone calendar, but the users's Live calendar, which can be synchronized in the phone. Of course, the user will need to authenticate.