hku.fyp14017.blencode.ProjectManager.java Source code

Java tutorial

Introduction

Here is the source code for hku.fyp14017.blencode.ProjectManager.java

Source

/*
 * Catroid: An on-device visual programming system for Android devices
 * Copyright (C) 2010-2014 The Catrobat Team
 * (<http://developer.fyp14017.hku/credits>)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * An additional term exception under section 7 of the GNU Affero
 * General Public License, version 3, is available at
 * http://developer.fyp14017.hku/license_additional_term
 *
 * 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.hku/licenses/>.
 */
package hku.fyp14017.blencode;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import android.support.v4.app.FragmentActivity;
import android.util.Log;

import hku.fyp14017.blencode.common.Constants;
import hku.fyp14017.blencode.common.FileChecksumContainer;
import hku.fyp14017.blencode.common.MessageContainer;
import hku.fyp14017.blencode.common.ScreenModes;
import hku.fyp14017.blencode.common.StandardProjectHandler;
import hku.fyp14017.blencode.content.Project;
import hku.fyp14017.blencode.content.Script;
import hku.fyp14017.blencode.content.Sprite;
import hku.fyp14017.blencode.content.bricks.Brick;
import hku.fyp14017.blencode.content.bricks.IfLogicBeginBrick;
import hku.fyp14017.blencode.content.bricks.IfLogicElseBrick;
import hku.fyp14017.blencode.content.bricks.IfLogicEndBrick;
import hku.fyp14017.blencode.content.bricks.LoopBeginBrick;
import hku.fyp14017.blencode.content.bricks.LoopEndBrick;
import hku.fyp14017.blencode.content.bricks.UserBrick;
import hku.fyp14017.blencode.exceptions.CompatibilityProjectException;
import hku.fyp14017.blencode.exceptions.LoadingProjectException;
import hku.fyp14017.blencode.exceptions.OutdatedVersionProjectException;
import hku.fyp14017.blencode.io.LoadProjectTask;
import hku.fyp14017.blencode.io.LoadProjectTask.OnLoadProjectCompleteListener;
import hku.fyp14017.blencode.io.StorageHandler;
import hku.fyp14017.blencode.transfers.CheckTokenTask;
import hku.fyp14017.blencode.transfers.CheckTokenTask.OnCheckTokenCompleteListener;
import hku.fyp14017.blencode.ui.dialogs.LoginRegisterDialog;
import hku.fyp14017.blencode.ui.dialogs.UploadProjectDialog;
import hku.fyp14017.blencode.utils.Utils;
import hku.fyp14017.blencode.web.ServerCalls;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import hku.fyp14017.blencode.common.MessageContainer;

public final class ProjectManager implements OnLoadProjectCompleteListener, OnCheckTokenCompleteListener {
    private static final ProjectManager INSTANCE = new ProjectManager();
    private static final String TAG = ProjectManager.class.getSimpleName();

    private Project project;
    private Script currentScript;
    private Sprite currentSprite;
    private UserBrick currentUserBrick;
    private boolean asynchronTask = true;

    private FileChecksumContainer fileChecksumContainer = new FileChecksumContainer();

    private ProjectManager() {
    }

    public static ProjectManager getInstance() {
        return INSTANCE;
    }

    public void uploadProject(String projectName, FragmentActivity fragmentActivity) {
        if (getCurrentProject() == null || !getCurrentProject().getName().equals(projectName)) {
            LoadProjectTask loadProjectTask = new LoadProjectTask(fragmentActivity, projectName, false, false);
            loadProjectTask.setOnLoadProjectCompleteListener(this);
            loadProjectTask.execute();
        }
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(fragmentActivity);
        String token = preferences.getString(Constants.TOKEN, Constants.NO_TOKEN);
        String username = preferences.getString(Constants.USERNAME, Constants.NO_USERNAME);

        if (token.equals(Constants.NO_TOKEN) || token.length() != ServerCalls.TOKEN_LENGTH
                || token.equals(ServerCalls.TOKEN_CODE_INVALID)) {
            showLoginRegisterDialog(fragmentActivity);
        } else {
            CheckTokenTask checkTokenTask = new CheckTokenTask(fragmentActivity, token, username);
            checkTokenTask.setOnCheckTokenCompleteListener(this);
            checkTokenTask.execute();
        }
    }

