com.geotrackin.gpslogger.senders.gdocs.GDocsHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.geotrackin.gpslogger.senders.gdocs.GDocsHelper.java

Source

/*
*    This file is part of GPSLogger for Android.
*
*    GPSLogger for Android is free software: you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation, either version 2 of the License, or
*    (at your option) any later version.
*
*    GPSLogger for Android is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with GPSLogger for Android.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.geotrackin.gpslogger.senders.gdocs;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.geotrackin.gpslogger.common.AppSettings;
import com.geotrackin.gpslogger.common.IActionListener;
import com.geotrackin.gpslogger.common.Utilities;
import com.geotrackin.gpslogger.senders.IFileSender;
import org.json.JSONObject;
import org.slf4j.LoggerFactory;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.List;

public class GDocsHelper implements IActionListener, IFileSender {

    private static final org.slf4j.Logger tracer = LoggerFactory.getLogger(GDocsHelper.class.getSimpleName());
    Context ctx;
    IActionListener callback;

    /*
    To revoke permissions:
    (new Android)
    ./adb -e shell 'sqlite3 /data/system/users/0/accounts.db "delete from grants;"'
    or
    (old Android)
    ./adb -e shell 'sqlite3 /data/system/accounts.db "delete from grants;"'
     */

    public GDocsHelper(Context applicationContext, IActionListener callback) {

        this.ctx = applicationContext;
        this.callback = callback;
    }

    public static String GetOauth2Scope() {
        return "oauth2:https://www.googleapis.com/auth/drive.file";
    }

    /**
     * Gets the stored authToken, which may be expired
     */
    public static String GetAuthToken(Context applicationContext) {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext);
        return prefs.getString("GDRIVE_AUTH_TOKEN", "");
    }

    /**
     * Gets the stored account name
     */
    public static String GetAccountName(Context applicationContext) {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext);
        return prefs.getString("GDRIVE_ACCOUNT_NAME", "");
    }

    public static void SetAccountName(Context applicationContext, String accountName) {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext);
        SharedPreferences.Editor editor = prefs.edit();

        editor.putString("GDRIVE_ACCOUNT_NAME", accountName);
        editor.commit();
    }

    /**
     * Saves the authToken and account name into shared preferences
     */
    public static void SaveAuthToken(Context applicationContext, String authToken) {
        try {
            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext);
            SharedPreferences.Editor editor = prefs.edit();

            tracer.debug("Saving GDocs authToken: " + authToken);
            editor.putString("GDRIVE_AUTH_TOKEN", authToken);
            editor.commit();
        } catch (Exception e) {

            tracer.error("GDocsHelper.SaveAuthToken", e);
        }

    }

    /**
     * Removes the authToken and account name from storage
     */
    public static void ClearAuthToken(Context applicationContext) {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext);
        SharedPreferences.Editor editor = prefs.edit();

        editor.remove("GDRIVE_AUTH_TOKEN");
        editor.remove("GDRIVE_ACCOUNT_NAME");
        editor.commit();
    }

    /**
     * Returns whether the app is authorized to perform Google API operations
     *
     * @param applicationContext
     * @return
     */
    public static boolean IsLinked(Context applicationContext) {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext);
        String gdocsAuthToken = prefs.getString("GDRIVE_AUTH_TOKEN", "");
        String gdocsAccount = prefs.getString("GDRIVE_ACCOUNT_NAME", "");
        return gdocsAuthToken.length() > 0 && gdocsAccount.length() > 0;
    }

    @Override
    public void UploadFile(List<File> files) {

        for (File f : files) {
            UploadFile(f.getName());
        }
    }

    public void UploadFile(final String fileName) {

        if (!IsLinked(ctx)) {
            callback.OnFailure();
            return;
        }

        try {

            File gpsDir = new File(AppSettings.getGpsLoggerFolder());
            File gpxFile = new File(gpsDir, fileName);
            FileInputStream fis = new FileInputStream(gpxFile);

            Thread t = new Thread(new GDocsUploadHandler(fis, fileName, GDocsHelper.this));
            t.start();

        } catch (Exception e) {
            callback.OnFailure();
            tracer.error("GDocsHelper.UploadFile", e);
        }
    }

    private class GDocsUploadHandler implements Runnable {

        String fileName;
        InputStream inputStream;
        IActionListener callback;

        GDocsUploadHandler(InputStream inputStream, String fileName, IActionListener callback) {

            this.inputStream = inputStream;
            this.fileName = fileName;
            this.callback = callback;
        }

        @Override
        public void run() {
            try {

                String token = GoogleAuthUtil.getTokenWithNotification(ctx, GetAccountName(ctx), GetOauth2Scope(),
                        new Bundle());
                GDocsHelper.SaveAuthToken(ctx, token);
                tracer.debug(token);

                String gpsLoggerFolderId = GetFileIdFromFileName(token, "GPSLogger For Android");

                if (Utilities.IsNullOrEmpty(gpsLoggerFolderId)) {
                    //Couldn't find folder, must create it
                    gpsLoggerFolderId = CreateEmptyFile(token, "GPSLogger For Android",
                            "application/vnd.google-apps.folder", "root");

                    if (Utilities.IsNullOrEmpty(gpsLoggerFolderId)) {
                        callback.OnFailure();
                        return;
                    }
                }

                //Now search for the file
                String gpxFileId = GetFileIdFromFileName(token, fileName);

                if (Utilities.IsNullOrEmpty(gpxFileId)) {
                    //Create empty file first
                    gpxFileId = CreateEmptyFile(token, fileName, GetMimeTypeFromFileName(fileName),
                            gpsLoggerFolderId);

                    if (Utilities.IsNullOrEmpty(gpxFileId)) {
                        callback.OnFailure();
                        return;
                    }
                }

                if (!Utilities.IsNullOrEmpty(gpxFileId)) {
                    //Set file's contents
                    UpdateFileContents(token, gpxFileId, Utilities.GetByteArrayFromInputStream(inputStream),
                            fileName);
                }

                callback.OnComplete();

            } catch (Exception e) {
                tracer.error("GDocsUploadHandler", e);
                callback.OnFailure();
            }

        }
    }

    private String UpdateFileContents(String authToken, String gpxFileId, byte[] fileContents, String fileName) {
        HttpURLConnection conn = null;
        String fileId = null;

        String fileUpdateUrl = "https://www.googleapis.com/upload/drive/v2/files/" + gpxFileId
                + "?uploadType=media";

        try {

            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
                //Due to a pre-froyo bug
                //http://android-developers.blogspot.com/2011/09/androids-http-clients.html
                System.setProperty("http.keepAlive", "false");
            }

            URL url = new URL(fileUpdateUrl);

            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("PUT");
            conn.setRequestProperty("User-Agent", "GPSLogger for Android");
            conn.setRequestProperty("Authorization", "Bearer " + authToken);
            conn.setRequestProperty("Content-Type", GetMimeTypeFromFileName(fileName));
            conn.setRequestProperty("Content-Length", String.valueOf(fileContents.length));

            conn.setUseCaches(false);
            conn.setDoInput(true);
            conn.setDoOutput(true);

            DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
            wr.write(fileContents);
            wr.flush();
            wr.close();

            String fileMetadata = Utilities.GetStringFromInputStream(conn.getInputStream());

            JSONObject fileMetadataJson = new JSONObject(fileMetadata);
            fileId = fileMetadataJson.getString("id");
            tracer.debug("File updated : " + fileId);

        } catch (Exception e) {
            System.out.println(e.getMessage());
        } finally {
            if (conn != null) {
                conn.disconnect();
            }

        }

        return fileId;

    }

    private String CreateEmptyFile(String authToken, String fileName, String mimeType, String parentFolderId) {

        String fileId = null;
        HttpURLConnection conn = null;

        String createFileUrl = "https://www.googleapis.com/drive/v2/files";

        String createFilePayload = "   {\n" + "             \"title\": \"" + fileName + "\",\n"
                + "             \"mimeType\": \"" + mimeType + "\",\n" + "             \"parents\": [\n"
                + "              {\n" + "               \"id\": \"" + parentFolderId + "\"\n" + "              }\n"
                + "             ]\n" + "            }";

        try {

            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
                //Due to a pre-froyo bug
                //http://android-developers.blogspot.com/2011/09/androids-http-clients.html
                System.setProperty("http.keepAlive", "false");
            }

            URL url = new URL(createFileUrl);

            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            conn.setRequestProperty("User-Agent", "GPSLogger for Android");
            conn.setRequestProperty("Authorization", "Bearer " + authToken);
            conn.setRequestProperty("Content-Type", "application/json");

            conn.setUseCaches(false);
            conn.setDoInput(true);
            conn.setDoOutput(true);

            DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
            wr.writeBytes(createFilePayload);
            wr.flush();
            wr.close();

            fileId = null;

            String fileMetadata = Utilities.GetStringFromInputStream(conn.getInputStream());

            JSONObject fileMetadataJson = new JSONObject(fileMetadata);
            fileId = fileMetadataJson.getString("id");
            tracer.debug("File created with ID " + fileId + " of type " + mimeType);

        } catch (Exception e) {

            System.out.println(e.getMessage());
            System.out.println(e.getMessage());
        } finally {
            if (conn != null) {
                conn.disconnect();
            }

        }

        return fileId;
    }

    private String GetFileIdFromFileName(String authToken, String fileName) {

        HttpURLConnection conn = null;
        String fileId = "";

        try {

            fileName = URLEncoder.encode(fileName, "UTF-8");
            String searchUrl = "https://www.googleapis.com/drive/v2/files?q=title%20%3D%20%27" + fileName
                    + "%27%20and%20trashed%20%3D%20false";

            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
                //Due to a pre-froyo bug
                //http://android-developers.blogspot.com/2011/09/androids-http-clients.html
                System.setProperty("http.keepAlive", "false");
            }

            URL url = new URL(searchUrl);

            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setRequestProperty("User-Agent", "GPSLogger for Android");
            conn.setRequestProperty("Authorization", "OAuth " + authToken);

            String fileMetadata = Utilities.GetStringFromInputStream(conn.getInputStream());

            JSONObject fileMetadataJson = new JSONObject(fileMetadata);
            if (fileMetadataJson.getJSONArray("items") != null
                    && fileMetadataJson.getJSONArray("items").length() > 0) {
                fileId = fileMetadataJson.getJSONArray("items").getJSONObject(0).get("id").toString();
                tracer.debug("Found file with ID " + fileId);
            }

        } catch (Exception e) {
            tracer.error("SearchForGPSLoggerFile", e);
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }

        return fileId;
    }

    private String GetMimeTypeFromFileName(String fileName) {
        if (fileName.endsWith("kml")) {
            return "application/vnd.google-earth.kml+xml";
        }

        if (fileName.endsWith("gpx")) {
            return "application/gpx+xml";
        }

        if (fileName.endsWith("zip")) {
            return "application/zip";
        }

        if (fileName.endsWith("xml")) {
            return "application/xml";
        }

        if (fileName.endsWith("nmea")) {
            return "text/plain";
        }

        return "application/vnd.google-apps.spreadsheet";
    }

    @Override
    public boolean accept(File dir, String name) {
        return true;
    }

    @Override
    public void OnComplete() {
        callback.OnComplete();
    }

    @Override
    public void OnFailure() {
        callback.OnFailure();
    }
}