Java tutorial
/* * Catroid: An on-device visual programming system for Android devices * Copyright (C) 2010-2015 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.ui.fragment; import android.app.AlertDialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.text.Spannable; import android.text.SpannableString; import android.text.style.ForegroundColorSpan; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.ListView; import com.actionbarsherlock.view.ActionMode; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuInflater; import org.catrobat.catroid.ProjectManager; import org.catrobat.catroid.R; import org.catrobat.catroid.common.Constants; import org.catrobat.catroid.content.Script; import org.catrobat.catroid.content.Sprite; import org.catrobat.catroid.content.bricks.AllowedAfterDeadEndBrick; import org.catrobat.catroid.content.bricks.Brick; import org.catrobat.catroid.content.bricks.DeadEndBrick; import org.catrobat.catroid.content.bricks.FormulaBrick; import org.catrobat.catroid.content.bricks.NestingBrick; import org.catrobat.catroid.content.bricks.ScriptBrick; import org.catrobat.catroid.content.bricks.UserBrick; import org.catrobat.catroid.content.commands.ChangeFormulaCommand; import org.catrobat.catroid.content.commands.CommandFactory; import org.catrobat.catroid.content.commands.OnFormulaChangedListener; import org.catrobat.catroid.formulaeditor.Formula; import org.catrobat.catroid.ui.BottomBar; import org.catrobat.catroid.ui.ScriptActivity; import org.catrobat.catroid.ui.ViewSwitchLock; import org.catrobat.catroid.ui.adapter.BrickAdapter; import org.catrobat.catroid.ui.adapter.BrickAdapter.OnBrickCheckedListener; import org.catrobat.catroid.ui.dialogs.CustomAlertDialogBuilder; import org.catrobat.catroid.ui.dialogs.DeleteLookDialog; import org.catrobat.catroid.ui.dragndrop.DragAndDropListView; import org.catrobat.catroid.ui.fragment.BrickCategoryFragment.OnCategorySelectedListener; import org.catrobat.catroid.utils.ToastUtil; import org.catrobat.catroid.utils.Utils; import java.util.List; import java.util.concurrent.locks.Lock; public class ScriptFragment extends ScriptActivityFragment implements OnCategorySelectedListener, OnBrickCheckedListener, OnFormulaChangedListener { public static final String TAG = ScriptFragment.class.getSimpleName(); private static final int ACTION_MODE_COPY = 0; private static final int ACTION_MODE_DELETE = 1; private static int selectedBrickPosition = Constants.NO_POSITION; private ActionMode actionMode; private View selectAllActionModeButton; private BrickAdapter adapter; private DragAndDropListView listView; private Sprite sprite; private Script scriptToEdit; private BrickListChangedReceiver brickListChangedReceiver; private Lock viewSwitchLock = new ViewSwitchLock(); private boolean deleteScriptFromContextMenu = false; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_script, null); listView = (DragAndDropListView) rootView.findViewById(android.R.id.list); return rootView; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); initListeners(); } @Override public void onPrepareOptionsMenu(Menu menu) { menu.findItem(R.id.show_details).setVisible(false); menu.findItem(R.id.rename).setVisible(false); menu.findItem(R.id.backpack).setVisible(false); menu.findItem(R.id.unpacking).setVisible(false); super.onPrepareOptionsMenu(menu); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { menu.findItem(R.id.delete).setVisible(true); menu.findItem(R.id.copy).setVisible(true); super.onCreateOptionsMenu(menu, inflater); } @Override public void onStart() { super.onStart(); BottomBar.showBottomBar(getActivity()); initListeners(); } @Override public void onResume() { super.onResume(); if (!Utils.checkForExternalStorageAvailableAndDisplayErrorIfNot(getActivity())) { return; } if (brickListChangedReceiver == null) { brickListChangedReceiver = new BrickListChangedReceiver(); } IntentFilter filterBrickListChanged = new IntentFilter(ScriptActivity.ACTION_BRICK_LIST_CHANGED); getActivity().registerReceiver(brickListChangedReceiver, filterBrickListChanged); BottomBar.showBottomBar(getActivity()); BottomBar.showPlayButton(getActivity()); BottomBar.showAddButton(getActivity()); initListeners(); adapter.resetAlphas(); } @Override public void onPause() { super.onPause(); ProjectManager projectManager = ProjectManager.getInstance(); if (brickListChangedReceiver != null) { getActivity().unregisterReceiver(brickListChangedReceiver); } if (projectManager.getCurrentProject() != null) { projectManager.saveProject(getActivity().getApplicationContext()); projectManager.getCurrentProject().removeUnusedBroadcastMessages(); // TODO: Find better place } } public BrickAdapter getAdapter() { return adapter; } @Override public DragAndDropListView getListView() { return listView; } @Override public void onCategorySelected(String category) { AddBrickFragment addBrickFragment = AddBrickFragment.newInstance(category, this); FragmentManager fragmentManager = getActivity().getSupportFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); fragmentTransaction.add(R.id.script_fragment_container, addBrickFragment, AddBrickFragment.ADD_BRICK_FRAGMENT_TAG); fragmentTransaction.addToBackStack(null); fragmentTransaction.commit(); adapter.notifyDataSetChanged(); } public void updateAdapterAfterAddNewBrick(Brick brickToBeAdded) { int firstVisibleBrick = listView.getFirstVisiblePosition(); int lastVisibleBrick = listView.getLastVisiblePosition(); int position = ((1 + lastVisibleBrick - firstVisibleBrick) / 2); position += firstVisibleBrick; //TODO: allow recursive userbricks if its possible if (adapter.getUserBrick() != null && brickToBeAdded instanceof UserBrick) { // && ((UserBrick) brickToBeAdded).getDefinitionBrick().equals(ProjectManager.getInstance().getCurrentUserBrick().getDefinitionBrick())) { ToastUtil.showError(getActivity().getApplicationContext(), R.string.recursive_user_brick_forbidden); } else { adapter.addNewBrick(position, brickToBeAdded, true); adapter.notifyDataSetChanged(); } } private void initListeners() { sprite = ProjectManager.getInstance().getCurrentSprite(); if (sprite == null) { return; } getSherlockActivity().findViewById(R.id.button_add).setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { handleAddButton(); } }); adapter = new BrickAdapter(getActivity(), sprite, listView); adapter.setOnBrickCheckedListener(this); ScriptActivity activity = (ScriptActivity) getActivity(); activity.setupBrickAdapter(adapter); if (ProjectManager.getInstance().getCurrentSprite().getNumberOfScripts() > 0) { ProjectManager.getInstance().setCurrentScript(((ScriptBrick) adapter.getItem(0)).getScriptSafe()); } listView.setOnCreateContextMenuListener(this); listView.setOnDragAndDropListener(adapter); listView.setAdapter(adapter); registerForContextMenu(listView); } private void showCategoryFragment() { BrickCategoryFragment brickCategoryFragment = new BrickCategoryFragment(); brickCategoryFragment.setBrickAdapter(adapter); brickCategoryFragment.setOnCategorySelectedListener(this); FragmentManager fragmentManager = getActivity().getSupportFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); fragmentTransaction.add(R.id.script_fragment_container, brickCategoryFragment, BrickCategoryFragment.BRICK_CATEGORY_FRAGMENT_TAG); fragmentTransaction.addToBackStack(BrickCategoryFragment.BRICK_CATEGORY_FRAGMENT_TAG); fragmentTransaction.commit(); adapter.notifyDataSetChanged(); } @Override public boolean getShowDetails() { //Currently no showDetails option return false; } @Override public void setShowDetails(boolean showDetails) { //Currently no showDetails option } @Override protected void showRenameDialog() { //Rename not supported } @Override public void startRenameActionMode() { //Rename not supported } @Override public void startCopyActionMode() { startActionMode(copyModeCallBack); } @Override public void startDeleteActionMode() { startActionMode(deleteModeCallBack); } private void startActionMode(ActionMode.Callback actionModeCallback) { actionMode = getSherlockActivity().startActionMode(actionModeCallback); for (int i = adapter.listItemCount; i < adapter.getBrickList().size(); i++) { adapter.getView(i, null, getListView()); } unregisterForContextMenu(listView); BottomBar.hideBottomBar(getActivity()); adapter.setCheckboxVisibility(View.VISIBLE); adapter.setActionMode(true); updateActionModeTitle(); } @Override public void handleAddButton() { if (!viewSwitchLock.tryLock()) { return; } // addButtonHandler != null when the user brick category is open in the AddBrickFragment if (AddBrickFragment.addButtonHandler != null) { AddBrickFragment.addButtonHandler.handleAddButton(); return; } if (listView.isCurrentlyDragging()) { listView.animateHoveringBrick(); return; } showCategoryFragment(); } @Override public boolean getActionModeActive() { return actionModeActive; } @Override public int getSelectMode() { return adapter.getSelectMode(); } @Override public void setSelectMode(int selectMode) { adapter.setSelectMode(selectMode); adapter.notifyDataSetChanged(); } @Override protected void showDeleteDialog() { DeleteLookDialog deleteLookDialog = DeleteLookDialog.newInstance(selectedBrickPosition); deleteLookDialog.show(getFragmentManager(), DeleteLookDialog.DIALOG_FRAGMENT_TAG); } @Override public void onFormulaChanged(FormulaBrick formulaBrick, Brick.BrickField brickField, Formula newFormula) { ChangeFormulaCommand changeFormulaCommand = CommandFactory.makeChangeFormulaCommand(formulaBrick, brickField, newFormula); changeFormulaCommand.execute(); adapter.notifyDataSetChanged(); } private class BrickListChangedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(ScriptActivity.ACTION_BRICK_LIST_CHANGED)) { adapter.updateProjectBrickList(); } } } private void addSelectAllActionModeButton(ActionMode mode, Menu menu) { selectAllActionModeButton = Utils.addSelectAllActionModeButton(getLayoutInflater(null), mode, menu); selectAllActionModeButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { adapter.checkAllItems(); } }); } private ActionMode.Callback deleteModeCallBack = new ActionMode.Callback() { @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return false; } @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { setSelectMode(ListView.CHOICE_MODE_MULTIPLE); setActionModeActive(true); mode.setTag(ACTION_MODE_DELETE); addSelectAllActionModeButton(mode, menu); return true; } @Override public boolean onActionItemClicked(ActionMode mode, com.actionbarsherlock.view.MenuItem item) { return false; } @Override public void onDestroyActionMode(ActionMode mode) { if (adapter.getAmountOfCheckedItems() == 0) { clearCheckedBricksAndEnableButtons(); } else { showConfirmDeleteDialog(false); } } }; private ActionMode.Callback copyModeCallBack = new ActionMode.Callback() { @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return false; } @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { setSelectMode(ListView.CHOICE_MODE_MULTIPLE); setActionModeActive(true); mode.setTag(ACTION_MODE_COPY); addSelectAllActionModeButton(mode, menu); return true; } @Override public boolean onActionItemClicked(ActionMode mode, com.actionbarsherlock.view.MenuItem item) { return false; } @Override public void onDestroyActionMode(ActionMode mode) { List<Brick> checkedBricks = adapter.getCheckedBricks(); for (Brick brick : checkedBricks) { copyBrick(brick); if (brick instanceof ScriptBrick) { break; } } clearCheckedBricksAndEnableButtons(); } }; private void copyBrick(Brick brick) { if (brick instanceof NestingBrick && (brick instanceof AllowedAfterDeadEndBrick || brick instanceof DeadEndBrick)) { return; } if (brick instanceof ScriptBrick) { scriptToEdit = ((ScriptBrick) brick).getScriptSafe(); Script clonedScript = scriptToEdit.copyScriptForSprite(sprite, sprite.getUserBrickList()); sprite.addScript(clonedScript); adapter.initBrickList(); adapter.notifyDataSetChanged(); return; } int brickId = adapter.getBrickList().indexOf(brick); if (brickId == -1) { return; } int newPosition = adapter.getCount(); try { Brick copiedBrick = brick.clone(); Script scriptList = null; if (adapter.getUserBrick() != null) { scriptList = ProjectManager.getInstance().getCurrentUserBrick().getDefinitionBrick() .getUserScript(); } else { scriptList = ProjectManager.getInstance().getCurrentScript(); } if (brick instanceof NestingBrick) { NestingBrick nestingBrickCopy = (NestingBrick) copiedBrick; nestingBrickCopy.initialize(); for (NestingBrick nestingBrick : nestingBrickCopy.getAllNestingBrickParts(true)) { scriptList.addBrick((Brick) nestingBrick); } } else { scriptList.addBrick(copiedBrick); } adapter.addNewBrick(newPosition, copiedBrick, false); adapter.initBrickList(); ProjectManager.getInstance().saveProject(getActivity().getApplicationContext()); adapter.notifyDataSetChanged(); } catch (CloneNotSupportedException exception) { Log.e(getTag(), "Copying a Brick failed", exception); ToastUtil.showError(getActivity(), R.string.error_copying_brick); } } private void deleteBrick(Brick brick) { if (brick instanceof ScriptBrick) { scriptToEdit = ((ScriptBrick) brick).getScriptSafe(); adapter.handleScriptDelete(sprite, scriptToEdit); return; } int brickId = adapter.getBrickList().indexOf(brick); if (brickId == -1) { return; } adapter.removeFromBrickListAndProject(brickId, true); } private void deleteCheckedBricks() { List<Brick> checkedBricks = adapter.getReversedCheckedBrickList(); for (Brick brick : checkedBricks) { deleteBrick(brick); } } private void showConfirmDeleteDialog(boolean fromContextMenu) { this.deleteScriptFromContextMenu = fromContextMenu; int titleId; if ((deleteScriptFromContextMenu && scriptToEdit.getBrickList().size() == 0) || adapter.getAmountOfCheckedItems() == 1) { titleId = R.string.dialog_confirm_delete_brick_title; } else { titleId = R.string.dialog_confirm_delete_multiple_bricks_title; } AlertDialog.Builder builder = new CustomAlertDialogBuilder(getActivity()); builder.setTitle(titleId); builder.setMessage(R.string.dialog_confirm_delete_brick_message); builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { if (deleteScriptFromContextMenu) { adapter.handleScriptDelete(sprite, scriptToEdit); } else { deleteCheckedBricks(); clearCheckedBricksAndEnableButtons(); } } }); builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }); builder.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { if (!deleteScriptFromContextMenu) { clearCheckedBricksAndEnableButtons(); } } }); AlertDialog alertDialog = builder.create(); alertDialog.show(); } private void clearCheckedBricksAndEnableButtons() { setSelectMode(ListView.CHOICE_MODE_NONE); adapter.clearCheckedItems(); setActionModeActive(false); registerForContextMenu(listView); BottomBar.showBottomBar(getActivity()); adapter.setActionMode(false); } @Override public void onBrickChecked() { updateActionModeTitle(); Utils.setSelectAllActionModeButtonVisibility(selectAllActionModeButton, adapter.getCount() > 0 && adapter.getAmountOfCheckedItems() != adapter.getCount()); } private void updateActionModeTitle() { int numberOfSelectedItems = adapter.getAmountOfCheckedItems(); String completeTitle; switch ((Integer) actionMode.getTag()) { case ACTION_MODE_COPY: completeTitle = getResources().getQuantityString(R.plurals.number_of_bricks_to_copy, numberOfSelectedItems, numberOfSelectedItems); break; case ACTION_MODE_DELETE: completeTitle = getResources().getQuantityString(R.plurals.number_of_bricks_to_delete, numberOfSelectedItems, numberOfSelectedItems); break; default: throw new IllegalArgumentException("Wrong or unhandled tag in ActionMode."); } int indexOfNumber = completeTitle.indexOf(' ') + 1; Spannable completeSpannedTitle = new SpannableString(completeTitle); completeSpannedTitle.setSpan( new ForegroundColorSpan(getResources().getColor(R.color.actionbar_title_color)), indexOfNumber, indexOfNumber + String.valueOf(numberOfSelectedItems).length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); actionMode.setTitle(completeSpannedTitle); } @Override public void startBackPackActionMode() { } }