    public void loadProject(String projectName, Context context)
            throws LoadingProjectException, OutdatedVersionProjectException, CompatibilityProjectException {
        fileChecksumContainer = new FileChecksumContainer();
        Project oldProject = project;
        MessageContainer.createBackup();
        project = StorageHandler.getInstance().loadProject(projectName);

        if (project == null) {
            if (oldProject != null) {
                project = oldProject;
                MessageContainer.restoreBackup();
            } else {
                project = Utils.findValidProject();
                if (project == null) {
                    try {
                        project = StandardProjectHandler.createAndSaveStandardProject(context);
                        MessageContainer.clearBackup();
                    } catch (IOException ioException) {
                        Log.e(TAG, "Cannot load project.", ioException);
                        throw new LoadingProjectException(context.getString(R.string.error_load_project));
                    }
                }
            }
            throw new LoadingProjectException(context.getString(R.string.error_load_project));
        } else if (project.getCatrobatLanguageVersion() > Constants.CURRENT_CATROBAT_LANGUAGE_VERSION) {
            project = oldProject;
            throw new OutdatedVersionProjectException(
                    context.getString(R.string.error_outdated_pocketcode_version));
        } else {
            if (project.getCatrobatLanguageVersion() == 0.8f) {
                //TODO insert in every "When project starts" script list a "show" brick
                project.setCatrobatLanguageVersion(0.9f);
            }
            if (project.getCatrobatLanguageVersion() == 0.9f) {
                project.setCatrobatLanguageVersion(0.91f);
                //no convertion needed - only change to white background
            }
            if (project.getCatrobatLanguageVersion() == 0.91f) {
                project.setCatrobatLanguageVersion(0.92f);
                project.setScreenMode(ScreenModes.STRETCH);
                checkNestingBrickReferences(false);
            }
            if (project.getCatrobatLanguageVersion() == 0.92f) {
                project.setCatrobatLanguageVersion(0.93f);
            }

            //insert further conversions here

            checkNestingBrickReferences(true);
            if (project.getCatrobatLanguageVersion() == Constants.CURRENT_CATROBAT_LANGUAGE_VERSION) {
                //project seems to be converted now and can be loaded
                localizeBackgroundSprite(context);
            } else {
                //project cannot be converted
                project = oldProject;
                throw new CompatibilityProjectException(context.getString(R.string.error_project_compatability));
            }
        }

    }

    private void localizeBackgroundSprite(Context context) {
        // Set generic localized name on background sprite and move it to the back.
        if (project.getSpriteList().size() > 0) {
            project.getSpriteList().get(0).setName(context.getString(R.string.background));
            project.getSpriteList().get(0).look.setZIndex(0);
        }
        MessageContainer.clearBackup();
        currentSprite = null;
        currentScript = null;
        Utils.saveToPreferences(context, Constants.PREF_PROJECTNAME_KEY, project.getName());
    }

    public boolean cancelLoadProject() {
        return StorageHandler.getInstance().cancelLoadProject();
    }

    public boolean canLoadProject(String projectName) {
        return StorageHandler.getInstance().loadProject(projectName) != null;
    }

    public void saveProject() {
        if (project == null) {
            return;
        }

        if (asynchronTask) {
            SaveProjectAsynchronousTask saveTask = new SaveProjectAsynchronousTask();
            saveTask.execute();
        } else {
            StorageHandler.getInstance().saveProject(project);
        }
    }

    public boolean initializeDefaultProject(Context context) {
        try {
            fileChecksumContainer = new FileChecksumContainer();
            project = StandardProjectHandler.createAndSaveEmptyProject("My first project", context);

            currentSprite = null;
            currentScript = null;
            return true;
        } catch (Exception ioException) {
            Log.e(TAG, "Cannot initialize default project.", ioException);
            Utils.showErrorDialog(context, R.string.error_load_project);
            return false;
        }
    }

    public boolean initializeDroneProject(Context context) {
        try {
            fileChecksumContainer = new FileChecksumContainer();
            project = StandardProjectHandler.createAndSaveStandardDroneProject(context);

            currentSprite = null;
            currentScript = null;
            return true;
        } catch (IOException ioException) {
            Log.e(TAG, "Cannot initialize default project.", ioException);
            Utils.showErrorDialog(context, R.string.error_load_project);
            return false;
        }
    }

    public void initializeNewProject(String projectName, Context context, boolean empty)
            throws IllegalArgumentException, IOException {
        fileChecksumContainer = new FileChecksumContainer();

        if (empty) {
            project = StandardProjectHandler.createAndSaveEmptyProject(projectName, context);
        } else {
            project = StandardProjectHandler.createAndSaveStandardProject(projectName, context);
        }

        currentSprite = null;
        currentScript = null;
    }

    public Project getCurrentProject() {
        return project;
    }

    public void setProject(Project project) {
        currentScript = null;
        currentSprite = null;

        this.project = project;
    }

    @Deprecated
    public void deleteCurrentProject(Context context) throws IllegalArgumentException, IOException {
        deleteProject(project.getName(), context);
    }

    public void deleteProject(String projectName, Context context) throws IllegalArgumentException, IOException {
        Log.d(TAG, "deleteProject " + projectName);
        if (StorageHandler.getInstance().projectExists(projectName)) {
            StorageHandler.getInstance().deleteProject(projectName);
        }

        if (project != null && project.getName().equals(projectName)) {
            Log.d(TAG, "deleteProject(): project instance set to null");

            project = null;

            if (context != null) {
                SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
                String currentProjectName = sharedPreferences.getString(Constants.PREF_PROJECTNAME_KEY, "notFound");
                if (!currentProjectName.equals("notFound")) {
                    Utils.removeFromPreferences(context, Constants.PREF_PROJECTNAME_KEY);
                }
            }
        }
    }

    public boolean renameProject(String newProjectName, Context context) {
        if (StorageHandler.getInstance().projectExists(newProjectName)) {
            Utils.showErrorDialog(context, R.string.error_project_exists);
            return false;
        }

        String oldProjectPath = Utils.buildProjectPath(project.getName());
        File oldProjectDirectory = new File(oldProjectPath);

        String newProjectPath = Utils.buildProjectPath(newProjectName);
        File newProjectDirectory = new File(newProjectPath);

        boolean directoryRenamed = false;

        if (oldProjectPath.equalsIgnoreCase(newProjectPath)) {
            String tmpProjectPath = Utils.buildProjectPath(createTemporaryDirectoryName(newProjectName));
            File tmpProjectDirectory = new File(tmpProjectPath);
            directoryRenamed = oldProjectDirectory.renameTo(tmpProjectDirectory);
            if (directoryRenamed) {
                directoryRenamed = tmpProjectDirectory.renameTo(newProjectDirectory);
            }
        } else {
            directoryRenamed = oldProjectDirectory.renameTo(newProjectDirectory);
        }

        if (directoryRenamed) {
            project.setName(newProjectName);
            saveProject();
        }

        if (!directoryRenamed) {
            Utils.showErrorDialog(context, R.string.error_rename_project);
        }

        return directoryRenamed;
    }

    public Sprite getCurrentSprite() {
        return currentSprite;
    }

    public void setCurrentSprite(Sprite sprite) {
        currentSprite = sprite;
    }

