简体   繁体   中英

Unable to get Write permission on Android

I'm developing an Android app that should interact with Google Calendar. I followed the instructions on the Google API site and copied the code in this class:

public class GoogleCalendarManager {
private static final String APPLICATION_NAME = "Locker";
private static final JacksonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
private static final String TOKENS_DIRECTORY_PATH = "tokens";

/**
 * Global instance of the scopes required by this quickstart.
 * If modifying these scopes, delete your previously saved tokens/ folder.
 */
private static final List<String> SCOPES = Collections.singletonList(CalendarScopes.CALENDAR_READONLY);
private static final String CREDENTIALS_FILE_PATH = "/credentials.json";

private Calendar.Events events;
private Context context;

/**
 * Creates an authorized Credential object.
 * @param HTTP_TRANSPORT The network HTTP Transport.
 * @return An authorized Credential object.
 * @throws IOException If the credentials.json file cannot be found.
 */
private static Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT) throws IOException {
    // Load client secrets.
    InputStream in = GoogleCalendarManager.class.getResourceAsStream(CREDENTIALS_FILE_PATH);
    if (in == null) {
        Log.println(Log.DEBUG, "DEB", "------- FILE NOT FOUND ---------");
        throw new FileNotFoundException("Resource not found: " + CREDENTIALS_FILE_PATH);
    } else {
        GoogleClientSecrets clientSecrets = GoogleClientSecrets
                .load(JSON_FACTORY, new InputStreamReader(in));

        GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
                HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
                .setDataStoreFactory(new FileDataStoreFactory(
                        new File(TOKENS_DIRECTORY_PATH)))
                .setAccessType("offline")
                .build();

        LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build();
        return new AuthorizationCodeInstalledApp(flow, receiver)
                .authorize("user");
    }
}

public GoogleCalendarManager(Context context) {
    this.context = context;
    // Build a new authorized API client service.
    NetHttpTransport HTTP_TRANSPORT = new NetHttpTransport();
    try {
        HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
    } catch (GeneralSecurityException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    Calendar service = null;
    try {
        service = new Calendar
                .Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))
                .setApplicationName(APPLICATION_NAME)
                .build();

        this.events = service.events();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public void addReminder(String domain, String username, String lastUpdate, int updatePeriodDays){
    Resources resources = context.getResources();

    Event event = new Event()
            .setSummary(resources.getString(R.string.event_summary) + domain)
            .setDescription(resources.getString(R.string.event_description)
                    + domain + ": " + username
                    + resources.getString(R.string.event_description_plus)
                    + lastUpdate);

    // Getting the date
    Date lastUpdateDate = null;
    try {
        lastUpdateDate = new SimpleDateFormat("dd/MM/yyyy")
                .parse(String.valueOf(lastUpdate));
    } catch (ParseException e) {
        e.printStackTrace();
    }

    long passPeriod = lastUpdateDate.getTime();
    long updatePeriod = TimeUnit.DAYS.toMillis(updatePeriodDays);
    long expirePeriod = passPeriod + updatePeriod;


    DateTime startDateTime = new DateTime(expirePeriod);
    EventDateTime start = new EventDateTime()
            .setDateTime(startDateTime);
    event.setStart(start);

    DateTime endDateTime = new DateTime(expirePeriod);
    EventDateTime end = new EventDateTime()
            .setDateTime(endDateTime);
    event.setEnd(end);

}
}

My problem is a java.io.IOException: unable to create directory: /tokens where I try to create a folder, in this line:

GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
            HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
            .setDataStoreFactory(new FileDataStoreFactory(
                    new File(TOKENS_DIRECTORY_PATH)))
            .setAccessType("offline")
            .build();

I've already tried to add permissions in the manifest file of my app:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.locker">
    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
    <application
        ...
    </application>

</manifest>

I really don't undestard how to write on the internal storage of the device. I've tried to write String.valueOf(Environment.getDataDirectory())) instead of the constant TOKENS_DIRECTORY_PATH in the File constructor, but I got another error, and the program crashes:

 java.lang.NoClassDefFoundError: Failed resolution of: Ljava/nio/file/attribute/PosixFilePermission;

I tried both the situations with an emulator and a real device, but the errors don't change. I don't know how to have permissions to write.

Thank you!!!

UPDATE I've added this methos in my Activity, executed before the creation of the GoogleCalendarManager:

public  boolean isStoragePermissionGranted() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
                == PackageManager.PERMISSION_GRANTED) {
            Log.println(Log.DEBUG, "DEB","Permission is granted");
            return true;
        } else {
            Log.println(Log.DEBUG, "DEB","Permission is revoked");
            ActivityCompat.requestPermissions(this, new String[]{
                    Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
            return false;
        }
    }
    else { //permission is automatically granted on sdk<23 upon installation
        Log.println(Log.DEBUG, "DEB","Permission is granted");
        return true;
    }
}

It prints Permission is granted but I have the same error as before.

You are trying to run slightly changed sample for java on Android without proper adaptation. The adaptation will include many steps and looks useless anyway since the used libraries looks not fully supported on Android. Launching jetty server instance looks redundant on mobile devices.

As first steps for adaptation to Android may be:

1.Call context.getFilesDir() instead new File(TOKENS_DIRECTORY_PATH) here:

GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
        HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
        .setDataStoreFactory(new FileDataStoreFactory(
                new File(TOKENS_DIRECTORY_PATH)))
        .setAccessType("offline")
        .build();

For internal storage (of your application) you don't need any permissions.

  1. Use AssetManager API to access resource files instead of GoogleCalendarManager.class.getResourceAsStream(CREDENTIALS_FILE_PATH) and so on)

I would suggest using standard Calendar Provider for Android or REST API

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM