简体   繁体   English

如何让我的Android应用使用“服务帐户”访问共享的Google日历?

[英]How can I make my Android app use a “Service Account” to access a shared Google Calendar?

I think the answer is YES but I'm not sure how to do it. 我认为答案是肯定的,但我不知道该怎么做。 I've been using the API to get events from calendars stored on the device. 我一直在使用API​​从存储在设备上的日历中获取事件。 eg 例如

 public static ArrayList<MINCalendarEvent> queryEvents(long startMillis, long endMillis) throws  MINPermissionException
 {
    if ( ContextCompat.checkSelfPermission(MINMainActivity.getSharedInstance(), Manifest.permission.WRITE_CALENDAR) != PackageManager.PERMISSION_GRANTED )
    {
        throw new MINPermissionException(NO_PERMISSION);
    }
    else
    {
        ArrayList<MINCalendarEvent> eventArray = new ArrayList<MINCalendarEvent>();

        Cursor cur = CalendarContract.Instances.query(MINMainActivity.getSharedInstance().getContentResolver(), EVENT_PROJECTION, startMillis, endMillis);
        int numItems = cur.getCount();
        Log.d("MINCalendarUtils.queryEvents", "Number of events retrieved: " + numItems);
        while (cur.moveToNext())
        {
            MINCalendarEvent event = new MINCalendarEvent();
            event.calendarID = cur.getLong(EVENT_PROJECTION_CALENDAR_ID);
            event.organizer = cur.getString(EVENT_PROJECTION_ORGANIZER);
            event.title = cur.getString(EVENT_PROJECTION_TITLE);
            event.eventLocation = cur.getString(EVENT_PROJECTION_EVENT_LOCATION);
            event.description = cur.getString(EVENT_PROJECTION_DESCRIPTION);
            event.dtStart = cur.getLong(EVENT_PROJECTION_DTSTART);
            event.dtEnd = cur.getLong(EVENT_PROJECTION_DTEND);
            event.eventTimeZone = cur.getString(EVENT_PROJECTION_EVENT_TIMEZONE);
            event.eventEndTimeZone = cur.getString(EVENT_PROJECTION_EVENT_END_TIMEZONE);
            event.duration = cur.getString(EVENT_PROJECTION_DURATION);
            event.allDay = (cur.getInt(EVENT_PROJECTION_ALL_DAY) != 0);
            event.rRule = cur.getString(EVENT_PROJECTION_RRULE);
            event.rDate = cur.getString(EVENT_PROJECTION_RDATE);
            event.availability = cur.getInt(EVENT_PROJECTION_AVAILABILITY);
            event.guestsCanModify = (cur.getInt(EVENT_PROJECTION_GUESTS_CAN_MODIFY) != 0);
            event.guestsCanInviteOthers = (cur.getInt(EVENT_PROJECTION_GUESTS_CAN_INVITE_OTHERS) != 0);
            event.guestsCanSeeGuests = (cur.getInt(EVENT_PROJECTION_GUESTS_CAN_SEE_GUESTS) != 0);
            eventArray.add(event);
        }
        return eventArray;
    }
}

This works great. 这非常有效。 The problem is that I need to query a shared calendar stored on the server which the user DOES NOT have rights too. 问题是我需要查询存储在服务器上的共享日历,用户也没有权限。 My app creates a LOCAL calendar. 我的应用创建了一个LOCAL日历。 I need to access a shared that the user WILL NOT have rights too and synchronize the events stored in the shared calendar with the LOCAL calendar (non-synchronized). 我需要访问用户也没有权限的共享,并将存储在共享日历中的事件与LOCAL日历(非同步)同步。 I'm assuming that I can use a service account to access the shared calendar. 我假设我可以使用服务帐户访问共享日历。

I've successfully created a service account and added the account to the shared calendar. 我已成功创建了一个服务帐户,并将该帐户添加到共享日历中。 Now what???? 怎么办????

There seems to be several ways of accessing calendar events using a service account but I'm totally confused. 似乎有几种使用服务帐户访问日历事件的方法,但我完全感到困惑。 Obviously, I'd like to use the API I've been using but I think it only works with calendars that are synchronized with the device. 显然,我想使用我一直在使用的API,但我认为它只适用于与设备同步的日历。

I've investigated using "GoogleCredentials" but I need some sample source code to make this work. 我调查过使用“GoogleCredentials”,但我需要一些示例源代码来完成这项工作。 1st of all, when I created the service account, I exported it using JSON and not p12. 首先,当我创建服务帐户时,我使用JSON而不是p12导出它。 I don't see how to use it. 我不知道如何使用它。 The API seems to require p12. API似乎需要p12。 I'm also totally confused on how to use the credentials once I have them. 一旦拥有凭证,我也完全不知道如何使用凭证。 Here's the sample source I started with: 这是我开始的示例源:

    //String emailAddress = "123456789000-abc123def456@developer.gserviceaccount.com";
    GoogleCredential credential = null;
    JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
    HttpTransport httpTransport = null;
    try
    {
        httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        credential = new GoogleCredential.Builder()
                .setTransport(httpTransport)
                .setJsonFactory(JSON_FACTORY)
                .setServiceAccountId(emailAddress)
                .setServiceAccountPrivateKeyFromP12File(new File("MyProject.p12"))
                .setServiceAccountScopes(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN))
                .build();
    }
    catch (GeneralSecurityException e)
    {
        e.printStackTrace();
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }

There are several problems. 有几个问题。 1) I can't event get this to compile. 1)我不能事件得到这个编译。 I can't seem to get the imports to work: 我似乎无法让导入工作:

 import com.google.api.services.sqladmin.SQLAdminScopes;

Assuming I can get past that, now what. 假设我可以通过它,现在是什么。 I'm not sure how to use the credentials to access the remote calendar. 我不确定如何使用凭据访问远程日历。 What I need is to get a list of events from the shared calendar. 我需要的是从共享日历中获取事件列表。

I've also been looking through the source code at the following link for directions, but it doesn't use a service account: https://developers.google.com/google-apps/calendar/quickstart/android 我也一直在查看以下链接的源代码以获取路线,但它不使用服务帐户: https//developers.google.com/google-apps/calendar/quickstart/android

Also, is there a way to hook changes to the server based shared calendar so that I get pinged when changes occur to the shared calendar? 此外,是否有一种方法可以将更改挂钩到基于服务器的共享日历,以便在共享日历发生更改时进行ping操作?

Any help???? 任何帮助????

EDIT - Working Code 编辑 - 工作代码

Based on the answer from Andres, I've put together the following code: 根据Andres的回答,我把以下代码放在一起:

