/*
* 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.ui.fragment;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import org.catrobat.catroid.ProjectManager;
import org.catrobat.catroid.R;
import org.catrobat.catroid.common.Constants;
import org.catrobat.catroid.common.ProjectData;
import org.catrobat.catroid.content.Project;
import org.catrobat.catroid.exceptions.CompatibilityProjectException;
import org.catrobat.catroid.exceptions.LoadingProjectException;
import org.catrobat.catroid.exceptions.OutdatedVersionProjectException;
import org.catrobat.catroid.exceptions.ProjectException;
import org.catrobat.catroid.io.LoadProjectTask;
import org.catrobat.catroid.io.StorageHandler;
import org.catrobat.catroid.merge.MergeManager;
import org.catrobat.catroid.ui.BottomBar;
import org.catrobat.catroid.ui.MyProjectsActivity;
import org.catrobat.catroid.ui.ProjectActivity;
import org.catrobat.catroid.ui.adapter.CheckBoxListAdapter;
import org.catrobat.catroid.ui.adapter.ProjectListAdapter;
import org.catrobat.catroid.ui.dialogs.CopyProjectDialog;
import org.catrobat.catroid.ui.dialogs.RenameItemDialog;
import org.catrobat.catroid.ui.dialogs.SetDescriptionDialog;
import org.catrobat.catroid.utils.ToastUtil;
import org.catrobat.catroid.utils.UtilFile;
import org.catrobat.catroid.utils.Utils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ProjectListFragment extends ListActivityFragment implements LoadProjectTask.OnLoadProjectCompleteListener,
CheckBoxListAdapter.ListItemClickHandler<ProjectData>, CheckBoxListAdapter.ListItemLongClickHandler,
SetDescriptionDialog.ChangeDescriptionInterface {
private static final String TAG = ProjectListFragment.class.getSimpleName();
private static final String BUNDLE_ARGUMENTS_PROJECT_DATA = "project_data";
private static final String SHARED_PREFERENCE_NAME = "showDetailsMyProjects";
private ProjectListAdapter projectAdapter;
private ListView listView;
private List<ProjectData> projectList;
private ProjectData projectToEdit;
private int selectedProjectPosition;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View projectListFragment = inflater.inflate(R.layout.fragment_projects_list, container, false);
listView = (ListView) projectListFragment.findViewById(android.R.id.list);
BottomBar.showBottomBar(getActivity());
return projectListFragment;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
registerForContextMenu(getListView());
singleItemTitle = getString(R.string.program);
multipleItemsTitle = getString(R.string.programs);
if (savedInstanceState != null) {
projectToEdit = (ProjectData) savedInstanceState.getSerializable(BUNDLE_ARGUMENTS_PROJECT_DATA);
}
initializeList();
}
public void initializeList() {
File rootDirectory = new File(Constants.DEFAULT_ROOT);
File projectCodeFile;
projectList = new ArrayList<>();
for (String projectName : UtilFile.getProjectNames(rootDirectory)) {
projectCodeFile = new File(Utils.buildPath(Utils.buildProjectPath(projectName), Constants.PROJECTCODE_NAME));
projectList.add(new ProjectData(projectName, projectCodeFile.lastModified()));
}
Collections.sort(projectList, new Comparator<ProjectData>() {
@Override
public int compare(ProjectData project1, ProjectData project2) {
return Long.valueOf(project2.lastUsed).compareTo(project1.lastUsed);
}
});
projectAdapter = new ProjectListAdapter(getActivity(), R.layout.list_item, projectList);
setListAdapter(projectAdapter);
projectAdapter.setListItemClickHandler(this);
projectAdapter.setListItemLongClickHandler(this);
projectAdapter.setListItemCheckHandler(this);
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putSerializable(BUNDLE_ARGUMENTS_PROJECT_DATA, projectToEdit);
super.onSaveInstanceState(outState);
}
@Override
public void onResume() {
super.onResume();
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getActivity()
.getApplicationContext());
setShowDetails(settings.getBoolean(SHARED_PREFERENCE_NAME, false));
getActivity().findViewById(R.id.fragment_container).setVisibility(View.VISIBLE);
if (ProjectManager.getInstance().getHandleNewSceneFromScriptActivity()) {
Intent intent = new Intent(getActivity(), ProjectActivity.class);
intent.putExtra(ProjectActivity.EXTRA_FRAGMENT_POSITION, ProjectActivity.FRAGMENT_SCENES);
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivity(intent);
}
initializeList();
}
@Override
public void onPause() {
super.onPause();
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getActivity()
.getApplicationContext());
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean(SHARED_PREFERENCE_NAME, getShowDetails());
editor.commit();
}
@Override
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, view, menuInfo);
projectToEdit = projectAdapter.getItem(selectedProjectPosition);
Project currentProject = ProjectManager.getInstance().getCurrentProject();
if (currentProject == null) {
try {
ProjectManager.getInstance().loadProject(projectToEdit.projectName, getActivity());
} catch (LoadingProjectException loadingProjectException) {
Log.e(TAG, "Project cannot load", loadingProjectException);
Utils.showErrorDialog(getActivity(), R.string.error_load_project);
} catch (OutdatedVersionProjectException outdatedVersionException) {
Log.e(TAG, "Projectcode version is outdated", outdatedVersionException);
Utils.showErrorDialog(getActivity(), R.string.error_outdated_pocketcode_version);
} catch (CompatibilityProjectException compatibilityException) {
Log.e(TAG, "Project is not compatible", compatibilityException);
Utils.showErrorDialog(getActivity(), R.string.error_project_compatability);
}
}
boolean isCurrentProject = projectToEdit.projectName.equals(ProjectManager.getInstance().getCurrentProject().getName());
if (!isCurrentProject) {
menu.add(0, R.string.merge_button, 1, getString(R.string.merge_button) + ": " + ProjectManager.getInstance().getCurrentProject().getName());
}
menu.setHeaderTitle(projectToEdit.projectName);
getActivity().getMenuInflater().inflate(R.menu.context_menu_my_projects, menu);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.context_menu_delete:
showDeleteDialog();
break;
case R.id.context_menu_copy:
showCopyProjectDialog();
break;
case R.id.context_menu_rename:
showRenameDialog();
break;
case R.id.show_details:
showDetailsFragment();
break;
case R.id.context_menu_set_description:
showSetDescriptionDialog();
break;
case R.id.context_menu_upload:
ProjectManager.getInstance().uploadProject(projectToEdit.projectName, this.getActivity());
break;
case R.string.merge_button:
String firstProjectName = ProjectManager.getInstance().getCurrentProject().getName();
String secondProjectName = projectToEdit.projectName;
MergeManager.merge(firstProjectName, secondProjectName, getActivity(), projectAdapter);
break;
}
return super.onContextItemSelected(item);
}
@Override
public void handleOnItemClick(int position, View view, ProjectData listItem) {
LoadProjectTask loadProjectTask = new LoadProjectTask(getActivity(), listItem.projectName, true, false);
loadProjectTask.setOnLoadProjectCompleteListener(this);
getActivity().findViewById(R.id.fragment_container).setVisibility(View.GONE);
loadProjectTask.execute();
}
@Override
public void handleOnItemLongClick(int position, View view) {
selectedProjectPosition = position;
listView.showContextMenuForChild(view);
}
@Override
public void showDeleteDialog() {
int titleId;
if (adapter.getCheckedItems().size() == 1) {
titleId = R.string.dialog_confirm_delete_program_title;
} else {
titleId = R.string.dialog_confirm_delete_multiple_programs_title;
}
showDeleteDialog(titleId);
}
@Override
protected void packCheckedItems() {
//NO BackPack for Projects.
}
protected void deleteCheckedItems() {
if (projectAdapter.getCheckedItems().isEmpty()) {
deleteProject();
} else {
for (ProjectData projectData : projectAdapter.getCheckedItems()) {
projectToEdit = projectData;
deleteProject();
}
}
if (projectList.isEmpty()) {
initializeDefaultProjectAfterDelete();
} else if (ProjectManager.getInstance().getCurrentProject() == null) {
Utils.saveToPreferences(getActivity().getApplicationContext(), Constants.PREF_PROJECTNAME_KEY,
projectList.get(0).projectName);
}
}
private void deleteProject() {
ProjectManager projectManager = ProjectManager.getInstance();
try {
projectManager.deleteProject(projectToEdit.projectName, getActivity().getApplicationContext());
projectList.remove(projectToEdit);
} catch (IOException exception) {
Log.e(TAG, "Project could not be deleted", exception);
ToastUtil.showError(getActivity(), R.string.error_delete_project);
} catch (IllegalArgumentException exception) {
Log.e(TAG, "Project does not exist!", exception);
ToastUtil.showError(getActivity(), R.string.error_unknown_project);
}
}
private void showCopyProjectDialog() {
CopyProjectDialog dialogCopyProject = new CopyProjectDialog(R.string.dialog_copy_project_title,
R.string.new_project_name, projectToEdit.projectName);
dialogCopyProject.setTargetFragment(this, 0);
dialogCopyProject.show(getActivity().getFragmentManager(), CopyProjectDialog.DIALOG_FRAGMENT_TAG);
}
@Override
protected void copyCheckedItems() {
for (ProjectData projectData : projectAdapter.getCheckedItems()) {
projectToEdit = projectData;
showCopyProjectDialog();
}
}
public void showRenameDialog() {
if (!projectAdapter.getCheckedItems().isEmpty()) {
projectToEdit = projectAdapter.getCheckedItems().get(0);
}
RenameItemDialog dialog = new RenameItemDialog(R.string.rename_project, R.string.new_project_name, projectToEdit.projectName,
this);
dialog.show(getActivity().getFragmentManager(), RenameItemDialog.DIALOG_FRAGMENT_TAG);
}
@Override
public boolean itemNameExists(String newName) {
return Utils.checkIfProjectExistsOrIsDownloadingIgnoreCase(newName);
}
@Override
public void renameItem(String newName) {
ProjectManager projectManager = ProjectManager.getInstance();
try {
projectManager.loadProject(projectToEdit.projectName, getActivity());
projectManager.renameProject(newName, getActivity());
projectManager.loadProject(newName, getActivity());
clearCheckedItems();
initializeList();
} catch (ProjectException projectException) {
Log.e(TAG, "Renaming an incompatible project isn't possible", projectException);
Utils.showErrorDialog(getActivity(), R.string.error_rename_incompatible_project);
}
}
private void showDetailsFragment() {
Bundle bundle = new Bundle();
bundle.putSerializable(ShowDetailsFragment.SELECTED_PROJECT_KEY, projectToEdit);
((MyProjectsActivity) getActivity()).loadFragment(ShowDetailsFragment.class, bundle, true);
}
private void showSetDescriptionDialog() {
projectToEdit = projectAdapter.getItem(selectedProjectPosition);
Project project = StorageHandler.getInstance().loadProject(projectToEdit.projectName, getActivity());
SetDescriptionDialog dialog = new SetDescriptionDialog(R.string.set_description, R.string.description,
project.getDescription(), this);
dialog.show(getActivity().getFragmentManager(), SetDescriptionDialog.DIALOG_FRAGMENT_TAG);
}
@Override
public void setDescription(String newDescription) {
Project project = StorageHandler.getInstance().loadProject(projectToEdit.projectName, getActivity());
project.setDescription(newDescription);
StorageHandler.getInstance().saveProject(project);
initializeList();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
return true;
}
return super.onOptionsItemSelected(item);
}
private void initializeDefaultProjectAfterDelete() {
final ProjectManager projectManager = ProjectManager.getInstance();
getActivity().findViewById(R.id.fragment_container).setVisibility(View.GONE);
getActivity().findViewById(R.id.progress_circle).setVisibility(View.VISIBLE);
Runnable r = new Runnable() {
@Override
public void run() {
projectManager.initializeDefaultProject(getActivity());
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
getActivity().findViewById(R.id.fragment_container).setVisibility(View.VISIBLE);
getActivity().findViewById(R.id.progress_circle).setVisibility(View.GONE);
initializeList();
}
});
}
};
(new Thread(r)).start();
}
@Override
public void onLoadProjectSuccess(boolean startProjectActivity) {
Intent intent = new Intent(getActivity(), ProjectActivity.class);
intent.putExtra(Constants.PROJECT_OPENED_FROM_PROJECTS_LIST, true);
getActivity().startActivity(intent);
}
@Override
public void onLoadProjectFailure() {
getActivity().findViewById(R.id.fragment_container).setVisibility(View.VISIBLE);
}
}