/* * Catroid: An on-device visual programming system for Android devices * Copyright (C) 2010-2016 The Catrobat Team * (<http://developer.catrobat.org/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.catrobat.org/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.org/licenses/>. */ package org.catrobat.catroid.io; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.BitmapFactory; import android.util.Log; import com.google.common.base.Charsets; import com.google.common.io.Files; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonIOException; import com.google.gson.JsonSyntaxException; import com.parrot.freeflight.utils.FileUtils; import com.thoughtworks.xstream.converters.reflection.FieldDictionary; import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider; import org.catrobat.catroid.ProjectManager; import org.catrobat.catroid.common.Backpack; import org.catrobat.catroid.common.Constants; import org.catrobat.catroid.common.DefaultProjectHandler; import org.catrobat.catroid.common.DroneVideoLookData; import org.catrobat.catroid.common.FileChecksumContainer; import org.catrobat.catroid.common.LookData; import org.catrobat.catroid.common.NfcTagData; import org.catrobat.catroid.common.SoundInfo; import org.catrobat.catroid.content.BroadcastScript; import org.catrobat.catroid.content.CollisionScript; import org.catrobat.catroid.content.GroupItemSprite; import org.catrobat.catroid.content.GroupSprite; import org.catrobat.catroid.content.LegoNXTSetting; import org.catrobat.catroid.content.Project; import org.catrobat.catroid.content.RaspiInterruptScript; import org.catrobat.catroid.content.Scene; import org.catrobat.catroid.content.Script; import org.catrobat.catroid.content.Setting; import org.catrobat.catroid.content.SingleSprite; import org.catrobat.catroid.content.Sprite; import org.catrobat.catroid.content.StartScript; import org.catrobat.catroid.content.SupportProject; import org.catrobat.catroid.content.WhenBackgroundChangesScript; import org.catrobat.catroid.content.WhenClonedScript; import org.catrobat.catroid.content.WhenConditionScript; import org.catrobat.catroid.content.WhenNfcScript; import org.catrobat.catroid.content.WhenScript; import org.catrobat.catroid.content.WhenTouchDownScript; import org.catrobat.catroid.content.XmlHeader; import org.catrobat.catroid.content.bricks.AddItemToUserListBrick; import org.catrobat.catroid.content.bricks.ArduinoSendDigitalValueBrick; import org.catrobat.catroid.content.bricks.ArduinoSendPWMValueBrick; import org.catrobat.catroid.content.bricks.AskBrick; import org.catrobat.catroid.content.bricks.AskSpeechBrick; import org.catrobat.catroid.content.bricks.Brick; import org.catrobat.catroid.content.bricks.BrickBaseType; import org.catrobat.catroid.content.bricks.BroadcastBrick; import org.catrobat.catroid.content.bricks.BroadcastReceiverBrick; import org.catrobat.catroid.content.bricks.BroadcastWaitBrick; import org.catrobat.catroid.content.bricks.CameraBrick; import org.catrobat.catroid.content.bricks.ChangeBrightnessByNBrick; import org.catrobat.catroid.content.bricks.ChangeColorByNBrick; import org.catrobat.catroid.content.bricks.ChangeSizeByNBrick; import org.catrobat.catroid.content.bricks.ChangeTransparencyByNBrick; import org.catrobat.catroid.content.bricks.ChangeVariableBrick; import org.catrobat.catroid.content.bricks.ChangeVolumeByNBrick; import org.catrobat.catroid.content.bricks.ChangeXByNBrick; import org.catrobat.catroid.content.bricks.ChangeYByNBrick; import org.catrobat.catroid.content.bricks.ChooseCameraBrick; import org.catrobat.catroid.content.bricks.ClearBackgroundBrick; import org.catrobat.catroid.content.bricks.ClearGraphicEffectBrick; import org.catrobat.catroid.content.bricks.CloneBrick; import org.catrobat.catroid.content.bricks.ComeToFrontBrick; import org.catrobat.catroid.content.bricks.DeleteItemOfUserListBrick; import org.catrobat.catroid.content.bricks.DeleteThisCloneBrick; import org.catrobat.catroid.content.bricks.DroneEmergencyBrick; import org.catrobat.catroid.content.bricks.DroneFlipBrick; import org.catrobat.catroid.content.bricks.DroneMoveBackwardBrick; import org.catrobat.catroid.content.bricks.DroneMoveDownBrick; import org.catrobat.catroid.content.bricks.DroneMoveForwardBrick; import org.catrobat.catroid.content.bricks.DroneMoveLeftBrick; import org.catrobat.catroid.content.bricks.DroneMoveRightBrick; import org.catrobat.catroid.content.bricks.DroneMoveUpBrick; import org.catrobat.catroid.content.bricks.DronePlayLedAnimationBrick; import org.catrobat.catroid.content.bricks.DroneSwitchCameraBrick; import org.catrobat.catroid.content.bricks.DroneTakeOffLandBrick; import org.catrobat.catroid.content.bricks.DroneTurnLeftBrick; import org.catrobat.catroid.content.bricks.DroneTurnRightBrick; import org.catrobat.catroid.content.bricks.FlashBrick; import org.catrobat.catroid.content.bricks.ForeverBrick; import org.catrobat.catroid.content.bricks.FormulaBrick; import org.catrobat.catroid.content.bricks.GlideToBrick; import org.catrobat.catroid.content.bricks.GoNStepsBackBrick; import org.catrobat.catroid.content.bricks.GoToBrick; import org.catrobat.catroid.content.bricks.HideBrick; import org.catrobat.catroid.content.bricks.HideTextBrick; import org.catrobat.catroid.content.bricks.IfLogicBeginBrick; import org.catrobat.catroid.content.bricks.IfLogicElseBrick; import org.catrobat.catroid.content.bricks.IfLogicEndBrick; import org.catrobat.catroid.content.bricks.IfOnEdgeBounceBrick; import org.catrobat.catroid.content.bricks.IfThenLogicBeginBrick; import org.catrobat.catroid.content.bricks.IfThenLogicEndBrick; import org.catrobat.catroid.content.bricks.InsertItemIntoUserListBrick; import org.catrobat.catroid.content.bricks.LegoEv3MotorMoveBrick; import org.catrobat.catroid.content.bricks.LegoEv3MotorStopBrick; import org.catrobat.catroid.content.bricks.LegoEv3PlayToneBrick; import org.catrobat.catroid.content.bricks.LegoEv3SetLedBrick; import org.catrobat.catroid.content.bricks.LegoNxtMotorMoveBrick; import org.catrobat.catroid.content.bricks.LegoNxtMotorStopBrick; import org.catrobat.catroid.content.bricks.LegoNxtMotorTurnAngleBrick; import org.catrobat.catroid.content.bricks.LegoNxtPlayToneBrick; import org.catrobat.catroid.content.bricks.LoopBeginBrick; import org.catrobat.catroid.content.bricks.LoopEndBrick; import org.catrobat.catroid.content.bricks.LoopEndlessBrick; import org.catrobat.catroid.content.bricks.MoveNStepsBrick; import org.catrobat.catroid.content.bricks.NextLookBrick; import org.catrobat.catroid.content.bricks.NoteBrick; import org.catrobat.catroid.content.bricks.PenDownBrick; import org.catrobat.catroid.content.bricks.PenUpBrick; import org.catrobat.catroid.content.bricks.PhiroIfLogicBeginBrick; import org.catrobat.catroid.content.bricks.PhiroMotorMoveBackwardBrick; import org.catrobat.catroid.content.bricks.PhiroMotorMoveForwardBrick; import org.catrobat.catroid.content.bricks.PhiroMotorStopBrick; import org.catrobat.catroid.content.bricks.PhiroPlayToneBrick; import org.catrobat.catroid.content.bricks.PhiroRGBLightBrick; import org.catrobat.catroid.content.bricks.PlaceAtBrick; import org.catrobat.catroid.content.bricks.PlaySoundAndWaitBrick; import org.catrobat.catroid.content.bricks.PlaySoundBrick; import org.catrobat.catroid.content.bricks.PointInDirectionBrick; import org.catrobat.catroid.content.bricks.PointToBrick; import org.catrobat.catroid.content.bricks.PreviousLookBrick; import org.catrobat.catroid.content.bricks.RaspiIfLogicBeginBrick; import org.catrobat.catroid.content.bricks.RaspiPwmBrick; import org.catrobat.catroid.content.bricks.RaspiSendDigitalValueBrick; import org.catrobat.catroid.content.bricks.RepeatBrick; import org.catrobat.catroid.content.bricks.RepeatUntilBrick; import org.catrobat.catroid.content.bricks.ReplaceItemInUserListBrick; import org.catrobat.catroid.content.bricks.SayBubbleBrick; import org.catrobat.catroid.content.bricks.SayForBubbleBrick; import org.catrobat.catroid.content.bricks.SceneStartBrick; import org.catrobat.catroid.content.bricks.SceneTransitionBrick; import org.catrobat.catroid.content.bricks.SetBackgroundAndWaitBrick; import org.catrobat.catroid.content.bricks.SetBackgroundBrick; import org.catrobat.catroid.content.bricks.SetBrightnessBrick; import org.catrobat.catroid.content.bricks.SetColorBrick; import org.catrobat.catroid.content.bricks.SetLookBrick; import org.catrobat.catroid.content.bricks.SetNfcTagBrick; import org.catrobat.catroid.content.bricks.SetPenColorBrick; import org.catrobat.catroid.content.bricks.SetPenSizeBrick; import org.catrobat.catroid.content.bricks.SetRotationStyleBrick; import org.catrobat.catroid.content.bricks.SetSizeToBrick; import org.catrobat.catroid.content.bricks.SetTransparencyBrick; import org.catrobat.catroid.content.bricks.SetVariableBrick; import org.catrobat.catroid.content.bricks.SetVolumeToBrick; import org.catrobat.catroid.content.bricks.SetXBrick; import org.catrobat.catroid.content.bricks.SetYBrick; import org.catrobat.catroid.content.bricks.ShowBrick; import org.catrobat.catroid.content.bricks.ShowTextBrick; import org.catrobat.catroid.content.bricks.SpeakAndWaitBrick; import org.catrobat.catroid.content.bricks.SpeakBrick; import org.catrobat.catroid.content.bricks.StampBrick; import org.catrobat.catroid.content.bricks.StopAllSoundsBrick; import org.catrobat.catroid.content.bricks.StopScriptBrick; import org.catrobat.catroid.content.bricks.ThinkBubbleBrick; import org.catrobat.catroid.content.bricks.ThinkForBubbleBrick; import org.catrobat.catroid.content.bricks.TurnLeftBrick; import org.catrobat.catroid.content.bricks.TurnRightBrick; import org.catrobat.catroid.content.bricks.UserBrick; import org.catrobat.catroid.content.bricks.UserBrickParameter; import org.catrobat.catroid.content.bricks.UserListBrick; import org.catrobat.catroid.content.bricks.UserScriptDefinitionBrick; import org.catrobat.catroid.content.bricks.UserScriptDefinitionBrickElement; import org.catrobat.catroid.content.bricks.UserVariableBrick; import org.catrobat.catroid.content.bricks.VibrationBrick; import org.catrobat.catroid.content.bricks.WaitBrick; import org.catrobat.catroid.content.bricks.WaitUntilBrick; import org.catrobat.catroid.content.bricks.WhenBackgroundChangesBrick; import org.catrobat.catroid.content.bricks.WhenBrick; import org.catrobat.catroid.content.bricks.WhenClonedBrick; import org.catrobat.catroid.content.bricks.WhenConditionBrick; import org.catrobat.catroid.content.bricks.WhenNfcBrick; import org.catrobat.catroid.content.bricks.WhenStartedBrick; import org.catrobat.catroid.formulaeditor.DataContainer; import org.catrobat.catroid.formulaeditor.SupportDataContainer; import org.catrobat.catroid.formulaeditor.UserList; import org.catrobat.catroid.formulaeditor.UserVariable; import org.catrobat.catroid.physics.content.bricks.CollisionReceiverBrick; import org.catrobat.catroid.physics.content.bricks.SetBounceBrick; import org.catrobat.catroid.physics.content.bricks.SetFrictionBrick; import org.catrobat.catroid.physics.content.bricks.SetGravityBrick; import org.catrobat.catroid.physics.content.bricks.SetMassBrick; import org.catrobat.catroid.physics.content.bricks.SetPhysicsObjectTypeBrick; import org.catrobat.catroid.physics.content.bricks.SetVelocityBrick; import org.catrobat.catroid.physics.content.bricks.TurnLeftSpeedBrick; import org.catrobat.catroid.physics.content.bricks.TurnRightSpeedBrick; import org.catrobat.catroid.stage.StageListener; import org.catrobat.catroid.utils.ImageEditing; import org.catrobat.catroid.utils.UtilFile; import org.catrobat.catroid.utils.Utils; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.nio.channels.FileChannel; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import static junit.framework.Assert.assertTrue; import static org.catrobat.catroid.common.Constants.BACKPACK_DIRECTORY; import static org.catrobat.catroid.common.Constants.BACKPACK_IMAGE_DIRECTORY; import static org.catrobat.catroid.common.Constants.BACKPACK_SOUND_DIRECTORY; import static org.catrobat.catroid.common.Constants.DEFAULT_ROOT; import static org.catrobat.catroid.common.Constants.IMAGE_DIRECTORY; import static org.catrobat.catroid.common.Constants.NO_MEDIA_FILE; import static org.catrobat.catroid.common.Constants.PROJECTCODE_NAME; import static org.catrobat.catroid.common.Constants.PROJECTCODE_NAME_TMP; import static org.catrobat.catroid.common.Constants.PROJECTPERMISSIONS_NAME; import static org.catrobat.catroid.common.Constants.SCENES_DIRECTORY; import static org.catrobat.catroid.common.Constants.SCENES_ENABLED_TAG; import static org.catrobat.catroid.common.Constants.SOUND_DIRECTORY; import static org.catrobat.catroid.utils.Utils.buildPath; import static org.catrobat.catroid.utils.Utils.buildProjectPath; import static org.catrobat.catroid.utils.Utils.buildScenePath; public final class StorageHandler { private static final StorageHandler INSTANCE; private static final String TAG = StorageHandler.class.getSimpleName(); private static final String XML_HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>\n"; private static final int JPG_COMPRESSION_SETTING = 95; public static final String BACKPACK_FILENAME = "backpack.json"; private BackwardCompatibleCatrobatLanguageXStream xstream; private Gson backpackGson; private FileInputStream fileInputStream; private File backPackSoundDirectory; private File backPackImageDirectory; private Lock loadSaveLock = new ReentrantLock(); // TODO: Since the StorageHandler constructor throws an exception, the member INSTANCE couldn't be assigned // directly and therefore we need this static block. Should be refactored and removed in the future. static { try { INSTANCE = new StorageHandler(); } catch (IOException ioException) { throw new RuntimeException("Initialize StorageHandler failed"); } } private StorageHandler() throws IOException { prepareProgramXstream(false); prepareBackpackGson(); if (!Utils.externalStorageAvailable()) { throw new IOException("Could not read external storage"); } createCatroidRoot(); } public static StorageHandler getInstance() { return INSTANCE; } private void prepareProgramXstream(boolean forSupportProject) { xstream = new BackwardCompatibleCatrobatLanguageXStream(new PureJavaReflectionProvider(new FieldDictionary(new CatroidFieldKeySorter()))); if (forSupportProject) { xstream.processAnnotations(SupportProject.class); xstream.processAnnotations(SupportDataContainer.class); } else { xstream.processAnnotations(Project.class); xstream.processAnnotations(DataContainer.class); } xstream.processAnnotations(Scene.class); xstream.processAnnotations(Sprite.class); xstream.processAnnotations(XmlHeader.class); xstream.processAnnotations(Setting.class); xstream.processAnnotations(UserVariableBrick.class); xstream.processAnnotations(UserListBrick.class); xstream.registerConverter(new XStreamConcurrentFormulaHashMapConverter()); xstream.registerConverter(new XStreamUserVariableConverter()); xstream.registerConverter(new XStreamBrickConverter(xstream.getMapper(), xstream.getReflectionProvider())); xstream.registerConverter(new XStreamScriptConverter(xstream.getMapper(), xstream.getReflectionProvider())); xstream.registerConverter(new XStreamSpriteConverter(xstream.getMapper(), xstream.getReflectionProvider())); xstream.registerConverter(new XStreamSettingConverter(xstream.getMapper(), xstream.getReflectionProvider())); xstream.omitField(CameraBrick.class, "spinnerValues"); xstream.omitField(ChooseCameraBrick.class, "spinnerValues"); xstream.omitField(FlashBrick.class, "spinnerValues"); xstream.omitField(StopScriptBrick.class, "spinnerValue"); setProgramXstreamAliases(); } private void prepareBackpackGson() { GsonBuilder gsonBuilder = new GsonBuilder().enableComplexMapKeySerialization().setPrettyPrinting(); gsonBuilder.registerTypeAdapter(Script.class, new BackpackScriptSerializerAndDeserializer()); gsonBuilder.registerTypeAdapter(Brick.class, new BackpackBrickSerializerAndDeserializer()); backpackGson = gsonBuilder.create(); } public static void saveBitmapToImageFile(File outputFile, Bitmap bitmap) throws FileNotFoundException { FileOutputStream outputStream = new FileOutputStream(outputFile); try { if (outputFile.getName().toLowerCase(Locale.US).endsWith(".jpg") || outputFile.getName().toLowerCase(Locale.US).endsWith(".jpeg")) { bitmap.compress(CompressFormat.JPEG, JPG_COMPRESSION_SETTING, outputStream); } else { bitmap.compress(CompressFormat.PNG, 0, outputStream); } outputStream.flush(); } catch (IOException ioException) { Log.e(TAG, Log.getStackTraceString(ioException)); } finally { try { outputStream.close(); } catch (IOException e) { Log.e(TAG, "Could not close outputstream.", e); } } } private void setProgramXstreamAliases() { xstream.alias("look", LookData.class); xstream.alias("droneLook", DroneVideoLookData.class); xstream.alias("sound", SoundInfo.class); xstream.alias("nfcTag", NfcTagData.class); xstream.alias("userVariable", UserVariable.class); xstream.alias("userList", UserList.class); xstream.alias("script", Script.class); xstream.alias("object", Sprite.class); xstream.alias("object", SingleSprite.class); xstream.alias("object", GroupSprite.class); xstream.alias("object", GroupItemSprite.class); xstream.alias("script", StartScript.class); xstream.alias("script", WhenClonedScript.class); xstream.alias("script", WhenScript.class); xstream.alias("script", WhenConditionScript.class); xstream.alias("script", WhenNfcScript.class); xstream.alias("script", BroadcastScript.class); xstream.alias("script", RaspiInterruptScript.class); xstream.alias("script", WhenTouchDownScript.class); xstream.alias("script", WhenBackgroundChangesScript.class); xstream.alias("brick", AddItemToUserListBrick.class); xstream.alias("brick", AskBrick.class); xstream.alias("brick", AskSpeechBrick.class); xstream.alias("brick", BroadcastBrick.class); xstream.alias("brick", BroadcastReceiverBrick.class); xstream.alias("brick", BroadcastWaitBrick.class); xstream.alias("brick", ChangeBrightnessByNBrick.class); xstream.alias("brick", ChangeColorByNBrick.class); xstream.alias("brick", ChangeTransparencyByNBrick.class); xstream.alias("brick", ChangeSizeByNBrick.class); xstream.alias("brick", ChangeVariableBrick.class); xstream.alias("brick", ChangeVolumeByNBrick.class); xstream.alias("brick", ChangeXByNBrick.class); xstream.alias("brick", ChangeYByNBrick.class); xstream.alias("brick", ClearBackgroundBrick.class); xstream.alias("brick", ClearGraphicEffectBrick.class); xstream.alias("brick", CloneBrick.class); xstream.alias("brick", ComeToFrontBrick.class); xstream.alias("brick", DeleteItemOfUserListBrick.class); xstream.alias("brick", DeleteThisCloneBrick.class); xstream.alias("brick", ForeverBrick.class); xstream.alias("brick", GlideToBrick.class); xstream.alias("brick", GoNStepsBackBrick.class); xstream.alias("brick", HideBrick.class); xstream.alias("brick", HideTextBrick.class); xstream.alias("brick", IfLogicBeginBrick.class); xstream.alias("brick", IfLogicElseBrick.class); xstream.alias("brick", IfLogicEndBrick.class); xstream.alias("brick", IfThenLogicBeginBrick.class); xstream.alias("brick", IfThenLogicEndBrick .class); xstream.alias("brick", IfOnEdgeBounceBrick.class); xstream.alias("brick", InsertItemIntoUserListBrick.class); xstream.alias("brick", FlashBrick.class); xstream.alias("brick", ChooseCameraBrick.class); xstream.alias("brick", CameraBrick.class); xstream.alias("brick", LegoNxtMotorMoveBrick.class); xstream.alias("brick", LegoNxtMotorStopBrick.class); xstream.alias("brick", LegoNxtMotorTurnAngleBrick.class); xstream.alias("brick", LegoNxtPlayToneBrick.class); xstream.alias("brick", LoopBeginBrick.class); xstream.alias("brick", LoopEndBrick.class); xstream.alias("brick", LoopEndlessBrick.class); xstream.alias("brick", MoveNStepsBrick.class); xstream.alias("brick", NextLookBrick.class); xstream.alias("brick", NoteBrick.class); xstream.alias("brick", PenDownBrick.class); xstream.alias("brick", PenUpBrick.class); xstream.alias("brick", PlaceAtBrick.class); xstream.alias("brick", GoToBrick.class); xstream.alias("brick", PlaySoundBrick.class); xstream.alias("brick", PlaySoundAndWaitBrick.class); xstream.alias("brick", PointInDirectionBrick.class); xstream.alias("brick", PointToBrick.class); xstream.alias("brick", PreviousLookBrick.class); xstream.alias("brick", RepeatBrick.class); xstream.alias("brick", RepeatUntilBrick.class); xstream.alias("brick", ReplaceItemInUserListBrick.class); xstream.alias("brick", SceneTransitionBrick.class); xstream.alias("brick", SceneStartBrick.class); xstream.alias("brick", SetBrightnessBrick.class); xstream.alias("brick", SetColorBrick.class); xstream.alias("brick", SetTransparencyBrick.class); xstream.alias("brick", SetLookBrick.class); xstream.alias("brick", SetBackgroundBrick.class); xstream.alias("brick", SetBackgroundAndWaitBrick.class); xstream.alias("brick", SetPenColorBrick.class); xstream.alias("brick", SetPenSizeBrick.class); xstream.alias("brick", SetRotationStyleBrick.class); xstream.alias("brick", SetSizeToBrick.class); xstream.alias("brick", SetVariableBrick.class); xstream.alias("brick", SetVolumeToBrick.class); xstream.alias("brick", SetXBrick.class); xstream.alias("brick", SetYBrick.class); xstream.alias("brick", ShowBrick.class); xstream.alias("brick", ShowTextBrick.class); xstream.alias("brick", SpeakBrick.class); xstream.alias("brick", SpeakAndWaitBrick.class); xstream.alias("brick", StampBrick.class); xstream.alias("brick", StopAllSoundsBrick.class); xstream.alias("brick", ThinkBubbleBrick.class); xstream.alias("brick", SayBubbleBrick.class); xstream.alias("brick", ThinkForBubbleBrick.class); xstream.alias("brick", SayForBubbleBrick.class); xstream.alias("brick", TurnLeftBrick.class); xstream.alias("brick", TurnRightBrick.class); xstream.alias("brick", UserBrick.class); xstream.alias("brick", UserScriptDefinitionBrick.class); xstream.alias("brick", VibrationBrick.class); xstream.alias("brick", WaitBrick.class); xstream.alias("brick", WaitUntilBrick.class); xstream.alias("brick", WhenBrick.class); xstream.alias("brick", WhenConditionBrick.class); xstream.alias("brick", WhenBackgroundChangesBrick.class); xstream.alias("brick", WhenStartedBrick.class); xstream.alias("brick", WhenClonedBrick.class); xstream.alias("brick", StopScriptBrick.class); xstream.alias("brick", WhenNfcBrick.class); xstream.alias("brick", SetNfcTagBrick.class); xstream.alias("brick", DronePlayLedAnimationBrick.class); xstream.alias("brick", DroneFlipBrick.class); xstream.alias("brick", DroneTakeOffLandBrick.class); xstream.alias("brick", DroneMoveForwardBrick.class); xstream.alias("brick", DroneMoveBackwardBrick.class); xstream.alias("brick", DroneMoveUpBrick.class); xstream.alias("brick", DroneMoveDownBrick.class); xstream.alias("brick", DroneMoveLeftBrick.class); xstream.alias("brick", DroneMoveRightBrick.class); xstream.alias("brick", DroneTurnLeftBrick.class); xstream.alias("brick", DroneTurnRightBrick.class); xstream.alias("brick", DroneSwitchCameraBrick.class); xstream.alias("brick", DroneEmergencyBrick.class); xstream.alias("brick", PhiroMotorMoveBackwardBrick.class); xstream.alias("brick", PhiroMotorMoveForwardBrick.class); xstream.alias("brick", PhiroMotorStopBrick.class); xstream.alias("brick", PhiroPlayToneBrick.class); xstream.alias("brick", PhiroRGBLightBrick.class); xstream.alias("brick", PhiroIfLogicBeginBrick.class); xstream.alias("brick", LegoEv3PlayToneBrick.class); xstream.alias("brick", LegoEv3MotorMoveBrick.class); xstream.alias("brick", LegoEv3MotorStopBrick.class); xstream.alias("brick", LegoEv3SetLedBrick.class); xstream.alias("brick", ArduinoSendPWMValueBrick.class); xstream.alias("brick", ArduinoSendDigitalValueBrick.class); xstream.alias("brick", RaspiSendDigitalValueBrick.class); xstream.alias("brick", RaspiIfLogicBeginBrick.class); xstream.alias("brick", RaspiPwmBrick.class); xstream.alias("userBrickElement", UserScriptDefinitionBrickElement.class); xstream.alias("userBrickParameter", UserBrickParameter.class); // Physics Script xstream.alias("script", CollisionScript.class); // Physics Bricks xstream.alias("brick", CollisionReceiverBrick.class); xstream.alias("brick", SetBounceBrick.class); xstream.alias("brick", SetFrictionBrick.class); xstream.alias("brick", SetGravityBrick.class); xstream.alias("brick", SetMassBrick.class); xstream.alias("brick", SetPhysicsObjectTypeBrick.class); xstream.alias("brick", SetVelocityBrick.class); xstream.alias("brick", TurnLeftSpeedBrick.class); xstream.alias("brick", TurnRightSpeedBrick.class); xstream.alias("setting", LegoNXTSetting.class); xstream.alias("nxtPort", LegoNXTSetting.NXTPort.class); xstream.aliasAttribute(LegoNXTSetting.NXTPort.class, "number", "number"); xstream.aliasField("formulaList", FormulaBrick.class, "formulaMap"); xstream.aliasField("object", BrickBaseType.class, "sprite"); } private void createCatroidRoot() { File catroidRoot = new File(DEFAULT_ROOT); if (!catroidRoot.exists()) { catroidRoot.mkdirs(); } try { createBackPackFileStructure(); } catch (IOException e) { Log.e(TAG, "Creating backpack file structure failed"); } } private boolean checkIfProjectHasScenes(String projectName) throws IOException { File projectXmlFile = new File(buildProjectPath(projectName), PROJECTCODE_NAME); String projectXml = Files.toString(projectXmlFile, Charsets.UTF_8); return projectXml.contains(SCENES_ENABLED_TAG); } public String getFirstSceneName(String projectName) { try { if (!checkIfProjectHasScenes(projectName)) { return null; } } catch (IOException e) { Log.e(TAG, "Exception getFirstSceneName", e); return null; } File projectXmlFile = new File(buildProjectPath(projectName), PROJECTCODE_NAME); String projectXml; try { projectXml = Files.toString(projectXmlFile, Charsets.UTF_8); } catch (IOException e) { Log.e(TAG, "Exception getFirstSceneName", e); return null; } int start = projectXml.indexOf("<scene>"); int end = projectXml.indexOf("</name>", start); int lengthOfSceneAndNameTags = 20; return projectXml.substring(start + lengthOfSceneAndNameTags, end); } public Project loadProject(String projectName, Context context) { File file = new File(DEFAULT_ROOT); if (!file.exists()) { Log.d(TAG, "Directory does not exist!"); return null; } try { if (!checkIfProjectHasScenes(projectName)) { return loadSupportProject(projectName, context); } } catch (IOException e) { Log.e(TAG, "Could not check Scene Tag!", e); return null; } assertTrue(codeFileSanityCheck(projectName)); Log.d(TAG, "loadProject " + projectName); if (!projectExists(projectName)) { return null; } loadSaveLock.lock(); Project project; try { project = (Project) xstream.getProjectFromXML(new File(buildProjectPath(projectName), PROJECTCODE_NAME)); for (String sceneName : project.getSceneOrder()) { project.getSceneByName(sceneName).setProject(project); project.getSceneByName(sceneName).getDataContainer().setProject(project); } } catch (Exception e) { Log.e(TAG, "Could not get Project from xml and get Scene from order", e); loadSaveLock.unlock(); return null; } loadSaveLock.unlock(); return project; } public static void copyDirectory(File destinationFile, File sourceFile) throws IOException { if (!sourceFile.exists()) { Log.e(TAG, "copyDirectory: sourceFile does not exist: " + sourceFile.getAbsolutePath()); return; } if (sourceFile.isDirectory()) { destinationFile.mkdirs(); for (String subDirectoryName : sourceFile.list()) { copyDirectory(new File(destinationFile, subDirectoryName), new File(sourceFile, subDirectoryName)); } } else { UtilFile.copyFile(destinationFile, sourceFile); } } private void fixFolderStructureForSupportProject(String projectName, String sceneName) throws IOException { String projectPath = buildProjectPath(projectName); String scenePath = buildScenePath(projectName, sceneName); File looksDirectory = new File(buildPath(projectPath, IMAGE_DIRECTORY)); File soundsDirectory = new File(buildPath(projectPath, SOUND_DIRECTORY)); File sceneDirectoryLooks = new File(buildPath(scenePath, IMAGE_DIRECTORY)); File sceneDirectorySounds = new File(buildPath(scenePath, SOUND_DIRECTORY)); File automaticScreenshot = new File(projectPath, StageListener.SCREENSHOT_AUTOMATIC_FILE_NAME); File manualScreenshot = new File(projectPath, StageListener.SCREENSHOT_MANUAL_FILE_NAME); File sceneAutomaticScreenshot = new File(scenePath, StageListener.SCREENSHOT_AUTOMATIC_FILE_NAME); File sceneManualScreenshot = new File(scenePath, StageListener.SCREENSHOT_MANUAL_FILE_NAME); copyDirectory(sceneDirectoryLooks, looksDirectory); copyDirectory(sceneDirectorySounds, soundsDirectory); if (automaticScreenshot.exists()) { FileUtils.copyFileToDir(automaticScreenshot, sceneAutomaticScreenshot); automaticScreenshot.delete(); } if (manualScreenshot.exists()) { FileUtils.copyFileToDir(manualScreenshot, sceneManualScreenshot); manualScreenshot.delete(); } UtilFile.deleteDirectory(looksDirectory); UtilFile.deleteDirectory(soundsDirectory); } public Project loadSupportProject(String projectName, Context context) { File file = new File(DEFAULT_ROOT); if (!file.exists()) { Log.d(TAG, "Directory does not exist!"); return null; } assertTrue(codeFileSanityCheck(projectName)); Log.d(TAG, "loadSupportProject " + projectName); if (!projectExists(projectName)) { return null; } loadSaveLock.lock(); try { File projectCodeFile = new File(buildProjectPath(projectName), PROJECTCODE_NAME); fileInputStream = new FileInputStream(projectCodeFile); prepareProgramXstream(true); SupportProject supportProject = (SupportProject) xstream.getProjectFromXML(projectCodeFile); prepareProgramXstream(false); Project project = new Project(supportProject, context); fixFolderStructureForSupportProject(projectName, project.getDefaultScene().getName()); return project; } catch (IOException e) { Log.d(TAG, "Could not load project!"); UtilFile.deleteDirectory(file); Log.d(TAG, "loadProject: directory is deleted and " + "default project should be restored!"); Log.e(TAG, "Exception: ", e); return null; } finally { if (fileInputStream != null) { try { fileInputStream.close(); } catch (IOException ioException) { Log.e(TAG, "can't close fileStream.", ioException); } } loadSaveLock.unlock(); } } public boolean cancelLoadProject() { if (fileInputStream != null) { try { fileInputStream.close(); return true; } catch (IOException ioException) { Log.e(TAG, "can't close fileStream.", ioException); } } return false; } public boolean saveProject(Project project) { BufferedWriter writer = null; if (project == null) { Log.d(TAG, "project is null!"); return false; } Log.d(TAG, "saveProject " + project.getName()); codeFileSanityCheck(project.getName()); loadSaveLock.lock(); String projectXml; File tmpCodeFile = null; File currentCodeFile = null; try { projectXml = XML_HEADER.concat(xstream.toXML(project)); tmpCodeFile = new File(buildProjectPath(project.getName()), PROJECTCODE_NAME_TMP); currentCodeFile = new File(buildProjectPath(project.getName()), PROJECTCODE_NAME); if (currentCodeFile.exists()) { try { String oldProjectXml = Files.toString(currentCodeFile, Charsets.UTF_8); if (oldProjectXml.equals(projectXml)) { Log.d(TAG, "Project version is the same. Do not update " + currentCodeFile.getName()); return false; } Log.d(TAG, "Project version differ <" + oldProjectXml.length() + "> <" + projectXml.length() + ">. update " + currentCodeFile.getName()); } catch (Exception exception) { Log.e(TAG, "Opening old project " + currentCodeFile.getAbsolutePath() + " failed.", exception); return false; } } createProjectFileStructure(project); writer = new BufferedWriter(new FileWriter(tmpCodeFile), Constants.BUFFER_8K); writer.write(projectXml); writer.flush(); File permissionFile = new File(buildProjectPath(project.getName()), PROJECTPERMISSIONS_NAME); writer = new BufferedWriter(new FileWriter(permissionFile), Constants.BUFFER_8K); for (String resource : generatePermissionsSetFromResource(project.getRequiredResources())) { writer.write(resource); writer.newLine(); } writer.flush(); return true; } catch (Exception exception) { Log.e(TAG, "Saving project " + project.getName() + " failed.", exception); return false; } finally { if (writer != null) { try { writer.close(); if (currentCodeFile.exists() && !currentCodeFile.delete()) { Log.e(TAG, "Could not delete " + currentCodeFile.getName()); } if (!tmpCodeFile.renameTo(currentCodeFile)) { Log.e(TAG, "Could not rename " + currentCodeFile.getName()); } } catch (IOException ioException) { Log.e(TAG, "Failed closing the buffered writer", ioException); } } loadSaveLock.unlock(); } } public Scene createDefaultScene(String sceneName, boolean drone, boolean landscape, Context context) { Project currentProject = ProjectManager.getInstance().getCurrentProject(); try { if (drone) { DefaultProjectHandler.getInstance().setDefaultProjectCreator(DefaultProjectHandler.ProjectCreatorType .PROJECT_CREATOR_DRONE); } else { DefaultProjectHandler.getInstance().setDefaultProjectCreator(DefaultProjectHandler.ProjectCreatorType .PROJECT_CREATOR_DEFAULT); } Project project = DefaultProjectHandler.createDefaultProjectForScene(context, landscape); if (!project.getDefaultScene().rename(sceneName, context, false)) { loadSaveLock.lock(); deleteProject(project.getName()); loadSaveLock.unlock(); ProjectManager.getInstance().setProject(currentProject); ProjectManager.getInstance().setCurrentScene(currentProject.getDefaultScene()); return null; } File defaultSceneDir = new File(buildScenePath(project.getName(), project.getDefaultScene().getName())); File targetSceneDir = new File(buildScenePath(currentProject.getName(), sceneName)); copyDirectory(targetSceneDir, defaultSceneDir); project.getDefaultScene().setProject(currentProject); project.getDefaultScene().resetDataContainerForDefaultScene(); loadSaveLock.lock(); deleteProject(project.getName()); loadSaveLock.unlock(); ProjectManager.getInstance().setProject(currentProject); ProjectManager.getInstance().setCurrentScene(currentProject.getDefaultScene()); return project.getDefaultScene(); } catch (IOException e) { Log.e(TAG, "Error while creating default Scene!", e); return null; } } public boolean saveBackpack(Backpack backpack) { Log.d(TAG, "Saving backpack json"); FileWriter writer = null; String json = backpackGson.toJson(backpack); Log.d(TAG, json); try { File backpackFile = new File(buildPath(DEFAULT_ROOT, BACKPACK_DIRECTORY, BACKPACK_FILENAME)); if (!backpackFile.exists()) { backpackFile.createNewFile(); } writer = new FileWriter(buildPath(DEFAULT_ROOT, BACKPACK_DIRECTORY, BACKPACK_FILENAME)); writer.write(json); return true; } catch (IOException e) { Log.e(TAG, "Could not write backpack file", e); return false; } finally { if (writer != null) { try { writer.close(); } catch (IOException ioException) { Log.e(TAG, "Failed closing the buffered writer", ioException); } } } } public Backpack loadBackpack() { Log.d(TAG, "Loading backpack json"); File backpackFile = new File(buildPath(DEFAULT_ROOT, BACKPACK_DIRECTORY, BACKPACK_FILENAME)); if (!backpackFile.exists()) { Log.d(TAG, "Backpack file does not exist!"); return null; } try { BufferedReader bufferedBackpackReader = new BufferedReader(new FileReader(backpackFile)); return backpackGson.fromJson(bufferedBackpackReader, Backpack.class); } catch (FileNotFoundException e) { Log.d(TAG, "Could not find backpack file!"); return new Backpack(); } catch (JsonSyntaxException | JsonIOException jsonException) { Log.d(TAG, "Could not load backpack file! File will be deleted!", jsonException); deleteBackpackFile(); return new Backpack(); } } public boolean deleteBackpackFile() { File backpackFile = new File(buildPath(DEFAULT_ROOT, BACKPACK_DIRECTORY, BACKPACK_FILENAME)); if (!backpackFile.exists()) { Log.d(TAG, "Backpack file does not exist!"); return false; } Log.d(TAG, "Deleting backpack.json"); return backpackFile.delete(); } public boolean codeFileSanityCheck(String projectName) { loadSaveLock.lock(); try { File tmpCodeFile = new File(buildProjectPath(projectName), PROJECTCODE_NAME_TMP); if (tmpCodeFile.exists()) { File currentCodeFile = new File(buildProjectPath(projectName), PROJECTCODE_NAME); if (currentCodeFile.exists()) { Log.w(TAG, "TMP File probably corrupted. Both files exist. Discard " + tmpCodeFile.getName()); if (!tmpCodeFile.delete()) { Log.e(TAG, "Could not delete " + tmpCodeFile.getName()); // fail("Could not delete " + tmpCodeFile.getName()); } // fail("TMP File probably corrupted. Both files exist. Discard " + tmpCodeFile.getName()); return false; } Log.w(TAG, "Process interrupted before renaming. Rename " + PROJECTCODE_NAME_TMP + " to " + PROJECTCODE_NAME); if (!tmpCodeFile.renameTo(currentCodeFile)) { Log.e(TAG, "Could not rename " + tmpCodeFile.getName()); // fail("Could not rename " + tmpCodeFile.getName()); return false; } } } catch (Exception exception) { Log.e(TAG, "Exception " + exception); } finally { loadSaveLock.unlock(); } return true; } private void createProjectFileStructure(Project project) throws IOException { Log.d(TAG, "create Project Data structure"); createCatroidRoot(); File projectDirectory = new File(buildProjectPath(project.getName())); projectDirectory.mkdir(); for (Scene scene : project.getSceneList()) { File sceneDirectory = new File(buildScenePath(project.getName(), scene.getName())); createSceneFileStructure(sceneDirectory); } } private void createSceneFileStructure(File sceneDirectory) throws IOException { Log.d(TAG, "create Scene Data structure"); sceneDirectory.mkdir(); File imageDirectory = new File(sceneDirectory, IMAGE_DIRECTORY); imageDirectory.mkdir(); File noMediaFile = new File(imageDirectory, NO_MEDIA_FILE); noMediaFile.createNewFile(); File soundDirectory = new File(sceneDirectory, SOUND_DIRECTORY); soundDirectory.mkdir(); noMediaFile = new File(soundDirectory, NO_MEDIA_FILE); noMediaFile.createNewFile(); } private void createBackPackFileStructure() throws IOException { File backPackDirectory = new File(DEFAULT_ROOT, BACKPACK_DIRECTORY); backPackDirectory.mkdir(); File backPackSceneDirectory = new File(backPackDirectory, SCENES_DIRECTORY); backPackSceneDirectory.mkdir(); backPackSoundDirectory = new File(backPackDirectory, BACKPACK_SOUND_DIRECTORY); backPackSoundDirectory.mkdir(); backPackImageDirectory = new File(backPackDirectory, BACKPACK_IMAGE_DIRECTORY); backPackImageDirectory.mkdir(); } public void clearBackPackSoundDirectory() { if (backPackSoundDirectory == null) { Log.d(TAG, "Backpack sound directory not created yet - probably project was never saved before"); return; } File[] backPackFiles = backPackSoundDirectory.listFiles(); if (backPackFiles != null && backPackFiles.length > 1) { for (File node : backPackSoundDirectory.listFiles()) { if (!(node.getName().equals(".nomedia"))) { node.delete(); } } } } public void clearBackPackLookDirectory() { if (backPackImageDirectory == null) { Log.d(TAG, "Backpack image directory not created yet - probably project was never saved before"); return; } File[] backPackFiles = backPackImageDirectory.listFiles(); if (backPackFiles != null && backPackFiles.length > 1) { for (File node : backPackImageDirectory.listFiles()) { if (!(node.getName().equals(".nomedia"))) { node.delete(); } } } } public boolean deleteProject(String projectName) { return UtilFile.deleteDirectory(new File(buildProjectPath(projectName))); } public boolean deleteScene(String projectName, String sceneName) { return UtilFile.deleteDirectory(new File(buildScenePath(projectName, sceneName))); } public boolean projectExists(String projectName) { List<String> projectNameList = UtilFile.getProjectNames(new File(DEFAULT_ROOT)); for (String projectNameIterator : projectNameList) { if (projectNameIterator.equals(projectName)) { return true; } } return false; } public File copySoundFile(String path) throws IOException, IllegalArgumentException { String currentProject = ProjectManager.getInstance().getCurrentProject().getName(); String currentScene = ProjectManager.getInstance().getCurrentScene().getName(); File soundDirectory = new File(buildPath(buildScenePath(currentProject, currentScene), SOUND_DIRECTORY)); File inputFile = new File(path); if (!inputFile.exists() || !inputFile.canRead()) { throw new IllegalArgumentException("file " + path + " doesn`t exist or can`t be read"); } String inputFileChecksum = Utils.md5Checksum(inputFile); File outputFile = new File(buildPath(soundDirectory.getAbsolutePath(), inputFileChecksum + "_" + inputFile.getName())); return copyFileAddCheckSum(outputFile, inputFile); } private File copyFileBackPack(String programSubDirectory, String backpackSubDirectory, String inputFilePath, String newTitle, boolean copyFromBackpack) throws IOException, IllegalArgumentException { File inputFile = new File(inputFilePath); if (!inputFile.exists() || !inputFile.canRead()) { Log.e(TAG, "file " + inputFilePath + " doesn`t exist or can`t be read"); return null; } String inputFileChecksum = Utils.md5Checksum(inputFile); String fileFormat = inputFilePath.substring(inputFilePath.lastIndexOf('.'), inputFilePath.length()); String outputFilePath; if (copyFromBackpack) { String currentProject = ProjectManager.getInstance().getCurrentProject().getName(); String currentScene = ProjectManager.getInstance().getCurrentScene().getName(); outputFilePath = buildPath(buildScenePath(currentProject, currentScene), programSubDirectory, inputFileChecksum + "_" + newTitle + fileFormat); } else { outputFilePath = buildPath(DEFAULT_ROOT, BACKPACK_DIRECTORY, backpackSubDirectory, inputFileChecksum + "_" + newTitle + fileFormat); FileChecksumContainer fileChecksumContainer = ProjectManager.getInstance().getFileChecksumContainer(); if (!fileChecksumContainer.containsChecksumBackPack(inputFileChecksum)) { fileChecksumContainer.addChecksumBackPack(inputFileChecksum, outputFilePath); } } File outputFile = new File(outputFilePath); if (!outputFile.exists()) { outputFile.createNewFile(); } return copyFileAddCheckSum(outputFile, inputFile); } public File copySoundFileBackPack(SoundInfo selectedSoundInfo, String newTitle, boolean copyFromBackpack) throws IOException, IllegalArgumentException { if (selectedSoundInfo == null) { return null; } String inputFilePath; if (copyFromBackpack) { inputFilePath = selectedSoundInfo.getAbsoluteBackPackPath(); } else { inputFilePath = selectedSoundInfo.getAbsoluteProjectPath(); } return copyFileBackPack(SOUND_DIRECTORY, BACKPACK_SOUND_DIRECTORY, inputFilePath, newTitle, copyFromBackpack); } public File copyImageBackPack(LookData selectedLookData, String newName, boolean copyFromBackpack) throws IOException { if (selectedLookData == null) { return null; } String inputFilePath; if (copyFromBackpack) { inputFilePath = selectedLookData.getAbsoluteBackPackPath(); } else { inputFilePath = selectedLookData.getAbsoluteProjectPath(); } return copyFileBackPack(IMAGE_DIRECTORY, BACKPACK_IMAGE_DIRECTORY, inputFilePath, newName, copyFromBackpack); } public File copyImageFromResourceToCatroid(Activity activity, int imageId, String defaultImageName) throws IOException { Bitmap newImage = BitmapFactory.decodeResource(activity.getApplicationContext().getResources(), imageId); String projectName = ProjectManager.getInstance().getCurrentProject().getName(); String sceneName = ProjectManager.getInstance().getCurrentScene().getName(); return createImageFromBitmap(projectName, sceneName, newImage, defaultImageName); } public File createImageFromBitmap(String currentProjectName, String currentSceneName, Bitmap inputImage, String newName) throws IOException { File imageDirectory = new File(buildPath(buildScenePath(currentProjectName, currentSceneName), IMAGE_DIRECTORY)); File outputFileDirectory = new File(imageDirectory.getAbsolutePath()); if (!outputFileDirectory.exists()) { outputFileDirectory.mkdirs(); } File outputFile = new File(buildPath(imageDirectory.getAbsolutePath(), newName)); return createFileFromBitmap(outputFile, inputImage, imageDirectory); } public File copyImage(String currentProjectName, String currentSceneName, String inputFilePath, String newName) throws IOException { String newFilePath; File imageDirectory = new File(buildPath(buildScenePath(currentProjectName, currentSceneName), IMAGE_DIRECTORY)); File inputFile = new File(inputFilePath); if (!inputFile.exists() || !inputFile.canRead()) { return null; } int[] imageDimensions = ImageEditing.getImageDimensions(inputFilePath); FileChecksumContainer checksumCont = ProjectManager.getInstance().getFileChecksumContainer(); File outputFileDirectory = new File(imageDirectory.getAbsolutePath()); if (!outputFileDirectory.exists()) { outputFileDirectory.mkdirs(); } Project project = ProjectManager.getInstance().getCurrentProject(); if ((imageDimensions[0] > project.getXmlHeader().virtualScreenWidth) && (imageDimensions[1] > project.getXmlHeader().virtualScreenHeight)) { File outputFile = new File(buildPath(imageDirectory.getAbsolutePath(), inputFile.getName())); return copyAndResizeImage(outputFile, inputFile, imageDirectory); } else { String checksumSource = Utils.md5Checksum(inputFile); if (newName != null) { newFilePath = buildPath(imageDirectory.getAbsolutePath(), checksumSource + "_" + newName); } else { newFilePath = buildPath(imageDirectory.getAbsolutePath(), checksumSource + "_" + inputFile.getName()); if (checksumCont.containsChecksum(checksumSource)) { checksumCont.addChecksum(checksumSource, newFilePath); return new File(checksumCont.getPath(checksumSource)); } } File outputFile = new File(newFilePath); return copyFileAddCheckSum(outputFile, inputFile); } } public File makeTempImageCopy(String inputFilePath) throws IOException { File tempDirectory = new File(Constants.TMP_PATH); File inputFile = new File(inputFilePath); if (!inputFile.exists() || !inputFile.canRead()) { return null; } File outputFileDirectory = new File(tempDirectory.getAbsolutePath()); if (!outputFileDirectory.exists()) { outputFileDirectory.mkdirs(); } File outputFile = new File(Constants.TMP_IMAGE_PATH); File copiedFile = UtilFile.copyFile(outputFile, inputFile); return copiedFile; } public void deleteTempImageCopy() { File temporaryPictureFileInPocketPaint = new File(Constants.TMP_IMAGE_PATH); if (temporaryPictureFileInPocketPaint.exists()) { temporaryPictureFileInPocketPaint.delete(); } } private File createFileFromBitmap(File outputFile, Bitmap inputImage, File imageDirectory) throws IOException { saveBitmapToImageFile(outputFile, inputImage); String checksumCompressedFile = Utils.md5Checksum(outputFile); FileChecksumContainer fileChecksumContainer = ProjectManager.getInstance().getFileChecksumContainer(); String newFilePath = buildPath(imageDirectory.getAbsolutePath(), checksumCompressedFile + "_" + outputFile.getName()); if (!fileChecksumContainer.addChecksum(checksumCompressedFile, newFilePath)) { return new File(fileChecksumContainer.getPath(checksumCompressedFile)); } File compressedFile = new File(newFilePath); outputFile.renameTo(compressedFile); return compressedFile; } private File copyAndResizeImage(File outputFile, File inputFile, File imageDirectory) throws IOException { Project project = ProjectManager.getInstance().getCurrentProject(); Bitmap bitmap = ImageEditing.getScaledBitmapFromPath(inputFile.getAbsolutePath(), project.getXmlHeader().virtualScreenWidth, project.getXmlHeader().virtualScreenHeight, ImageEditing.ResizeType.FILL_RECTANGLE_WITH_SAME_ASPECT_RATIO, true); saveBitmapToImageFile(outputFile, bitmap); String checksumCompressedFile = Utils.md5Checksum(outputFile); FileChecksumContainer fileChecksumContainer = ProjectManager.getInstance().getFileChecksumContainer(); String newFilePath = buildPath(imageDirectory.getAbsolutePath(), checksumCompressedFile + "_" + inputFile.getName()); if (!fileChecksumContainer.addChecksum(checksumCompressedFile, newFilePath)) { if (!outputFile.getAbsolutePath().equalsIgnoreCase(inputFile.getAbsolutePath())) { outputFile.delete(); } return new File(fileChecksumContainer.getPath(checksumCompressedFile)); } File compressedFile = new File(newFilePath); outputFile.renameTo(compressedFile); return compressedFile; } public void deleteFile(String filepath, boolean isBackPackFile) { FileChecksumContainer container = ProjectManager.getInstance().getFileChecksumContainer(); try { if (isBackPackFile) { File toDelete = new File(filepath); Log.d(TAG, "delete" + toDelete); toDelete.delete(); } else if (container.decrementUsage(filepath)) { File toDelete = new File(filepath); Log.d(TAG, "delete" + toDelete); toDelete.delete(); } } catch (FileNotFoundException fileNotFoundException) { Log.e(TAG, Log.getStackTraceString(fileNotFoundException)); } } public void deleteAllFile(String filepath) { File toDelete = new File(filepath); if (toDelete.isDirectory()) { Log.d(TAG, "file is directory" + filepath); for (String file : toDelete.list()) { deleteAllFile(file); } } toDelete.delete(); } public void fillChecksumContainer() { Project currentProject = ProjectManager.getInstance().getCurrentProject(); if (currentProject == null) { return; } ProjectManager.getInstance().setFileChecksumContainer(new FileChecksumContainer()); FileChecksumContainer container = ProjectManager.getInstance().getFileChecksumContainer(); Project newProject = ProjectManager.getInstance().getCurrentProject(); for (Scene scene : newProject.getSceneList()) { for (Sprite currentSprite : scene.getSpriteList()) { for (SoundInfo soundInfo : currentSprite.getSoundList()) { container.addChecksum(soundInfo.getChecksum(), soundInfo.getAbsolutePath()); } for (LookData lookData : currentSprite.getLookDataList()) { container.addChecksum(lookData.getChecksum(), lookData.getAbsolutePath()); } } } } public String getXMLStringOfAProject(Project project) { loadSaveLock.lock(); String xmlProject = ""; try { xmlProject = xstream.toXML(project); } finally { loadSaveLock.unlock(); } return xmlProject; } private File copyFileAddCheckSum(File destinationFile, File sourceFile) throws IOException { File copiedFile = UtilFile.copyFile(destinationFile, sourceFile); addChecksum(destinationFile, sourceFile); return copiedFile; } private void addChecksum(File destinationFile, File sourceFile) { String checksumSource = Utils.md5Checksum(sourceFile); FileChecksumContainer fileChecksumContainer = ProjectManager.getInstance().getFileChecksumContainer(); fileChecksumContainer.addChecksum(checksumSource, destinationFile.getAbsolutePath()); } private Set<String> generatePermissionsSetFromResource(int resources) { Set<String> permissionsSet = new HashSet<String>(); if ((resources & Brick.TEXT_TO_SPEECH) > 0) { permissionsSet.add(Constants.TEXT_TO_SPEECH); } if ((resources & Brick.BLUETOOTH_LEGO_NXT) > 0) { permissionsSet.add(Constants.BLUETOOTH_LEGO_NXT); } if ((resources & Brick.ARDRONE_SUPPORT) > 0) { permissionsSet.add(Constants.ARDRONE_SUPPORT); } if ((resources & Brick.BLUETOOTH_PHIRO) > 0) { permissionsSet.add(Constants.BLUETOOTH_PHIRO_PRO); } if ((resources & Brick.CAMERA_FLASH) > 0) { permissionsSet.add(Constants.CAMERA_FLASH); } if ((resources & Brick.VIBRATOR) > 0) { permissionsSet.add(Constants.VIBRATOR); } if ((resources & Brick.FACE_DETECTION) > 0) { permissionsSet.add(Constants.FACE_DETECTION); } if ((resources & Brick.NFC_ADAPTER) > 0) { permissionsSet.add(Constants.NFC); } return permissionsSet; } public void copyImageFiles(String targetScene, String targetProject, String sourceScene, String sourceProject) throws Exception { copyFiles(targetScene, targetProject, sourceScene, sourceProject, false); } public void copySoundFiles(String targetScene, String targetProject, String sourceScene, String sourceProject) throws Exception { copyFiles(targetScene, targetProject, sourceScene, sourceProject, true); } private void copyFiles(String targetScene, String targetProject, String sourceScene, String sourceProject, boolean copySoundFiles) throws Exception { String type = IMAGE_DIRECTORY; if (copySoundFiles) { type = SOUND_DIRECTORY; } File sourceDirectory = new File(buildPath(buildScenePath(sourceProject, sourceScene), type)); File targetDirectory = new File(buildPath(buildScenePath(targetProject, targetScene), type)); targetDirectory.mkdirs(); for (File sourceFile : sourceDirectory.listFiles()) { File targetFile = new File(targetDirectory.getAbsolutePath(), sourceFile.getName()); targetFile.createNewFile(); FileChannel source = new FileInputStream(sourceFile).getChannel(); FileChannel target = new FileOutputStream(targetFile).getChannel(); target.transferFrom(source, 0, source.size()); source.close(); target.close(); } } public void updateCodefileOnDownload(String projectName) { File projectCodeFile = new File(buildProjectPath(projectName), PROJECTCODE_NAME); xstream.updateCollisionReceiverBrickMessage(projectCodeFile); } }