public static void calendarAuthenticate()
{
    Thread thread = new Thread(new Runnable()
    {
        @Override
        public void run()
        {
            try
            {
                MINAppConfiguration appConfig = MINAppConfiguration.getSharedInstance();

                // Application Name
                appName = appConfig.getCurrentAppInfo().appName;

                // Directory to store user credentials for this application;
                //dataStoreDir = new File(appConfig.appDirectoryOnDevice);

                // Instance of the JSON factory
                jsonFactory = JacksonFactory.getDefaultInstance();

                // Instance of the scopes required
                scopes = new ArrayList<String>();
                scopes.add(CalendarScopes.CALENDAR_READONLY);

                // Http transport creation
                httpTransport = AndroidHttp.newCompatibleTransport();

                java.io.File licenseFile = getSecretFile();
                GoogleCredential credential = new GoogleCredential.Builder()
                        .setTransport(httpTransport)
                        .setJsonFactory(jsonFactory)
                        .setServiceAccountId("account-1@handy-contact-762.iam.gserviceaccount.com")
                        .setServiceAccountScopes(scopes)
                        .setServiceAccountPrivateKeyFromP12File(licenseFile)
                        .build();
                com.google.api.services.calendar.Calendar.Builder builder = new com.google.api.services.calendar.Calendar.Builder(httpTransport, jsonFactory, credential);
                builder.setApplicationName(appName);
                com.google.api.services.calendar.Calendar client = builder.build();

                // List the next 10 events from the target calendar.
                DateTime now = new DateTime(System.currentTimeMillis());
                com.google.api.services.calendar.Calendar.Events.List list = client.events().list("mobilityinitiative.com_qfvbdrk368f9la06hacb4bduos@group.calendar.google.com");
                list.setMaxAttendees(10);
                list.setTimeMin(now);
                list.setOrderBy("startTime");
                list.setSingleEvents(true);
                Events events = list.execute();

                List<Event> items = events.getItems();
                if (items.size() == 0)
                {
                    Log.d(TAG, "No upcoming events found.");
                }
                else
                {
                    Log.d(TAG, "Upcoming events");
                    for (Event event : items) {
                        DateTime start = event.getStart().getDateTime();
                        if (start == null) {
                            start = event.getStart().getDate();
                        }
                        Log.d(TAG, event.getSummary() + " (" + start + ")\n");
                    }
                }
            }
            catch (GeneralSecurityException e)
            {
                e.printStackTrace();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    });
    thread.start();
}

public static java.io.File getSecretFile()
{
    File f = new File(MINMainActivity.getSharedInstance().getCacheDir()+ "/" +"google_calendar_secret.p12");
    if (f.exists())
    {
        f.delete();
    }
    try
    {
        InputStream is = MINMainActivity.getSharedInstance().getAssets().open("google_calendar_secret.p12");
        int size = is.available();
        byte[] buffer = new byte[size];
        is.read(buffer);
        is.close();


        FileOutputStream fos = new FileOutputStream(f);
        fos.write(buffer);
        fos.close();
    }
    catch (Exception e)
    {
        throw new RuntimeException(e);
    }
    return f;
}

A couple of notes: 几个笔记:

I'm not an Android expert, but either of the following approach should work for your use case. 我不是Android专家,但以下任何一种方法都适用于您的用例。

  1. Your approach: Using Service Account, will avoid having the user authenticating to your application. 您的方法:使用服务帐户,将避免让用户对您的应用程序进行身份验证。 You may want to read about ' Delegating domain-wide authority ', this will allow your application to make API calls as the user in your domain (also known as 'impersonate' users). 您可能希望了解“ 委派域范围权限 ”,这将允许您的应用程序以域中的用户(也称为“模拟”用户)进行API调用。 I also found this this SO to be helpful. 我也发现了这本SO是有帮助的。

  2. Another approach: Using the Acl.Insert resource. 另一种方法:使用Acl.Insert资源。 This will require user authentication every time they log-in to your application. 这将需要每次登录到您的应用程序时进行用户身份验证。 Here is an example on how to implement this. 这是一个如何实现它的示例

From your sample source above, set your scopes to Calendar scope instead of SQL Admin, looks something like this: 从上面的示例源中,将范围设置为Calendar范围而不是SQL Admin,如下所示:

GoogleCredential credential = new GoogleCredential.Builder()
    ...    
      .setServiceAccountScopes(CalendarScopes.CALENDAR)
      .setServiceAccountPrivateKeyFromP12File(xxxx)  
      .build();

Hope this helps and good luck! 希望这有帮助,祝你好运!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何将Android上的Google帐户链接到我的应用程序? - How can I link the Google account on the android to my app? 使用Google Calendar API在Android中访问特定帐户的日历 - Use Google Calendar API to Access Specific Account's Calendar in Android 我的android应用的Google云端硬盘服务帐户 - Google Drive service account for my android app 如何在我的Android应用程序中撤消Google帐户访问权限? - How to revoke google account access within my android app? 如何在我的Android应用程序中使用Google Calendar API? - How to use Google Calendar API with my Android App? 如何使我的Android应用出现在Google搜索中 - How can I make my android app appears in Google search 如何强制Android应用程序仅使用某个帐户来使用Google Drive API(GDAA)? - How can I force an android app to use only a certain account to use google drive api (GDAA)? 如何使用Google日历用于我的Android应用程序的日期选择器? - How can I used the date picker that Google Calendar uses for my android app? 如何在我的 Android 应用程序中使用 Lotus note 日历视图 - How can i use lotus note calendar view in my android app 如何在我的 Android 应用程序中使用用户自己的短信服务发送 OTP 并进行手机号码验证? - How can I use a user's own SMS service to send OTP and make mobile number verification in my Android app?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM