Google service object for Google Calendar API - google-api

I am trying to use the Google Calendar API in .NET, specifically I am trying to get a list of events. According to the examples here, in different programming languages I need to create a 'service' object and an 'event' object. However, I can't find a clear explanation of what either of these objects is or how to initiate them. Does anyone have an explanation? Or can anyone provide any information or give me a link to where this is explained? It doesn't necessarily have to be in .NET
Here is the example in Java:
String pageToken = null;
do {
events = service.events().list('primary').setPageToken(pageToken).execute();
List<Event> items = events.getItems();
for (Event event : items) {
System.out.println(event.getSummary());
}
pageToken = events.getNextPageToken();
} while (pageToken != null);
Following the advice answered, I am getting the following error:
Could not load file or assembly 'Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.16.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.
Here is the code, the error occurs on the credentials = Await... line
Dim credential As UserCredential
Dim clientSecretsPath As String = Server.MapPath("~/App_Data/client_secret.json")
Dim scopes As IList(Of String) = New List(Of String)()
scopes.Add(CalendarService.Scope.Calendar)
Using stream = New System.IO.FileStream(clientSecretsPath, System.IO.FileMode.Open, System.IO.FileAccess.Read)
credential = Await GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets, scopes, "user", CancellationToken.None)
End Using

The problem with GoogleWebAuthorizationBroker is that it tries to launch a new instance of a web browser to go and get authorization where you have to click the "Grant" button.
Obviously if you're running a MVC project under IIS it's just going to get confused when the code tries to execute a web browser!
My solution:
Download the .net sample projects: https://code.google.com/p/google-api-dotnet-client/source/checkout?repo=samples
Build and run one of the projects relevant to you (Eg Calendar or Drive). Dont forget to include your client_secret.json file downloaded from the cloud console.
Run the project and it will open a new browser on your computer where you will have to click the "Grant" button. Do this once and then your MVC code will work because it will not try to open a web browser to grant the permissions.
I'm not aware of any other way to grant this permission to the SDK but it worked for me just great!
Good luck. This took me a good 5 hours to figure out.

Just had the same issue running VS2013 (using .net45 for my project):
After fetching the CalendarV3 API via NuGet you just have to manually add the reference to:
...packages\Microsoft.Bcl.Async.1.0.165\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll
to the project (because it is not inserted automatically via the NuGet-Script)!
That's it! Maybe #peleyal is correcting the script somewhen in future ;)

Remember that this sample is for Java. My recommendation is to do the following:
Take a look in our VB sample for the Calendar API which is available here
You should take a look also in other sample for C#, let's say Tasks API sample
Start a new project and add a NuGet reference to Google.Apis.Calednar.v3. Remember that it's prerelease version.
Your code should look like the following:
It's based on the 2 samples above, I didn't compile or test it but it should work.
UserCredential credential;
using (var stream = new System.IO.FileStream("client_secrets.json",
System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { CalendarService.Scope.Calendar },
"user", CancellationToken.None);
}
// Create the service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "YOUR APP NAME HERE",
});
var firstCalendar = (await service.CalendarList.List().ExecuteAsync()).Items().FirstOrDefault();
if (firstCalendar != null)
{
// Get all events from the first calendar.
var calEvents = await service.Events.List(firstCalendar.Id).ExecuteAsync();
// DO SOMETHING
var nextPage = calEvents.NextPage;
while (nextPage != null)
{
var listRequest = service.Events.List(firstCalendar.Id);
// Set the page token for getting the next events.
listRequest.PageToken = nextPage;
calEvents = await listRequest.EsecuteAsync();
// DO SOMETHING
nextPage = calEvents.NextPage;
}
}

I had the same error, and it was due to the app trying to launch the accept screen.
I first tried to get the vb.net example from google and ran that, which I did get to work, and change to my secret info, ran and got the accept screen. I then tried my app, and it still did not work.
I noticed that the dll was found here under my project installed from the nuget packages.
...packages\Microsoft.Bcl.Async.1.0.165\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll
but was not in the net45 dir. So I uninstalled the nuget packages (have to if changing the .net version) then changed my .net version for my project to 4.0 instead of 4.5, reinstalled the nuget packages, and then it worked!!

Related

google-drive-api method "drive.files.create" stopped working (V3 - 1.58.0.2859) and return nothing

We have a .Net app using Google Drive api to upload files to a g-drive. And it just stopped working days ago (Nov 29th). But we didn't remember doing anything changes during that time.
During the investigation, we could confirm the service account for calling the Google API are valid, since the same service account is also being used for calling other google APIs, and works fine. We also can confirm it's not a permission issue, since we even set the permission of the gdrive to allow "anyone" who has the link, to have edit permission, but the issue is still there.
Unfortunately, we cannot find any useful log, and the return message of the API call is NULL. No error code or error message returned.The only related info we saw is: on the chart of "Error by API method", it shows "drive.files.create" failed 100%.
One interesting thing is, if we disable the Google Drive API, then enable it again, it will work once, then will stop working again.
private string SaveFileToGoogleDrive(IFormFile file, string claimNumber)
{
try
{
var driveService = GetDriveServiceInstance();
var fileMetadata = new Google.Apis.Drive.v3.Data.File();
var mimeType = file.ContentType;
fileMetadata.Name = CreateFileName(file.FileName, claimNumber);
fileMetadata.MimeType = mimeType;
fileMetadata.Parents = new List { _googleSettings.GoogleDriveFolderId };
FilesResource.CreateMediaUpload request;
using (var stream = new MemoryStream())
{
file.CopyTo(stream);
request = driveService.Files.Create(fileMetadata, stream, mimeType);
request.Fields = "id";
request.Upload();
}
var googleFile = request.ResponseBody; \\The response body is always NULL, after the issue happened. :(
return googleFile.Id;
}
catch(Exception ex)
{
_logger.Error($"Google Drive exception {ex.Message} SACKTRACE: {(ex.StackTrace ?? "")} INNER EXCEPTION: {(ex.InnerException != null ? ex.InnerException.Message + "STACK TRACE:" + ex.InnerException.StackTrace ?? "" : "")}");
return string.Empty;
}
}
We found more details from the progress property in the response object, and saw the error message "The user's Drive storage quota has been exceeded.", but it does not make sense at all, since we are using "Enterprise edition" Google Workspace, which is supposed to have no limit. The service account and the key look good, GCP didn't complain at all. And that's the first thing we checked during troubleshooting.
Do you have any idea on what to do to solve the issue or what too look for when investigating this issue?
We found more details from the progress property in the response object, and saw the error message "The user's Drive storage quota has been exceeded.", but it's not make sense at all, since we are using "Enterprise editions" google workspace, which suppose has no limit. The service account and key look good, GCP didn't complain any thing. And that's the first thing we checked during troubleshooting. Anyway, the fix is: after create a new service account then use the new key of this new service account, the system back to work.

How to save a csv or txt in Xamarin UWP app?

Today I've got a question about saving a .csv or .txt file within a Xamarin app on UWP platform. I am trying to save a file I create in my code call tags.csv. My goal is to have no .csv's saved initially, I create an instance in my code, then save it and create a new .csv file when my code executes. The creation and filling of the .csv occurs in one function which triggers based on a Button instance in my app. Also, ideally I could make it save in a location determined by a file explorer popup.
I have tried two routes so far to make and save a .csv file, the CSVExport package and CSVhelper package. Both I have been able to download and add to my project from NuGet successfully.
I have tried separately a simple implementation of each, basically just taking their Example code to see if it would work in my UWP app. Here is the respective code
// CSVExport methods, two ways to save
var myExport = new CsvExport();
...
File(myExport.ExportToBytes(), "text/csv", "results.csv"); // method 1
myExport.ExportToFile("./results.csv"); // method 2
// CSVhelper method
var records = new List<Foo>
{
new Foo { Id = 1, Name = "one" },
};
using (var writer = new StreamWriter("./tags.csv"))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords(records);
}
Here is the error I am receiving: System.UnauthorizedAccessException: 'Access to the path C:...(filepath)...BLE.Client.UWP\bin\x86\Debug\AppX\tags.csv' is denied.'
Whenever the code reaches my saving of the .csv file, it crashes the app and Visual Studio 2022 gives me this error message. The same exact error occurs whether I am using CSVExport or CSVhelper.
Attempted Solutions:
My attempted solutions are mainly in regards to giving the app the permissions it needs to save. If an alternative like getting a different CSV package is better, I would take that advice too.
One solution I saw on StackOverflow linked to this page. The issue is I cannot load StorageFolder or Windows.Storage in my Xamarin app, it just won't recognize it and won't compile cause it's a missing load action.
Another solution I saw was changing your Capabilities in the Package.appxmanifest file and changing your Package header. I have done so, so mine looks like the following code sample. I need the internetClient and bluetooth and location for the app itself, so I added broadFilesystemaccess and even documents and pictures just to see if that would work too.
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap mp rescap">
...
<Capabilities>
<Capability Name="internetClient" />
<rescap:Capability Name="broadFileSystemAccess" />
<uap:Capability Name="documentsLibrary"/>
<uap:Capability Name="picturesLibrary" />
<DeviceCapability Name="bluetooth" />
<DeviceCapability Name="location"/>
</Capabilities>
Another solution was making sure the UWP app had permissions, which I went into system settings and allowed, so it should have full access now.
I am not sure where to go from here, so any advice about UWP or saving files within Xamarin UWP apps would be appreciated.
Based on your requirement, you could try to use the DependencyService feature of Xamarin.Forms.
DependencyService enables you to invoke native platform functionality from shared code. For your scenario, you could pass the stream of the file to the DependencyService first. then you could call the UWP FileSavePicker using DependencyService in your Forms app and save the stream as a file.
Here are some code snippets about how to implement the interface.
SaveFileAsync
public async Task SaveFileAsync(Stream data)
{
FileSavePicker savePicker = new FileSavePicker();
savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
// Dropdown of file types the user can save the file as
savePicker.FileTypeChoices.Add("Text", new List<string>() { ".txt" });
savePicker.FileTypeChoices.Add("CSV", new List<string>() { ".csv" });
// Default file name if the user does not type one in or select a file to replace
savePicker.SuggestedFileName = "New Document";
StorageFile file = await savePicker.PickSaveFileAsync();
if (file!= null)
{
using (IRandomAccessStream dataStream= data.AsRandomAccessStream())
{
using (var reader = new DataReader(dataStream.GetInputStreamAt(0)))
{
await reader.LoadAsync((uint)dataStream.Size);
var buffer = new byte[(int)dataStream.Size];
reader.ReadBytes(buffer);
await Windows.Storage.FileIO.WriteBytesAsync(file, buffer);
}
}
}
}
ISaveFileServicecs interface
public interface ISaveFileServicecs
{
Task<Stream> SaveFileAsync(Stream stream);
}
Usage
await DependencyService.Get<ISaveFileServicecs>().SaveFileAsync(stream);

Download Progress stopped on Screen lock in iOS

I have developed an Xamarin forms application. Provided download option to download file in our application. I have clicked download file and download progress show in app itself. If lock the iphone while download is in progress and unlock it again download stopped. How can I process download even locked phone?. This occurs only in iOS and works properly in Android.
I have used webclient DownloadFileTaskAsync process to download a file and maintain progress value in it.
WebClient client = new WebClient();
client.DownloadFileTaskAsync(new Uri(DownloadUrl), FileName);
Updated Query:
I have implemented the back-grounding process into my source. I have used "Creating Background-Safe Tasks" concept for my download process. I can download more than one file, so put this process in Task itself previously. Now, I have used BeginBackgroundTask in my download process but download process not carried to UI, even BeginBackgroundTask code doesn't hit while debug the code.
Below function put in native and called this function from forms when click download button.
public async Task DownloadFile(string DownloadUrl, string FileName)
{
var taskID = UIApplication.SharedApplication.BeginBackgroundTask(async() =>
{
WebClient client = new WebClient();
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
client.DownloadFileTaskAsync(new Uri(DownloadUrl), FileName);
});
UIApplication.SharedApplication.EndBackgroundTask(taskID);
}
Also registered the app into Background fetch registration categories and now also progress not carried out while lock screen or minimize the app.
Am I missing any process in background techniques? Could you please help me to resolve this or suggest some simple sample as my scenario?
Please help me on this to resolve it.
Regards,
Cheran
You will need to use NSURLSession instead and create a background session for it. You can read how to do this in the official Xamarin Documentation.
You might get away with using WebClient in a background session, but only for a very short time span by using the pattern described in the "Handle iOS Background Limits" docs:
Task.Factory.StartNew(() =>
{
//expirationHandler only called if background time allowed exceeded
var taskId = UIApplication.SharedApplication.BeginBackgroundTask(() => {
Console.WriteLine("Exhausted time");
UIApplication.SharedApplication.EndBackgroundTask(taskId);
});
while(myFlag == true)
{
Console.WriteLine(UIApplication.SharedApplication.TimeRemaining);
myFlag = SomeCalculationNeedsMoreTime();
}
//Only called if loop terminated due to myFlag and not expiration of time
UIApplication.SharedApplication.EndBackgroundTask(taskId);
});
Where you would replace the while with your WebClient code.
However, as mentioned you really need to use Background Transfers for this to work properly.

Android Xamarin.Auth 1.2.2 How to Update Uploaded Cloud Storage File LastModifiedDate?

With Xamarin.Forms, I am using OneDrive SDK on UWP to access OneDrive and it is working good. I am uploading/downloading small data files and I use the following code to change a file's LastModifiedDate:
Item itemUpdate1 = new Item();
itemUpdate1.FileSystemInfo = new Microsoft.OneDrive.Sdk.FileSystemInfo {LastModifiedDateTime = lastModifiedDateTime };
await oneDriveClient1.Drive.Items[item1.Id].Request().UpdateAsync(itemUpdate1);
On Android, I use Xamarin.Auth to access OneDrive and I cannot figure out how to update a file's LastModifiedDate. I am using the following code to sign in and upload the file:
var auth = new OAuth2Authenticator(clientId: clientId, scope: storageScopes1, authorizeUrl: new System.Uri("https://login.live.com/oauth20_authorize.srf"),
redirectUrl: new System.Uri("https://login.live.com/oauth20_desktop.srf"));
System.Uri dataFileUri = new System.Uri("https://api.onedrive.com/v1.0/drive/special/approot:/" + dataFileName1 + ":/content");
var requestUpload = new OAuth2Request("PUT", dataFileUri, null, account);
I would like to know if OAuth2Request can be used to update the file's LastModifiedDate or if there is another way to do it?
Thanks for your help.
Short answer is no. Xamarin.Auth is only to handle the Authentication between your App and the OneDrive Rest Api.
If you want to modify any of the properties of a file in OneDrive you will need to either use a OneDrive SDK for Android as you do in the UWP project or do these modification using the Rest API directly as you did to upload the file.
UPDATE 1
System.Uri dataFileUri = new System.Uri("https://api.onedrive.com/v1.0/drive/special/approot:/" + dataFileName1 + ":/content");
var requestUpload = new OAuth2Request("PUT", dataFileUri, null, account);
As you did in the code above, if you can get the endpoint for the OneDrive REST API to modify the file's properties (like LastModifiedDate) you could be able do it with OAuth2Request.
Go to the OneDrive Dev Portal and try to get that information from the documentation.

Google Drive SDK 1.8.1 RedirectURL

Is there any way to provide RedirectURL then using GoogleWebAuthorizationBroker?
Here is the sample code in C#:
Task<UserCredential> credential = GoogleWebAuthorizationBroker.AuthorizeAsync(secrets, scopes, GoogleDataStore.User, cancellationToken, dataStore);
Or we have to use different approach?
I have an "installed application" that runs on a user's desktop, not a website. By default, when I create an "installed application" project in the API console, the redirect URI seems to be set to local host by default.
What ends up happening is that after the authentication sequence the user gets redirected to localhost and receives a browser error. I would like to prevent this from happening by providing my own redirect URI: urn:ietf:wg:oauth:2.0:oob:auto
This seems to be possible using Python version of the Google Client API, but I find it difficult to find any reference to this with .NET.
Take a look in the implementation of PromptCodeReceiver, as you can see it contains the redirect uri.
You can implement your own ICodeReceiver with your prefer redirect uri, and call it from a WebBroker which should be similar to GoogleWebAuthorizationBroker.
I think it would be great to understand why can't you just use PrompotCodeReceiver or LocalServerCodeReceiver.
And be aware that we just released a new library last week, so you should update it to 1.9.0.
UPDATE (more details, Nov 25th 2014):
You can create your own ICodeReceiver. You will have to do the following:
* The code was never tested... sorry.
public class MyNewCodeReceiver : ICodeReceiver
{
public string RedirectUri
{
get { return YOU_REDIRECT_URI; }
}
public Task<AuthorizationCodeResponseUrl> ReceiveCodeAsync(
AuthorizationCodeRequestUrl url,
CancellationToken taskCancellationToken)
{
// YOUR CODE HERE FOR RECEIVING CODE FROM THE URL.
// TAKE A LOOK AT THE FOLLOWING:
// PromptCodeReceiver AND LocalServerCodeReceiver
// FOR EXAMPLES.
}
}
PromptCodeReceiver
and LocalServerCodeReceiver.
Then you will have to do the following
(instead of using the GoogleWebAuthorizationBroker.AuthorizeAsync method):
var initializer = new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = secrets,
Scopes = scopes,
DataStore = new FileDataStore("Google.Apis.Auth");
};
await new AuthorizationCodeInstalledApp(
new GoogleAuthorizationCodeFlow(initializer),
new MyNewCodeReceiver())
.AuthorizeAsync(user, taskCancellationToken);
In addition:
I'll be happy to understand further why you need to set a different redirect uri, so we will be able to improve the library accordingly.
When I create an installed application the current PromptCodeReceiver and LocalServerCodeReceiver work for me, so I'm not sure what's the problem with your code.

Resources