javaandroidgoogle-drive-api

Connecting to google Drive API v3 with a service account in Android java app


I'm developing an Android app for internal use in the company I work for. I need to save a text file on a shared drive, on Google Drive. I've done this several times before using a service account in the web appications I developed, but I can't seem to set the connection up correctly in an Android application environment.

I've tried the example provided by google https://developers.google.com/drive/api/v3/quickstart/java but it seems that this isn't compatible with Android, because it tries to use a library only available in desktop environment.

Here is the code of what I implemented:

// Implements OnClick for sendData button
public void onClickSendData(View view) {
    try {
        // Build a new authorized API client service.
        final NetHttpTransport HTTP_TRANSPORT = new com.google.api.client.http.javanet.NetHttpTransport();
        Drive service = new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))
                .setApplicationName(APPLICATION_NAME)
                .build();

        // Upload file to google drive
        String timeStamp = new SimpleDateFormat("yyyyMMddHHmmss", Locale.forLanguageTag("hu-HU")).format(new java.util.Date());
        File fileMetadata = new File();
        fileMetadata.setName("scan_" + timeStamp + ".txt");
        fileMetadata.setParents(new ArrayList<String>(Arrays.asList(TARGET_FOLDER_ID)));
        java.io.File filePath = new java.io.File(getFilesDir() + java.io.File.separator + BARCODE_CONTAINER_TEMP_FILE);
        FileContent mediaContent = new FileContent("text/plain", filePath);
        File file = service.files().create(fileMetadata, mediaContent)
                .setFields("id")
                .execute();

        barcodeContainer.setText(R.string.tarhely_ures);
        barcodContainerEmptyFlag = true;

    }
    catch(Exception ex) {
        // Handle any exception
        ex.printStackTrace();

    }

}

private Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT) throws IOException {
    // Load client secrets.
    InputStream in = MainActivity.class.getResourceAsStream(CREDENTIALS_FILE_PATH);
    if (in == null)
        throw new FileNotFoundException("Resource not found: " + CREDENTIALS_FILE_PATH);

    GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));

    java.io.File tokenFile = new java.io.File(getFilesDir() + java.io.File.separator + TOKENS_DIRECTORY_PATH);
    if(!tokenFile.isDirectory())
        tokenFile.mkdirs();

    // Build flow and trigger user authorization request.
    GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
            HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
            .setDataStoreFactory(new FileDataStoreFactory(tokenFile))
            .setAccessType("offline")
            .build();
    LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build();

    return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");

}

And the runtime error is the following: java.lang.ClassNotFoundException: Didn't find class “java.awt.Desktop” in Android


Solution

  • I finally figured out the answer to my problem, here is my solution using the GoogleCredential.Builder() documented here:

        //  Upload file to google drive
        public void uploadBarcodeFileToDrive() throws IOException, GeneralSecurityException {
            // Get service account secret
            InputStream inputStream = MainActivity.class.getResourceAsStream(CREDENTIALS_FILE_PATH);
            if (inputStream == null)
                throw new FileNotFoundException("Resource not found: " + CREDENTIALS_FILE_PATH);
    
            // Convert inputStream to file
            java.io.File clientSecret = new java.io.File(getFilesDir() + java.io.File.separator + "credentials.p12");
            OutputStream outputStream = new FileOutputStream(clientSecret);
            IOUtils.copy(inputStream, outputStream);
            if (!clientSecret.exists())
                throw new FileNotFoundException("Credentials (credentials.p12) not created from: " + CREDENTIALS_FILE_PATH);
    
            // Http transport creation
            HttpTransport httpTransport = AndroidHttp.newCompatibleTransport();
            // Instance of the JSON factory
            JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
            // Instance of the scopes required
            List<String> scopes = new ArrayList<>();
            scopes.add(DriveScopes.DRIVE);
            // Build Google credential
            GoogleCredential credential = new GoogleCredential.Builder()
                    .setTransport(httpTransport)
                    .setJsonFactory(jsonFactory)
                    .setServiceAccountId(SERVICE_ACCOUNT_PROVIDER)
                    .setServiceAccountScopes(scopes)
                    .setServiceAccountPrivateKeyFromP12File(clientSecret)
                    .setServiceAccountUser(SERVICE_ACCOUNT_USER)
                    .build();
            // Build Drive service
            Drive service = new Drive.Builder(httpTransport, jsonFactory, credential)
                    .setApplicationName(APPLICATION_NAME)
                    .build();
    
            // Parents
            List<String> parents = new ArrayList<>();
            parents.add(TARGET_FOLDER_ID);
            // Setup file
            String timeStamp = new SimpleDateFormat("yyyyMMddHHmmss", Locale.forLanguageTag("hu-HU")).format(new java.util.Date());
            File fileMetadata = new File();
            fileMetadata.setName("scan_" + timeStamp + ".txt");
            fileMetadata.setParents(parents);
            java.io.File filePath = new java.io.File(getFilesDir() + java.io.File.separator + BARCODE_CONTAINER_TEMP_FILE);
            FileContent mediaContent = new FileContent("text/plain", filePath);
            // Upload file to google drive
            service.files().create(fileMetadata, mediaContent)
                    .setFields("id")
                    .execute();
    
        }
    

    The key differences in the example described in the question, and the current solution is the following: