com.dycody.android.idealnote.async.DataBackupIntentService.java Source code

Java tutorial

Introduction

Here is the source code for com.dycody.android.idealnote.async.DataBackupIntentService.java

Source

/*
 * Copyright (C) 2015 Federico Iosue (federico.iosue@gmail.com)
 *
 * This program 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 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.dycody.android.idealnote.async;

import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.net.Uri;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;

import com.dycody.android.idealnote.IdealNote;
import com.dycody.android.idealnote.MainActivity;
import com.dycody.android.idealnote.models.Category;
import com.dycody.android.idealnote.models.Note;
import com.dycody.android.idealnote.utils.Constants;
import com.dycody.android.idealnote.utils.NotificationsHelper;
import com.dycody.android.idealnote.utils.TextHelper;

import exceptions.ImportException;

import com.dycody.android.idealnote.utils.GeocodeHelper;
import com.dycody.android.idealnote.utils.ReminderHelper;
import com.dycody.android.idealnote.utils.StorageHelper;
import com.dycody.android.idealnote.db.DbHelper;
import com.dycody.android.idealnote.models.Attachment;
import com.dycody.android.idealnote.models.listeners.OnAttachingFileListener;
import it.feio.android.springpadimporter.Importer;
import it.feio.android.springpadimporter.models.SpringpadAttachment;
import it.feio.android.springpadimporter.models.SpringpadComment;
import it.feio.android.springpadimporter.models.SpringpadElement;
import it.feio.android.springpadimporter.models.SpringpadItem;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.RegexFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.*;

public class DataBackupIntentService extends IntentService implements OnAttachingFileListener {

    public final static String INTENT_BACKUP_NAME = "backup_name";
    public final static String INTENT_BACKUP_INCLUDE_SETTINGS = "backup_include_settings";
    public final static String ACTION_DATA_EXPORT = "action_data_export";
    public final static String ACTION_DATA_IMPORT = "action_data_import";
    public final static String ACTION_DATA_IMPORT_SPRINGPAD = "action_data_import_springpad";
    public final static String ACTION_DATA_DELETE = "action_data_delete";
    public final static String EXTRA_SPRINGPAD_BACKUP = "extra_springpad_backup";

    private SharedPreferences prefs;
    private NotificationsHelper mNotificationsHelper;

    private int importedSpringpadNotes, importedSpringpadNotebooks;

    public DataBackupIntentService() {
        super("DataBackupIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        prefs = getSharedPreferences(Constants.PREFS_NAME, MODE_MULTI_PROCESS);

        // Creates an indeterminate processing notification until the work is complete
        mNotificationsHelper = new NotificationsHelper(this)
                .createNotification(com.dycody.android.idealnote.R.drawable.ic_content_save_white_24dp,
                        getString(com.dycody.android.idealnote.R.string.working), null)
                .setIndeterminate().setOngoing().show();

        // If an alarm has been fired a notification must be generated
        if (ACTION_DATA_EXPORT.equals(intent.getAction())) {
            exportData(intent);
        } else if (ACTION_DATA_IMPORT.equals(intent.getAction())) {
            importData(intent);
        } else if (ACTION_DATA_IMPORT_SPRINGPAD.equals(intent.getAction())) {
            importDataFromSpringpad(intent);
        } else if (ACTION_DATA_DELETE.equals(intent.getAction())) {
            deleteData(intent);
        }
    }

    synchronized private void exportData(Intent intent) {

        // Gets backup folder
        String backupName = intent.getStringExtra(INTENT_BACKUP_NAME);
        File backupDir = StorageHelper.getBackupDir(backupName);

        // Directory clean in case of previously used backup name
        StorageHelper.delete(this, backupDir.getAbsolutePath());

        // Directory is re-created in case of previously used backup name (removed above)
        backupDir = StorageHelper.getBackupDir(backupName);

        // Database backup
        exportDB(backupDir);
        //      exportNotes(backupDir);

        // Attachments backup
        exportAttachments(backupDir);

        // Settings
        if (intent.getBooleanExtra(INTENT_BACKUP_INCLUDE_SETTINGS, true)) {
            exportSettings(backupDir);
        }

        // Notification of operation ended
        String title = getString(com.dycody.android.idealnote.R.string.data_export_completed);
        String text = backupDir.getPath();
        createNotification(intent, this, title, text, backupDir);
    }

    synchronized private void importData(Intent intent) {

        // Gets backup folder
        String backupName = intent.getStringExtra(INTENT_BACKUP_NAME);
        File backupDir = StorageHelper.getBackupDir(backupName);

        // Database backup
        importDB(backupDir);
        //        importNotes(backupDir);

        // Attachments backup
        importAttachments(backupDir);

        // Settings restore
        importSettings(backupDir);

        // Reminders restore
        resetReminders();

        String title = getString(com.dycody.android.idealnote.R.string.data_import_completed);
        String text = getString(com.dycody.android.idealnote.R.string.click_to_refresh_application);
        createNotification(intent, this, title, text, backupDir);
    }

    /**
      * Imports notes and notebooks from Springpad exported archive
      *
      * @param intent
      */
    synchronized private void importDataFromSpringpad(Intent intent) {
        String backupPath = intent.getStringExtra(EXTRA_SPRINGPAD_BACKUP);
        Importer importer = new Importer();
        try {
            importer.setZipProgressesListener(percentage -> mNotificationsHelper
                    .setMessage(getString(com.dycody.android.idealnote.R.string.extracted) + " " + percentage + "%")
                    .show());
            importer.doImport(backupPath);
            // Updating notification
            updateImportNotification(importer);
        } catch (ImportException e) {
            new NotificationsHelper(this)
                    .createNotification(com.dycody.android.idealnote.R.drawable.ic_emoticon_sad_white_24dp,
                            getString(com.dycody.android.idealnote.R.string.import_fail) + ": " + e.getMessage(),
                            null)
                    .setLedActive().show();
            return;
        }
        List<SpringpadElement> elements = importer.getSpringpadNotes();

        // If nothing is retrieved it will exit
        if (elements == null || elements.size() == 0) {
            return;
        }

        // These maps are used to associate with post processing notes to categories (notebooks)
        HashMap<String, Category> categoriesWithUuid = new HashMap<>();

        // Adds all the notebooks (categories)
        for (SpringpadElement springpadElement : importer.getNotebooks()) {
            Category cat = new Category();
            cat.setName(springpadElement.getName());
            cat.setColor(String.valueOf(Color.parseColor("#F9EA1B")));
            DbHelper.getInstance().updateCategory(cat);
            categoriesWithUuid.put(springpadElement.getUuid(), cat);

            // Updating notification
            importedSpringpadNotebooks++;
            updateImportNotification(importer);
        }
        // And creates a default one for notes without notebook 
        Category defaulCategory = new Category();
        defaulCategory.setName("Springpad");
        defaulCategory.setColor(String.valueOf(Color.parseColor("#F9EA1B")));
        DbHelper.getInstance().updateCategory(defaulCategory);

        // And then notes are created
        Note note;
        Attachment mAttachment = null;
        Uri uri;
        for (SpringpadElement springpadElement : importer.getNotes()) {
            note = new Note();

            // Title
            note.setTitle(springpadElement.getName());

            // Content dependent from type of Springpad note
            StringBuilder content = new StringBuilder();
            content.append(
                    TextUtils.isEmpty(springpadElement.getText()) ? "" : Html.fromHtml(springpadElement.getText()));
            content.append(
                    TextUtils.isEmpty(springpadElement.getDescription()) ? "" : springpadElement.getDescription());

            // Some notes could have been exported wrongly
            if (springpadElement.getType() == null) {
                Toast.makeText(this, getString(com.dycody.android.idealnote.R.string.error), Toast.LENGTH_SHORT)
                        .show();
                continue;
            }

            if (springpadElement.getType().equals(SpringpadElement.TYPE_VIDEO)) {
                try {
                    content.append(System.getProperty("line.separator"))
                            .append(springpadElement.getVideos().get(0));
                } catch (IndexOutOfBoundsException e) {
                    content.append(System.getProperty("line.separator")).append(springpadElement.getUrl());
                }
            }
            if (springpadElement.getType().equals(SpringpadElement.TYPE_TVSHOW)) {
                content.append(System.getProperty("line.separator"))
                        .append(TextUtils.join(", ", springpadElement.getCast()));
            }
            if (springpadElement.getType().equals(SpringpadElement.TYPE_BOOK)) {
                content.append(System.getProperty("line.separator")).append("Author: ")
                        .append(springpadElement.getAuthor()).append(System.getProperty("line.separator"))
                        .append("Publication date: ").append(springpadElement.getPublicationDate());
            }
            if (springpadElement.getType().equals(SpringpadElement.TYPE_RECIPE)) {
                content.append(System.getProperty("line.separator")).append("Ingredients: ")
                        .append(springpadElement.getIngredients()).append(System.getProperty("line.separator"))
                        .append("Directions: ").append(springpadElement.getDirections());
            }
            if (springpadElement.getType().equals(SpringpadElement.TYPE_BOOKMARK)) {
                content.append(System.getProperty("line.separator")).append(springpadElement.getUrl());
            }
            if (springpadElement.getType().equals(SpringpadElement.TYPE_BUSINESS)
                    && springpadElement.getPhoneNumbers() != null) {
                content.append(System.getProperty("line.separator")).append("Phone number: ")
                        .append(springpadElement.getPhoneNumbers().getPhone());
            }
            if (springpadElement.getType().equals(SpringpadElement.TYPE_PRODUCT)) {
                content.append(System.getProperty("line.separator")).append("Category: ")
                        .append(springpadElement.getCategory()).append(System.getProperty("line.separator"))
                        .append("Manufacturer: ").append(springpadElement.getManufacturer())
                        .append(System.getProperty("line.separator")).append("Price: ")
                        .append(springpadElement.getPrice());
            }
            if (springpadElement.getType().equals(SpringpadElement.TYPE_WINE)) {
                content.append(System.getProperty("line.separator")).append("Wine type: ")
                        .append(springpadElement.getWine_type()).append(System.getProperty("line.separator"))
                        .append("Varietal: ").append(springpadElement.getVarietal())
                        .append(System.getProperty("line.separator")).append("Price: ")
                        .append(springpadElement.getPrice());
            }
            if (springpadElement.getType().equals(SpringpadElement.TYPE_ALBUM)) {
                content.append(System.getProperty("line.separator")).append("Artist: ")
                        .append(springpadElement.getArtist());
            }
            for (SpringpadComment springpadComment : springpadElement.getComments()) {
                content.append(System.getProperty("line.separator")).append(springpadComment.getCommenter())
                        .append(" commented at 0").append(springpadComment.getDate()).append(": ")
                        .append(springpadElement.getArtist());
            }

            note.setContent(content.toString());

            // Checklists
            if (springpadElement.getType().equals(SpringpadElement.TYPE_CHECKLIST)) {
                StringBuilder sb = new StringBuilder();
                String checkmark;
                for (SpringpadItem mSpringpadItem : springpadElement.getItems()) {
                    checkmark = mSpringpadItem.getComplete()
                            ? it.feio.android.checklistview.interfaces.Constants.CHECKED_SYM
                            : it.feio.android.checklistview.interfaces.Constants.UNCHECKED_SYM;
                    sb.append(checkmark).append(mSpringpadItem.getName())
                            .append(System.getProperty("line.separator"));
                }
                note.setContent(sb.toString());
                note.setChecklist(true);
            }

            // Tags
            String tags = springpadElement.getTags().size() > 0
                    ? "#" + TextUtils.join(" #", springpadElement.getTags())
                    : "";
            if (note.isChecklist()) {
                note.setTitle(note.getTitle() + tags);
            } else {
                note.setContent(note.getContent() + System.getProperty("line.separator") + tags);
            }

            // Address
            String address = springpadElement.getAddresses() != null ? springpadElement.getAddresses().getAddress()
                    : "";
            if (!TextUtils.isEmpty(address)) {
                try {
                    double[] coords = GeocodeHelper.getCoordinatesFromAddress(this, address);
                    note.setLatitude(coords[0]);
                    note.setLongitude(coords[1]);
                } catch (IOException e) {
                    Log.e(Constants.TAG,
                            "An error occurred trying to resolve address to coords during Springpad import");
                }
                note.setAddress(address);
            }

            // Reminder
            if (springpadElement.getDate() != null) {
                note.setAlarm(springpadElement.getDate().getTime());
            }

            // Creation, modification, category
            note.setCreation(springpadElement.getCreated().getTime());
            note.setLastModification(springpadElement.getModified().getTime());

            // Image
            String image = springpadElement.getImage();
            if (!TextUtils.isEmpty(image)) {
                try {
                    File file = StorageHelper.createNewAttachmentFileFromHttp(this, image);
                    uri = Uri.fromFile(file);
                    String mimeType = StorageHelper.getMimeType(uri.getPath());
                    mAttachment = new Attachment(uri, mimeType);
                } catch (MalformedURLException e) {
                    uri = Uri.parse(importer.getWorkingPath() + image);
                    mAttachment = StorageHelper.createAttachmentFromUri(this, uri, true);
                } catch (IOException e) {
                    Log.e(Constants.TAG, "Error retrieving Springpad online image");
                }
                if (mAttachment != null) {
                    note.addAttachment(mAttachment);
                }
                mAttachment = null;
            }

            // Other attachments
            for (SpringpadAttachment springpadAttachment : springpadElement.getAttachments()) {
                // The attachment could be the image itself so it's jumped
                if (image != null && image.equals(springpadAttachment.getUrl()))
                    continue;

                if (TextUtils.isEmpty(springpadAttachment.getUrl())) {
                    continue;
                }

                // Tries first with online images
                try {
                    File file = StorageHelper.createNewAttachmentFileFromHttp(this, springpadAttachment.getUrl());
                    uri = Uri.fromFile(file);
                    String mimeType = StorageHelper.getMimeType(uri.getPath());
                    mAttachment = new Attachment(uri, mimeType);
                } catch (MalformedURLException e) {
                    uri = Uri.parse(importer.getWorkingPath() + springpadAttachment.getUrl());
                    mAttachment = StorageHelper.createAttachmentFromUri(this, uri, true);
                } catch (IOException e) {
                    Log.e(Constants.TAG, "Error retrieving Springpad online image");
                }
                if (mAttachment != null) {
                    note.addAttachment(mAttachment);
                }
                mAttachment = null;
            }

            // If the note has a category is added to the map to be post-processed
            if (springpadElement.getNotebooks().size() > 0) {
                note.setCategory(categoriesWithUuid.get(springpadElement.getNotebooks().get(0)));
            } else {
                note.setCategory(defaulCategory);
            }

            // The note is saved
            DbHelper.getInstance().updateNote(note, false);
            ReminderHelper.addReminder(IdealNote.getAppContext(), note);

            // Updating notification
            importedSpringpadNotes++;
            updateImportNotification(importer);
        }

        // Delete temp data
        try {
            importer.clean();
        } catch (IOException e) {
            Log.w(Constants.TAG, "Springpad import temp files not deleted");
        }

        String title = getString(com.dycody.android.idealnote.R.string.data_import_completed);
        String text = getString(com.dycody.android.idealnote.R.string.click_to_refresh_application);
        createNotification(intent, this, title, text, null);
    }

    private void updateImportNotification(Importer importer) {
        mNotificationsHelper.setMessage(importer.getNotebooksCount() + " "
                + getString(com.dycody.android.idealnote.R.string.categories) + " (" + importedSpringpadNotebooks
                + " " + getString(com.dycody.android.idealnote.R.string.imported) + "), "
                + +importer.getNotesCount() + " " + getString(com.dycody.android.idealnote.R.string.notes) + " ("
                + importedSpringpadNotes + " " + getString(com.dycody.android.idealnote.R.string.imported) + ")")
                .show();
    }

    synchronized private void deleteData(Intent intent) {

        // Gets backup folder
        String backupName = intent.getStringExtra(INTENT_BACKUP_NAME);
        File backupDir = StorageHelper.getBackupDir(backupName);

        // Backup directory removal
        StorageHelper.delete(this, backupDir.getAbsolutePath());

        String title = getString(com.dycody.android.idealnote.R.string.data_deletion_completed);
        String text = backupName + " " + getString(com.dycody.android.idealnote.R.string.deleted);
        createNotification(intent, this, title, text, backupDir);
    }

    /**
     * Creation of notification on operations completed
     */
    private void createNotification(Intent intent, Context mContext, String title, String message, File backupDir) {

        // The behavior differs depending on intent action
        Intent intentLaunch;
        if (DataBackupIntentService.ACTION_DATA_IMPORT.equals(intent.getAction())
                || DataBackupIntentService.ACTION_DATA_IMPORT_SPRINGPAD.equals(intent.getAction())) {
            intentLaunch = new Intent(mContext, MainActivity.class);
            intentLaunch.setAction(Constants.ACTION_RESTART_APP);
        } else {
            intentLaunch = new Intent();
        }
        // Add this bundle to the intent
        intentLaunch.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        intentLaunch.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        // Creates the PendingIntent
        PendingIntent notifyIntent = PendingIntent.getActivity(mContext, 0, intentLaunch,
                PendingIntent.FLAG_UPDATE_CURRENT);

        NotificationsHelper mNotificationsHelper = new NotificationsHelper(mContext);
        mNotificationsHelper
                .createNotification(com.dycody.android.idealnote.R.drawable.ic_content_save_white_24dp, title,
                        notifyIntent)
                .setMessage(message).setRingtone(prefs.getString("settings_notification_ringtone", null))
                .setLedActive();
        if (prefs.getBoolean("settings_notification_vibration", true))
            mNotificationsHelper.setVibration();
        mNotificationsHelper.show();
    }

    /**
     * Export database to backup folder
     *
     * @return True if success, false otherwise
     */
    private boolean exportDB(File backupDir) {
        File database = getDatabasePath(Constants.DATABASE_NAME);
        return (StorageHelper.copyFile(database, new File(backupDir, Constants.DATABASE_NAME)));
    }

    private void exportNotes(File backupDir) {
        for (Note note : DbHelper.getInstance().getAllNotes(false)) {
            File noteFile = new File(backupDir, String.valueOf(note.get_id()));
            try {
                FileUtils.write(noteFile, note.toJSON());
            } catch (IOException e) {
                Log.e(Constants.TAG, "Error backupping note: " + note.get_id());
            }
        }
    }

    /**
     * Export attachments to backup folder
     *
     * @return True if success, false otherwise
     */
    private boolean exportAttachments(File backupDir) {
        File attachmentsDir = StorageHelper.getAttachmentDir(this);
        File destinationattachmentsDir = new File(backupDir, attachmentsDir.getName());

        DbHelper db = DbHelper.getInstance();
        ArrayList<Attachment> list = db.getAllAttachments();

        int exported = 0;
        for (Attachment attachment : list) {
            StorageHelper.copyToBackupDir(destinationattachmentsDir, new File(attachment.getUri().getPath()));
            mNotificationsHelper
                    .setMessage(TextHelper.capitalize(getString(com.dycody.android.idealnote.R.string.attachment))
                            + " " + exported++ + "/" + list.size())
                    .show();
        }
        return true;
    }

    /**
     * Exports settings if required
     */
    private boolean exportSettings(File backupDir) {
        File preferences = StorageHelper.getSharedPreferencesFile(this);
        return (StorageHelper.copyFile(preferences, new File(backupDir, preferences.getName())));
    }

    /**
     * Imports settings
     */
    private boolean importSettings(File backupDir) {
        File preferences = StorageHelper.getSharedPreferencesFile(this);
        File preferenceBackup = new File(backupDir, preferences.getName());
        return (StorageHelper.copyFile(preferenceBackup, preferences));
    }

    /**
     * Schedules reminders
     */
    private void resetReminders() {
        Log.d(Constants.TAG, "Resettings reminders");
        for (Note note : DbHelper.getInstance().getNotesWithReminderNotFired()) {
            ReminderHelper.addReminder(IdealNote.getAppContext(), note);
        }
    }

    /**
     * Import database from backup folder
     */
    private boolean importDB(File backupDir) {
        File database = getDatabasePath(Constants.DATABASE_NAME);
        if (database.exists()) {
            database.delete();
        }
        return (StorageHelper.copyFile(new File(backupDir, Constants.DATABASE_NAME), database));
    }

    private void importNotes(File backupDir) {
        for (File file : FileUtils.listFiles(backupDir, new RegexFileFilter("\\d{13}"), TrueFileFilter.INSTANCE)) {
            try {
                Note note = new Note();
                note.buildFromJson(FileUtils.readFileToString(file));
                if (note.getCategory() != null) {
                    DbHelper.getInstance().updateCategory(note.getCategory());
                }
                for (Attachment attachment : note.getAttachmentsList()) {
                    DbHelper.getInstance().updateAttachment(attachment);
                }
                DbHelper.getInstance().updateNote(note, false);
            } catch (IOException e) {
                Log.e(Constants.TAG, "Error parsing note json");
            }
        }
    }

    /**
     * Import attachments from backup folder
     */
    private boolean importAttachments(File backupDir) {
        File attachmentsDir = StorageHelper.getAttachmentDir(this);
        // Moving back
        File backupAttachmentsDir = new File(backupDir, attachmentsDir.getName());
        if (!backupAttachmentsDir.exists())
            return true;
        boolean result = true;
        Collection list = FileUtils.listFiles(backupAttachmentsDir, FileFilterUtils.trueFileFilter(),
                TrueFileFilter.INSTANCE);
        Iterator i = list.iterator();
        int imported = 0;
        File file = null;
        while (i.hasNext()) {
            try {
                file = (File) i.next();
                FileUtils.copyFileToDirectory(file, attachmentsDir, true);
                mNotificationsHelper.setMessage(
                        TextHelper.capitalize(getString(com.dycody.android.idealnote.R.string.attachment)) + " "
                                + imported++ + "/" + list.size())
                        .show();
            } catch (IOException e) {
                result = false;
                Log.e(Constants.TAG, "Error importing the attachment " + file.getName());
            }
        }
        return result;
    }

    @Override
    public void onAttachingFileErrorOccurred(Attachment mAttachment) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onAttachingFileFinished(Attachment mAttachment) {
        // TODO Auto-generated method stub

    }

}