    public Script getCurrentScript() {
        return currentScript;
    }

    public void setCurrentScript(Script script) {
        if (script == null) {
            currentScript = null;
        } else if (currentSprite.getScriptIndex(script) != -1) {
            currentScript = script;
        }
    }

    public UserBrick getCurrentUserBrick() {
        return currentUserBrick;
    }

    public void setCurrentUserBrick(UserBrick brick) {
        currentUserBrick = brick;
    }

    public void addSprite(Sprite sprite) {
        project.addSprite(sprite);
    }

    public void addScript(Script script) {
        currentSprite.addScript(script);
    }

    public boolean spriteExists(String spriteName) {
        for (Sprite sprite : project.getSpriteList()) {
            if (sprite.getName().equalsIgnoreCase(spriteName)) {
                return true;
            }
        }
        return false;
    }

    public int getCurrentSpritePosition() {
        return project.getSpriteList().indexOf(currentSprite);
    }

    public int getCurrentScriptPosition() {
        int currentSpritePosition = this.getCurrentSpritePosition();
        if (currentSpritePosition == -1) {
            return -1;
        }

        return project.getSpriteList().get(currentSpritePosition).getScriptIndex(currentScript);
    }

    private String createTemporaryDirectoryName(String projectDirectoryName) {
        String temporaryDirectorySuffix = "_tmp";
        String temporaryDirectoryName = projectDirectoryName + temporaryDirectorySuffix;
        int suffixCounter = 0;
        while (Utils.checkIfProjectExistsOrIsDownloadingIgnoreCase(temporaryDirectoryName)) {
            temporaryDirectoryName = projectDirectoryName + temporaryDirectorySuffix + suffixCounter;
            suffixCounter++;
        }
        return temporaryDirectoryName;
    }

    public FileChecksumContainer getFileChecksumContainer() {
        return this.fileChecksumContainer;
    }

    public void setFileChecksumContainer(FileChecksumContainer fileChecksumContainer) {
        this.fileChecksumContainer = fileChecksumContainer;
    }

    @Override
    public void onTokenNotValid(FragmentActivity fragmentActivity) {
        showLoginRegisterDialog(fragmentActivity);
    }

    @Override
    public void onCheckTokenSuccess(FragmentActivity fragmentActivity) {
        UploadProjectDialog uploadProjectDialog = new UploadProjectDialog();
        uploadProjectDialog.show(fragmentActivity.getSupportFragmentManager(),
                UploadProjectDialog.DIALOG_FRAGMENT_TAG);
    }

    private void showLoginRegisterDialog(FragmentActivity fragmentActivity) {
        LoginRegisterDialog loginRegisterDialog = new LoginRegisterDialog();
        loginRegisterDialog.show(fragmentActivity.getSupportFragmentManager(),
                LoginRegisterDialog.DIALOG_FRAGMENT_TAG);
    }

    @Override
    public void onLoadProjectSuccess(boolean startProjectActivity) {

    }

    @Override
    public void onLoadProjectFailure() {

    }

    public void checkNestingBrickReferences(boolean assumeWrong) {
        Project currentProject = ProjectManager.getInstance().getCurrentProject();
        if (currentProject != null) {
            for (Sprite currentSprite : currentProject.getSpriteList()) {
                int numberOfScripts = currentSprite.getNumberOfScripts();
                for (int pos = 0; pos < numberOfScripts; pos++) {
                    Script script = currentSprite.getScript(pos);
                    boolean scriptCorrect = true;
                    if (assumeWrong) {
                        scriptCorrect = false;
                    }
                    for (Brick currentBrick : script.getBrickList()) {
                        if (!scriptCorrect) {
                            continue;
                        }
                        scriptCorrect = checkReferencesOfCurrentBrick(currentBrick);
                    }
                    if (!scriptCorrect) {
                        correctAllNestedReferences(script);
                    }
                }
            }
        }
    }

    private boolean checkReferencesOfCurrentBrick(Brick currentBrick) {
        if (currentBrick instanceof IfLogicBeginBrick) {
            IfLogicElseBrick elseBrick = ((IfLogicBeginBrick) currentBrick).getIfElseBrick();
            IfLogicEndBrick endBrick = ((IfLogicBeginBrick) currentBrick).getIfEndBrick();
            if (elseBrick == null || endBrick == null || elseBrick.getIfBeginBrick() == null
                    || elseBrick.getIfEndBrick() == null || endBrick.getIfBeginBrick() == null
                    || endBrick.getIfElseBrick() == null || !elseBrick.getIfBeginBrick().equals(currentBrick)
                    || !elseBrick.getIfEndBrick().equals(endBrick)
                    || !endBrick.getIfBeginBrick().equals(currentBrick)
                    || !endBrick.getIfElseBrick().equals(elseBrick)) {
                Log.d("REFERENCE ERROR!!", "Brick has wrong reference:" + currentSprite + " " + currentBrick);
                return false;
            }
        } else if (currentBrick instanceof LoopBeginBrick) {
            LoopEndBrick endBrick = ((LoopBeginBrick) currentBrick).getLoopEndBrick();
            if (endBrick == null || endBrick.getLoopBeginBrick() == null
                    || !endBrick.getLoopBeginBrick().equals(currentBrick)) {
                Log.d("REFERENCE ERROR!!", "Brick has wrong reference:" + currentSprite + " " + currentBrick);
                return false;
            }
        }
        return true;
    }

    private void correctAllNestedReferences(Script script) {
        ArrayList<IfLogicBeginBrick> ifBeginList = new ArrayList<IfLogicBeginBrick>();
        ArrayList<LoopBeginBrick> loopBeginList = new ArrayList<LoopBeginBrick>();
        for (Brick currentBrick : script.getBrickList()) {
            if (currentBrick instanceof IfLogicBeginBrick) {
                ifBeginList.add((IfLogicBeginBrick) currentBrick);
            } else if (currentBrick instanceof LoopBeginBrick) {
                loopBeginList.add((LoopBeginBrick) currentBrick);
            } else if (currentBrick instanceof LoopEndBrick) {
                LoopBeginBrick loopBeginBrick = loopBeginList.get(loopBeginList.size() - 1);
                loopBeginBrick.setLoopEndBrick((LoopEndBrick) currentBrick);
                ((LoopEndBrick) currentBrick).setLoopBeginBrick(loopBeginBrick);
                loopBeginList.remove(loopBeginBrick);
            } else if (currentBrick instanceof IfLogicElseBrick) {
                IfLogicBeginBrick ifBeginBrick = ifBeginList.get(ifBeginList.size() - 1);
                ifBeginBrick.setIfElseBrick((IfLogicElseBrick) currentBrick);
                ((IfLogicElseBrick) currentBrick).setIfBeginBrick(ifBeginBrick);
            } else if (currentBrick instanceof IfLogicEndBrick) {
                IfLogicBeginBrick ifBeginBrick = ifBeginList.get(ifBeginList.size() - 1);
                IfLogicElseBrick elseBrick = ifBeginBrick.getIfElseBrick();
                ifBeginBrick.setIfEndBrick((IfLogicEndBrick) currentBrick);
                elseBrick.setIfEndBrick((IfLogicEndBrick) currentBrick);
                ((IfLogicEndBrick) currentBrick).setIfBeginBrick(ifBeginBrick);
                ((IfLogicEndBrick) currentBrick).setIfElseBrick(elseBrick);
                ifBeginList.remove(ifBeginBrick);
            }
        }
    }

    private class SaveProjectAsynchronousTask extends AsyncTask<Void, Void, Void> {

        @Override
        protected Void doInBackground(Void... params) {
            StorageHandler.getInstance().saveProject(project);
            return null;
        }
    }
}