/* * 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.content.bricks; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.view.Gravity; import android.view.View; import android.view.View.MeasureSpec; import android.view.View.OnClickListener; import android.widget.BaseAdapter; import android.widget.CheckBox; import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import com.badlogic.gdx.scenes.scene2d.actions.SequenceAction; import com.thoughtworks.xstream.annotations.XStreamAlias; import org.catrobat.catroid.ProjectManager; import org.catrobat.catroid.R; import org.catrobat.catroid.common.ScreenValues; import org.catrobat.catroid.content.Scene; import org.catrobat.catroid.content.Script; import org.catrobat.catroid.content.Sprite; import org.catrobat.catroid.content.StartScript; import org.catrobat.catroid.formulaeditor.DataContainer; import org.catrobat.catroid.formulaeditor.Formula; import org.catrobat.catroid.formulaeditor.UserVariable; import org.catrobat.catroid.ui.BrickLayout; import org.catrobat.catroid.ui.dragndrop.BrickDragAndDropListView; import org.catrobat.catroid.ui.fragment.UserBrickElementEditorFragment; import org.catrobat.catroid.utils.Utils; import java.util.ArrayList; import java.util.List; public class UserScriptDefinitionBrick extends BrickBaseType implements ScriptBrick, OnClickListener { private static final long serialVersionUID = 1L; private static final String TAG = UserScriptDefinitionBrick.class.getSimpleName(); private static final String LINE_BREAK = "linebreak"; private StartScript script; @XStreamAlias("userBrickElements") private List<UserScriptDefinitionBrickElement> userScriptDefinitionBrickElements; public UserScriptDefinitionBrick() { this.script = new StartScript(true); this.userScriptDefinitionBrickElements = new ArrayList<>(); } @Override public boolean equals(Object obj) { if (!(obj instanceof UserScriptDefinitionBrick)) { return false; } if (obj == this) { return true; } UserScriptDefinitionBrick definitionBrick = (UserScriptDefinitionBrick) obj; List<UserScriptDefinitionBrickElement> elements = definitionBrick.getUserScriptDefinitionBrickElements(); if (userScriptDefinitionBrickElements.size() != elements.size()) { return false; } for (int elementPosition = 0; elementPosition < userScriptDefinitionBrickElements.size(); elementPosition++) { UserScriptDefinitionBrickElement elementToCompare = elements.get(elementPosition); UserScriptDefinitionBrickElement element = userScriptDefinitionBrickElements.get(elementPosition); if (!(elementToCompare.equals(element))) { return false; } } return true; } @Override public int hashCode() { return super.hashCode() * TAG.hashCode(); } public int getRequiredResources() { int resources = Brick.NO_RESOURCES; for (Brick brick : script.getBrickList()) { if (brick instanceof UserBrick && ((UserBrick) brick).getDefinitionBrick() == this) { continue; } resources |= brick.getRequiredResources(); } return resources; } public void appendBrickToScript(Brick brick) { this.getScriptSafe().addBrick(brick); } @Override public CheckBox getCheckBox() { return null; } @Override public Brick copyBrickForSprite(Sprite sprite) { UserScriptDefinitionBrick clonedBrick = new UserScriptDefinitionBrick(); clonedBrick.userScriptDefinitionBrickElements = cloneDefinitionBrickElements(); for (Brick brick : this.script.getBrickList()) { clonedBrick.script.addBrick(brick.copyBrickForSprite(sprite)); } return clonedBrick; } public List<UserScriptDefinitionBrickElement> cloneDefinitionBrickElements() { List<UserScriptDefinitionBrickElement> cloneList = new ArrayList<>(); for (UserScriptDefinitionBrickElement originalUserBrickElement : userScriptDefinitionBrickElements) { UserScriptDefinitionBrickElement clonedUserBrickElement = new UserScriptDefinitionBrickElement(); clonedUserBrickElement.setText(originalUserBrickElement.getText()); clonedUserBrickElement.setElementType(originalUserBrickElement.getElementType()); clonedUserBrickElement.setNewLineHint(originalUserBrickElement.isNewLineHint()); cloneList.add(clonedUserBrickElement); } return cloneList; } @Override public View getView(final Context context, int brickId, final BaseAdapter baseAdapter) { if (animationState) { return view; } view = View.inflate(context, R.layout.brick_user_definition, null); setCheckboxView(R.id.brick_user_definition_checkbox); onLayoutChanged(); return view; } public void onLayoutChanged() { Context context = view.getContext(); LinearLayout layout = (LinearLayout) view.findViewById(R.id.brick_user_definition_layout); layout.setFocusable(false); layout.setFocusableInTouchMode(false); if (layout.getChildCount() > 0) { layout.removeAllViews(); } View userBrickPrototype = getUserBrickPrototypeView(context); Bitmap brickImage = getBrickImage(userBrickPrototype); ImageView preview = getBorderedPreview(brickImage); TextView define = new TextView(context); define.setTextAppearance(context, R.style.BrickText); define.setText(context.getString(R.string.define).concat(" ")); layout.addView(define); LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) define.getLayoutParams(); params.gravity = Gravity.CENTER_VERTICAL; // This stuff isn't being included by the style when I use setTextAppearance. define.setFocusable(false); define.setFocusableInTouchMode(false); define.setClickable(true); layout.setFocusable(false); layout.setFocusableInTouchMode(false); layout.setClickable(true); preview.setClickable(true); preview.setOnClickListener(this); layout.setOnClickListener(this); define.setOnClickListener(this); layout.addView(preview); } private View getUserBrickPrototypeView(Context context) { View prototypeView = View.inflate(context, R.layout.brick_user, null); BrickLayout layout = (BrickLayout) prototypeView.findViewById(R.id.brick_user_flow_layout); if (layout.getChildCount() > 0) { layout.removeAllViews(); } for (UserScriptDefinitionBrickElement element : getUserScriptDefinitionBrickElements()) { TextView currentTextView; if (element.isLineBreak()) { continue; } else if (element.isVariable()) { currentTextView = new EditText(context); currentTextView.setText(String.valueOf(0d)); currentTextView.setVisibility(View.VISIBLE); } else { currentTextView = new TextView(context); currentTextView.setTextAppearance(context, R.style.BrickText_Multiple); currentTextView.setText(element.getText()); } currentTextView.setFocusable(false); currentTextView.setFocusableInTouchMode(false); currentTextView.setClickable(false); layout.addView(currentTextView); if (element.isNewLineHint()) { BrickLayout.LayoutParams params = (BrickLayout.LayoutParams) currentTextView.getLayoutParams(); params.setNewLine(true); currentTextView.setLayoutParams(params); } } return prototypeView; } private Bitmap getBrickImage(View view) { boolean drawingCacheEnabled = view.isDrawingCacheEnabled(); view.setDrawingCacheEnabled(true); view.measure(MeasureSpec.makeMeasureSpec(ScreenValues.SCREEN_WIDTH, MeasureSpec.EXACTLY), MeasureSpec .makeMeasureSpec( Utils.getPhysicalPixels(BrickDragAndDropListView.WIDTH_OF_BRICK_PREVIEW_IMAGE, view.getContext()), MeasureSpec.AT_MOST)); view.layout(0, 0, ScreenValues.SCREEN_WIDTH, view.getMeasuredHeight()); view.setDrawingCacheBackgroundColor(Color.TRANSPARENT); view.buildDrawingCache(true); if (view.getDrawingCache() == null) { view.setDrawingCacheEnabled(drawingCacheEnabled); return null; } Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache()); view.setDrawingCacheEnabled(drawingCacheEnabled); return bitmap; } public ImageView getBorderedPreview(final Bitmap bitmap) { ImageView imageView = new ImageView(view.getContext()); imageView.setBackgroundColor(Color.TRANSPARENT); int radius = 7; Bitmap result = getWithBorder(radius, bitmap, Color.argb(Math.round(0.25f * 255), 0, 0, Math.round(0.1f * 255))); imageView.setImageBitmap(result); return imageView; } public Bitmap getWithBorder(int radius, Bitmap bitmap, int color) { int width = bitmap.getWidth(); int height = bitmap.getHeight(); int borderedWidth = width + radius * 2; int borderedHeight = height + radius * 2; Bitmap toReturn = Bitmap.createBitmap(borderedWidth, borderedHeight, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(toReturn); Bitmap border = Bitmap.createBitmap(borderedWidth, borderedHeight, Bitmap.Config.ARGB_8888); Canvas borderCanvas = new Canvas(border); Bitmap alpha = bitmap.extractAlpha(); Paint paintBorder = new Paint(); paintBorder.setColor(Color.WHITE); Paint paintBorder2 = new Paint(); paintBorder2.setColor(color); Paint paint = new Paint(); borderCanvas.drawBitmap(alpha, 0, 0, paintBorder); borderCanvas.drawBitmap(alpha, radius * 2, 0, paintBorder); borderCanvas.drawBitmap(alpha, 0, radius * 2, paintBorder); borderCanvas.drawBitmap(alpha, radius * 2, radius * 2, paintBorder); alpha = border.extractAlpha(); canvas.drawBitmap(alpha, 0, 0, paintBorder2); canvas.drawBitmap(bitmap, radius, radius, paint); return toReturn; } @Override public View getPrototypeView(Context context) { return getView(context, 0, null); } @Override public List<SequenceAction> addActionToSequence(Sprite sprite, SequenceAction sequence) { return null; } @Override public void onClick(View eventOrigin) { if (checkbox.getVisibility() == View.VISIBLE) { return; } UserBrickElementEditorFragment.showFragment(view, this); } @Override public Brick clone() { return new UserScriptDefinitionBrick(); } @Override public Script getScriptSafe() { return getUserScript(); } public Script getUserScript() { return script; } public int addUIText(String text) { UserScriptDefinitionBrickElement element = new UserScriptDefinitionBrickElement(); element.setIsText(); element.setText(text); int toReturn = userScriptDefinitionBrickElements.size(); userScriptDefinitionBrickElements.add(element); return toReturn; } public void addUILineBreak() { UserScriptDefinitionBrickElement element = new UserScriptDefinitionBrickElement(); element.setIsLineBreak(); element.setText(LINE_BREAK); userScriptDefinitionBrickElements.add(element); } public int addUILocalizedVariable(String name) { UserScriptDefinitionBrickElement element = new UserScriptDefinitionBrickElement(); element.setIsVariable(); element.setText(name); int toReturn = userScriptDefinitionBrickElements.size(); userScriptDefinitionBrickElements.add(element); return toReturn; } public void renameUIElement(UserScriptDefinitionBrickElement element, String oldName, String newName, Context context) { if (element.getText().equals(oldName)) { element.setText(newName); if (element.isVariable()) { Scene currentScene = ProjectManager.getInstance().getCurrentScene(); Sprite currentSprite = ProjectManager.getInstance().getCurrentSprite(); DataContainer dataContainer = currentScene.getDataContainer(); if (dataContainer != null) { List<UserBrick> matchingBricks = currentSprite.getUserBricksByDefinitionBrick(this, true, true); for (UserBrick userBrick : matchingBricks) { UserVariable userVariable = dataContainer.getUserVariable(oldName, userBrick, currentSprite); if (userVariable != null) { userVariable.setName(newName); } } } } } renameVariablesInFormulasAndBricks(oldName, newName, context); } public void removeDataAt(int id, Context context) { removeVariablesInFormulas(getUserScriptDefinitionBrickElements().get(id).getText(), context); userScriptDefinitionBrickElements.remove(id); } /** * Removes element at <b>from</b> and adds it after element at <b>to</b> */ public void reorderUIData(int from, int to) { if (to == -1) { UserScriptDefinitionBrickElement element = getUserScriptDefinitionBrickElements().remove(from); userScriptDefinitionBrickElements.add(0, element); } else if (from <= to) { UserScriptDefinitionBrickElement element = getUserScriptDefinitionBrickElements().remove(from); userScriptDefinitionBrickElements.add(to, element); } else { // from > to UserScriptDefinitionBrickElement element = getUserScriptDefinitionBrickElements().remove(from); userScriptDefinitionBrickElements.add(to + 1, element); } } public CharSequence getName() { CharSequence name = ""; for (UserScriptDefinitionBrickElement element : getUserScriptDefinitionBrickElements()) { if (!element.isVariable()) { name = element.getText(); break; } } return name; } public List<UserScriptDefinitionBrickElement> getUserScriptDefinitionBrickElements() { return userScriptDefinitionBrickElements; } public void renameVariablesInFormulasAndBricks(String oldName, String newName, Context context) { List<Brick> brickList = script.getBrickList(); for (Brick brick : brickList) { if (brick instanceof UserBrick) { List<Formula> formulaList = ((UserBrick) brick).getFormulas(); for (Formula formula : formulaList) { formula.updateVariableReferences(oldName, newName, context); } } if (brick instanceof FormulaBrick) { List<Formula> formulas = ((FormulaBrick) brick).getFormulas(); for (Formula formula : formulas) { formula.updateVariableReferences(oldName, newName, context); } } if (brick instanceof ShowTextBrick) { ShowTextBrick showTextBrick = (ShowTextBrick) brick; if (showTextBrick.getUserVariable().getName().equals(oldName)) { ((ShowTextBrick) brick).getUserVariable().setName(newName); } } if (brick instanceof HideTextBrick) { HideTextBrick showTextBrick = (HideTextBrick) brick; if (showTextBrick.getUserVariable().getName().equals(oldName)) { ((HideTextBrick) brick).getUserVariable().setName(newName); } } } } public void removeVariablesInFormulas(String name, Context context) { if (ProjectManager.getInstance().getCurrentScript() == null) { return; } List<Brick> brickList = ProjectManager.getInstance().getCurrentScript().getBrickList(); for (Brick brick : brickList) { if (brick instanceof UserBrick) { List<Formula> formulaList = ((UserBrick) brick).getFormulas(); for (Formula formula : formulaList) { formula.removeVariableReferences(name, context); } } if (brick instanceof FormulaBrick) { List<Formula> formulas = ((FormulaBrick) brick).getFormulas(); for (Formula formula : formulas) { formula.removeVariableReferences(name, context); } } } } @Override public void setCommentedOut(boolean commentedOut) { super.setCommentedOut(commentedOut); getScriptSafe().setCommentedOut(commentedOut); } }