com.zns.comicdroid.service.GoogleDriveService.java Source code

Java tutorial

Introduction

Here is the source code for com.zns.comicdroid.service.GoogleDriveService.java

Source

/*******************************************************************************
 * Copyright (c) 2013 Ulrik Andersson.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 * 
 * Contributors:
 *     Ulrik Andersson - initial API and implementation
 ******************************************************************************/
package com.zns.comicdroid.service;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

import android.app.IntentService;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.util.Base64;

import com.google.android.gms.auth.GoogleAuthException;
import com.google.android.gms.auth.UserRecoverableAuthException;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.http.ByteArrayContent;
import com.google.api.client.http.FileContent;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.Drive.Files.Update;
import com.google.api.services.drive.model.FileList;
import com.google.api.services.drive.model.ParentReference;
import com.zns.comicdroid.Application;
import com.zns.comicdroid.R;
import com.zns.comicdroid.activity.Settings;
import com.zns.comicdroid.data.Comic;
import com.zns.comicdroid.data.DBHelper;
import com.zns.comicdroid.util.BackupUtil;
import com.zns.comicdroid.util.DriveUtil;
import com.zns.comicdroid.util.Logger;

import de.greenrobot.event.EventBus;

public class GoogleDriveService extends IntentService {

    public static final String INTENT_PUBLISH_ONLY = "com.zns.comicdroid.PUBLISH_ONLY";
    public static final String BACKUP_META_FILENAME = "backup.meta";
    public static final String BACKUP_DATA_FILENAME = "data.dat";
    public static final String PUBLISH_INDEX_FILENAME = "index.html";
    private DBHelper mDb;
    private Logger mLogger;

    private NotificationManager notificationManager;

    public GoogleDriveService() {
        super("ComicDroid google drive service");
    }

    private void NotifyAuthentication() {
        //Set prefs
        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
        Editor editor = pref.edit();
        editor.putBoolean(Application.PREF_DRIVE_PUBLISH, false);
        editor.commit();

        Intent settingsIntent = new Intent(getApplicationContext(), Settings.class);
        settingsIntent.putExtra(Settings.INTENT_STOP_UPLOAD, true);

        TaskStackBuilder stack = TaskStackBuilder.create(getApplicationContext());
        stack.addParentStack(Settings.class);
        stack.addNextIntent(settingsIntent);

        PendingIntent pendingIntent = stack.getPendingIntent(0, PendingIntent.FLAG_CANCEL_CURRENT);
        NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext())
                .setContentTitle(getString(R.string.nUploaderrorheading)).setSmallIcon(R.drawable.ic_launcher)
                .setContentText(getString(R.string.nUploaderrorsub)).setAutoCancel(true)
                .setContentIntent(pendingIntent);

        notificationManager.notify(1, builder.build());
    }

    @Override
    public void onCreate() {
        super.onCreate();
        notificationManager = (NotificationManager) getApplicationContext().getSystemService(NOTIFICATION_SERVICE);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        boolean publishOnly = intent.getBooleanExtra(INTENT_PUBLISH_ONLY, false);

        mLogger = new Logger(getExternalFilesDir(null).toString() + "/log");
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());

        //Get database connections
        mDb = DBHelper.getHelper(getApplicationContext());

        //Google drive check      
        boolean publishEnabled = prefs.getBoolean(Application.PREF_DRIVE_PUBLISH, false);
        boolean backupEnabled = prefs.getBoolean(Application.PREF_DRIVE_BACKUP, false);
        String account = prefs.getString(Application.PREF_DRIVE_ACCOUNT, null);
        String webFolderId = prefs.getString(Application.PREF_DRIVE_WEBFOLDERID, null);
        String appId = prefs.getString(Application.PREF_APP_ID, "");

        //Backup
        if (backupEnabled && !publishOnly) {
            if (account != null) {
                Backup(account, appId);
            } else {
                //We do not seem to have access to appdata.... try to recover
                NotifyAuthentication();
            }
        }

        //Publish  
        if (publishEnabled || publishOnly) {
            if (account != null && webFolderId != null) {
                PublishComics(account, webFolderId, publishOnly);
            } else {
                //Webfolder id does not exist anymore.... try to recover            
                NotifyAuthentication();
            }
        }

        stopAndClean();
    }

    private synchronized void Backup(String account, String appId) {
        //Get Service
        Drive service = null;
        try {
            GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(getApplicationContext(),
                    Arrays.asList(Application.DRIVE_SCOPE_BACKUP));
            credential.setSelectedAccountName(account);
            credential.getToken();
            service = new Drive.Builder(AndroidHttp.newCompatibleTransport(), new JacksonFactory(), credential)
                    .build();
        } catch (UserRecoverableAuthException e) {
            //We are not authenticated for some reason, notify user.
            NotifyAuthentication();
            return;
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return;
        } catch (GoogleAuthException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return;
        }

        //Make sure the current backup is made from the same device
        try {
            com.google.api.services.drive.model.File f = DriveUtil.getFile(service, "appdata",
                    BACKUP_META_FILENAME);
            if (f != null) {
                HttpResponse response = service.getRequestFactory()
                        .buildGetRequest(new GenericUrl(f.getDownloadUrl())).execute();
                BufferedReader reader = new BufferedReader(new InputStreamReader(response.getContent()));
                String backupAppId = reader.readLine();
                reader.close();
                response.disconnect();

                if (!backupAppId.equals(appId)) {
                    mLogger.appendLog("Unable to backup to drive, appid does not match", Logger.TAG_BACKUP);
                    return;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        //------------------Upload current backup to google drive---------------------------
        //Get file path
        String outPath = getApplicationContext().getExternalFilesDir(null).toString() + "/backup";
        File fileData = new File(outPath, BACKUP_DATA_FILENAME);

        //Upload to google drive
        int timeStamp = (int) (System.currentTimeMillis() / 1000L);
        boolean uploadSuccess = true;
        try {
            //Get files to update/insert
            com.google.api.services.drive.model.File dataDriveFile = null;
            com.google.api.services.drive.model.File metaDriveFile = null;
            FileList list = service.files().list().setQ("'appdata' in parents").execute();
            for (com.google.api.services.drive.model.File f : list.getItems()) {
                if (f.getTitle().toLowerCase(Locale.ENGLISH).equals(BACKUP_META_FILENAME)) {
                    metaDriveFile = f;
                } else if (f.getTitle().toLowerCase(Locale.ENGLISH).equals(BACKUP_DATA_FILENAME)) {
                    dataDriveFile = f;
                }
            }

            //Insert meta         
            ByteArrayContent contentMeta = ByteArrayContent.fromString("text/plain",
                    appId + "\n" + Integer.toString(timeStamp));
            com.google.api.services.drive.model.File fMeta = new com.google.api.services.drive.model.File();
            fMeta.setTitle(BACKUP_META_FILENAME);
            fMeta.setMimeType("text/plain");
            fMeta.setParents(Arrays.asList(new ParentReference().setId("appdata")));
            if (metaDriveFile == null) {
                service.files().insert(fMeta, contentMeta).execute();
            } else {
                Update update = service.files().update(metaDriveFile.getId(), null, contentMeta);
                update.setNewRevision(false);
                update.execute();
            }

            //Insert/Update data
            FileContent contentData = new FileContent("application/octet-stream", fileData);
            com.google.api.services.drive.model.File fData = new com.google.api.services.drive.model.File();
            fData.setTitle(BACKUP_DATA_FILENAME);
            fData.setMimeType("application/octet-stream");
            fData.setParents(Arrays.asList(new ParentReference().setId("appdata")));
            if (dataDriveFile == null) {
                service.files().insert(fData, contentData).execute();
            } else {
                Update update = service.files().update(dataDriveFile.getId(), null, contentData);
                update.setNewRevision(false);
                update.execute();
            }
        } catch (Exception e) {
            uploadSuccess = false;
            e.printStackTrace();
        }

        //Success?
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
        Editor edit = prefs.edit();
        edit.putBoolean(Application.PREF_BACKUP_SUCCESS, uploadSuccess);
        if (uploadSuccess) {
            mLogger.appendLog("Backup to drive success", Logger.TAG_BACKUP);
            edit.putInt(Application.PREF_BACKUP_LAST, timeStamp);
        } else {
            mLogger.appendLog("Backup to drive failed", Logger.TAG_BACKUP);
        }
        edit.commit();
    }

    private synchronized void PublishComics(String account, String webFolderId, boolean force) {
        //Get Service
        Drive service = null;
        try {
            GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(getApplicationContext(),
                    Arrays.asList(Application.DRIVE_SCOPE_PUBLISH));
            credential.setSelectedAccountName(account);
            credential.getToken();
            service = new Drive.Builder(AndroidHttp.newCompatibleTransport(), new JacksonFactory(), credential)
                    .build();
        } catch (UserRecoverableAuthException e) {
            //We are not authenticated for some reason, notify user.
            NotifyAuthentication();
            return;
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return;
        } catch (GoogleAuthException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return;
        }

        //Make sure webfolder still exists
        try {
            com.google.api.services.drive.model.File webFolder = service.files().get(webFolderId).execute();
            if (webFolder == null || webFolder.getExplicitlyTrashed() == Boolean.TRUE) {
                NotifyAuthentication();
                return;
            }
        } catch (IOException e) {
            NotifyAuthentication();
            return;
        }

        //Get some more stuff from context
        String outPath = getApplicationContext().getExternalFilesDir(null).toString() + "/html";
        String imagePath = ((Application) getApplication()).getImagePath(true);

        //Let's get started
        File fileOut = new File(outPath);
        fileOut.mkdirs();
        fileOut = new File(outPath, PUBLISH_INDEX_FILENAME);

        BufferedReader reader = null;
        BufferedWriter writer = null;
        Cursor cursor = null;

        try {
            cursor = mDb.getCursor(
                    "SELECT _id, Title, Subtitle, Author, Image, ImageUrl, 1 AS ItemType, 0 AS BookCount, IsBorrowed, AddedDate "
                            + "FROM tblBooks WHERE GroupId = 0 OR ifnull(GroupId, '') = '' " + "UNION "
                            + "SELECT _id, Name AS Title, '' AS Subtitle, '' AS Author, (SELECT Image FROM tblBooks where GroupId = tblGroups._id) AS Image, ImageUrl, 2 AS ItemType, BookCount, 0 AS IsBorrowed, 0 AS PublishDate "
                            + "FROM tblGroups " + "ORDER BY Title",
                    null);

            int rowCount = cursor.getCount();
            String line;

            //Read template
            StringBuilder sbTemplate = new StringBuilder();
            reader = new BufferedReader(new InputStreamReader(getResources().openRawResource(R.raw.listtemplate)));
            while ((line = reader.readLine()) != null) {
                sbTemplate.append(line);
            }
            reader.close();

            //Write HTML
            writer = new BufferedWriter(new FileWriter(fileOut));
            reader = new BufferedReader(new InputStreamReader(getResources().openRawResource(R.raw.framework)));
            int i = 0;
            while ((line = reader.readLine()) != null) {
                if (line.trim().equals("#LISTCOMICS#")) {
                    while (cursor.moveToNext()) {
                        int id = cursor.getInt(0);
                        String title = nullToEmpty(cursor.getString(1));
                        String subTitle = nullToEmpty(cursor.getString(2));
                        String author = nullToEmpty(cursor.getString(3));
                        String image = nullToEmpty(cursor.getString(4));
                        String imageUrl = nullToEmpty(cursor.getString(5));
                        int type = cursor.getInt(6);
                        int date = cursor.getInt(9);

                        StringBuilder sbChildren = new StringBuilder();

                        String comicLine = sbTemplate.toString();
                        comicLine = comicLine.replace("#TITLE#",
                                title + (type == 1 && !subTitle.equals("") ? " - " + subTitle : ""));
                        comicLine = comicLine.replace("#AUTHOR#", author);
                        comicLine = comicLine.replace("#IMAGEURL#", getImageSrc(imageUrl, image, imagePath));
                        comicLine = comicLine.replace("#ISSUE#", "");
                        comicLine = comicLine.replace("#ISAGGREGATE#", type == 2 ? " Aggregate" : "");
                        comicLine = comicLine.replace("#MARK#", type == 2 ? "<div class=\"Mark\"></div>" : "");
                        comicLine = comicLine.replace("#DATE#", type == 1 ? Integer.toString(date) : "");

                        if (type == 2) {
                            //Render group children
                            List<Comic> comics = mDb.getComics(id);
                            for (Comic comic : comics) {
                                String childComic = sbTemplate.toString();
                                childComic = childComic.replace("#TITLE#",
                                        nullToEmpty(comic.getTitle())
                                                + (!nullToEmpty(comic.getSubTitle()).equals("")
                                                        ? " - " + comic.getSubTitle()
                                                        : ""));
                                childComic = childComic.replace("#AUTHOR#", nullToEmpty(comic.getAuthor()));
                                childComic = childComic.replace("#IMAGEURL#",
                                        getImageSrc(nullToEmpty(comic.getImageUrl()), nullToEmpty(comic.getImage()),
                                                imagePath));
                                childComic = childComic.replace("#ISSUE#",
                                        comic.getIssue() > 0 ? Integer.toString(comic.getIssue()) : "");
                                childComic = childComic.replace("#DATE#",
                                        Integer.toString(comic.getAddedDateTimestamp()));
                                childComic = childComic.replace("#ISAGGREGATE#", "");
                                childComic = childComic.replace("#MARK#", "");
                                childComic = childComic.replace("#CHILDREN#", "");
                                sbChildren.append(childComic);
                            }
                            comicLine = comicLine.replace("#CHILDREN#", sbChildren.toString());
                        } else {
                            comicLine = comicLine.replace("#CHILDREN#", "");
                        }

                        writer.write(comicLine);

                        if (force) {
                            Double part = (((double) i + 1) / (double) rowCount) * 100.0;
                            EventBus.getDefault().post(
                                    new ProgressResult(part.intValue(), getString(R.string.progress_publish)));
                            i++;
                        }
                    }
                } else {
                    writer.write(line);
                }
            }
        } catch (Exception e) {
            //Gotta catch'em all!
            return;
        } finally {
            if (cursor != null)
                cursor.close();
            try {
                if (writer != null)
                    writer.close();
                if (reader != null)
                    reader.close();
            } catch (IOException e) {
            }
        }

        //Upload to google drive
        if (force) {
            EventBus.getDefault().post(new ProgressResult(20, getString(R.string.progress_publishupload)));
        }

        try {
            //Get current index file
            com.google.api.services.drive.model.File fileIndex = DriveUtil.getFile(service, webFolderId,
                    PUBLISH_INDEX_FILENAME);

            //Set content of file
            FileContent content = new FileContent("text/html", fileOut);

            //Insert / Update
            if (fileIndex == null) {
                com.google.api.services.drive.model.File driveFile = new com.google.api.services.drive.model.File();
                driveFile.setTitle(PUBLISH_INDEX_FILENAME);
                driveFile.setMimeType("text/html");
                driveFile.setParents(Arrays.asList(new ParentReference().setId(webFolderId)));
                service.files().insert(driveFile, content).execute();
            } else {
                Update update = service.files().update(fileIndex.getId(), null, content);
                update.setNewRevision(false);
                update.execute();
            }

            mLogger.appendLog("Published to google drive", Logger.TAG_BACKUP);
        } catch (Exception e) {
            mLogger.appendLog("Failed to publish to google drive", Logger.TAG_BACKUP);
            e.printStackTrace();
        }

        if (force) {
            EventBus.getDefault().post(new ProgressResult(100, getString(R.string.progress_publishupload)));
        }
    }

    @Override
    public void onDestroy() {
        //Just to make sure we release the wifi lock
        BackupUtil.releaseWifiLock();
        super.onDestroy();
    }

    private void stopAndClean() {
        BackupUtil.releaseWifiLock();
        stopSelf();
    }

    private String nullToEmpty(String val) {
        if (val == null)
            return "";
        return val;
    }

    private String getImageSrc(String imageUrl, String image, String imagePath) {
        String imgSrc = imageUrl;
        if (!image.equals("") && imageUrl.equals("")) {
            ByteArrayOutputStream stream = null;
            try {
                Bitmap bmp = BitmapFactory.decodeFile(imagePath.concat(image));
                stream = new ByteArrayOutputStream();
                bmp.compress(Bitmap.CompressFormat.JPEG, 100, stream);
                byte[] b = stream.toByteArray();
                imgSrc = "data:image/jpeg;base64,".concat(Base64.encodeToString(b, Base64.DEFAULT));
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    stream.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return imgSrc;
    }
}