// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
package com.google.appinventor.client.explorer.youngandroid;
import com.google.appinventor.client.ErrorReporter;
import com.google.appinventor.client.GalleryClient;
import com.google.appinventor.client.Ode;
import com.google.appinventor.client.OdeAsyncCallback;
import com.google.appinventor.client.boxes.ProjectListBox;
import com.google.appinventor.client.boxes.ViewerBox;
import com.google.appinventor.client.explorer.project.Project;
import com.google.appinventor.client.tracking.Tracking;
import com.google.appinventor.client.widgets.Toolbar;
import com.google.appinventor.client.wizards.youngandroid.NewYoungAndroidProjectWizard;
import com.google.appinventor.shared.rpc.project.GalleryApp;
import com.google.appinventor.shared.rpc.project.GallerySettings;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Window;
import java.util.List;
import static com.google.appinventor.client.Ode.MESSAGES;
/**
* The project toolbar houses command buttons in the Young Android Project tab.
*
*/
public class ProjectToolbar extends Toolbar {
private static final String WIDGET_NAME_NEW = "New";
private static final String WIDGET_NAME_DELETE = "Delete";
private static final String WIDGET_NAME_PUBLISH_OR_UPDATE = "PublishOrUpdate";
private boolean isReadOnly;
/**
* Initializes and assembles all commands into buttons in the toolbar.
*/
public ProjectToolbar() {
super();
isReadOnly = Ode.getInstance().isReadOnly();
addButton(new ToolbarItem(WIDGET_NAME_NEW, MESSAGES.newProjectMenuItem(),
new NewAction(this)));
addButton(new ToolbarItem(WIDGET_NAME_DELETE, MESSAGES.deleteProjectButton(),
new DeleteAction()));
addButton(new ToolbarItem(WIDGET_NAME_PUBLISH_OR_UPDATE, MESSAGES.publishToGalleryButton(),
new PublishOrUpdateAction()));
updateButtons();
}
public void setPublishOrUpdateButtonVisible(boolean visible){
setButtonVisible(WIDGET_NAME_PUBLISH_OR_UPDATE, visible);
}
private static class NewAction implements Command {
ProjectToolbar parent;
public NewAction(ProjectToolbar parent) {
this.parent = parent;
}
@Override
public void execute() {
if (Ode.getInstance().screensLocked()) {
return; // Refuse to switch if locked (save file happening)
}
// Disabled the Start New Project button. We do this because on slow machines people
// click it multiple times while the wizard (below) is starting. This then causes
// a second wizard to start and a very confused user experience.
// We will enable the button again when we re-visit the Project List page
parent.setButtonEnabled(WIDGET_NAME_NEW, false);
new NewYoungAndroidProjectWizard(parent).center();
// The wizard will switch to the design view when the new
// project is created.
}
}
private static class DeleteAction implements Command {
@Override
public void execute() {
Ode.getInstance().getEditorManager().saveDirtyEditors(new Command() {
@Override
public void execute() {
List<Project> selectedProjects =
ProjectListBox.getProjectListBox().getProjectList().getSelectedProjects();
if (selectedProjects.size() > 0) {
// Show one confirmation window for selected projects.
if (deleteConfirmation(selectedProjects)) {
for (Project project : selectedProjects) {
deleteProject(project);
}
}
} else {
// The user can select a project to resolve the
// error.
ErrorReporter.reportInfo(MESSAGES.noProjectSelectedForDelete());
}
}
});
}
private boolean deleteConfirmation(List<Project> projects) {
String message;
GallerySettings gallerySettings = GalleryClient.getInstance().getGallerySettings();
if (projects.size() == 1) {
if (projects.get(0).isPublished()) {
message = MESSAGES.confirmDeleteSinglePublishedProject(projects.get(0).getProjectName());
} else {
message = MESSAGES.confirmDeleteSingleProject(projects.get(0).getProjectName());
}
} else {
StringBuilder sb = new StringBuilder();
String separator = "";
for (Project project : projects) {
sb.append(separator).append(project.getProjectName());
separator = ", ";
}
String projectNames = sb.toString();
if(!gallerySettings.galleryEnabled()){
message = MESSAGES.confirmDeleteManyProjects(projectNames);
} else {
message = MESSAGES.confirmDeleteManyProjectsWithGalleryOn(projectNames);
}
}
return Window.confirm(message);
}
private void deleteProject(Project project) {
Tracking.trackEvent(Tracking.PROJECT_EVENT,
Tracking.PROJECT_ACTION_DELETE_PROJECT_YA, project.getProjectName());
final long projectId = project.getProjectId();
Ode ode = Ode.getInstance();
boolean isCurrentProject = (projectId == ode.getCurrentYoungAndroidProjectId());
ode.getEditorManager().closeProjectEditor(projectId);
if (isCurrentProject) {
// If we're deleting the project that is currently open in the Designer we
// need to clear the ViewerBox first.
ViewerBox.getViewerBox().clear();
}
if (project.isPublished()) {
doDeleteGalleryApp(project.getGalleryId());
}
// Make sure that we delete projects even if they are not open.
doDeleteProject(projectId);
}
private void doDeleteProject(final long projectId) {
Ode.getInstance().getProjectService().deleteProject(projectId,
new OdeAsyncCallback<Void>(
// failure message
MESSAGES.deleteProjectError()) {
@Override
public void onSuccess(Void result) {
Ode.getInstance().getProjectManager().removeProject(projectId);
// Show a welcome dialog in case there are no
// projects saved.
if (Ode.getInstance().getProjectManager().getProjects().size() == 0) {
Ode.getInstance().createNoProjectsDialog(true);
}
}
});
}
private void doDeleteGalleryApp(final long galleryId) {
Ode.getInstance().getGalleryService().deleteApp(galleryId,
new OdeAsyncCallback<Void>(
// failure message
MESSAGES.galleryDeleteError()) {
@Override
public void onSuccess(Void result) {
// need to update gallery list
GalleryClient gallery = GalleryClient.getInstance();
gallery.appWasChanged();
}
});
}
}
private static class PublishOrUpdateAction implements Command {
@Override
public void execute() {
List<Project> selectedProjects =
ProjectListBox.getProjectListBox().getProjectList().getSelectedProjects();
if (selectedProjects.size() == 1) {
Project currentSelectedProject = ProjectListBox.getProjectListBox().getProjectList()
.getSelectedProjects().get(0);
if(!currentSelectedProject.isPublished()){
// app is not yet published
publishToGallery(currentSelectedProject);
}else{
updateGalleryApp(currentSelectedProject);
}
} else {
// The publish/update button will be disabled if selectedProjects.size != 1
// This should not happen, but just in case
ErrorReporter.reportInfo(MESSAGES.wrongNumberProjectSelectedForPublishOrUpdate());
}
}
private void publishToGallery(Project p) {
// first create an app object with default data
final GalleryApp app = new GalleryApp(p.getProjectName(), p.getProjectId(),
p.getProjectName(), p.getGalleryId(), p.getAttributionId());
Ode.getInstance().switchToGalleryAppView(app, GalleryPage.NEWAPP);
}
private void updateGalleryApp(Project p) {
// setup what happens when we load the app in
final OdeAsyncCallback<GalleryApp> callback = new OdeAsyncCallback<GalleryApp>(
MESSAGES.galleryError()) {
@Override
public void onSuccess(GalleryApp app) {
// the server has returned us something
int editStatus=GalleryPage.UPDATEAPP;
Ode.getInstance().switchToGalleryAppView(app, editStatus);
}
};
// ok, this is below the call back, but of course it is done first
Ode.getInstance().getGalleryService().getApp(p.getGalleryId(),callback);
}
}
/**
* Enables and/or disables buttons based on how many projects exist
* (in the case of "Download All Projects") or are selected (in the case
* of "Delete" and "Download Source").
*/
public void updateButtons() {
ProjectList projectList = ProjectListBox.getProjectListBox().getProjectList();
int numProjects = projectList.getNumProjects();
int numSelectedProjects = projectList.getNumSelectedProjects();
if (isReadOnly) { // If we are read-only, we disable all buttons
setButtonText(WIDGET_NAME_PUBLISH_OR_UPDATE, MESSAGES.publishToGalleryButton());
setButtonEnabled(WIDGET_NAME_NEW, false);
setButtonEnabled(WIDGET_NAME_DELETE, false);
setButtonEnabled(WIDGET_NAME_PUBLISH_OR_UPDATE, false);
Ode.getInstance().getTopToolbar().fileDropDown.setItemEnabled(MESSAGES.exportProjectMenuItem(),
numSelectedProjects > 0);
Ode.getInstance().getTopToolbar().fileDropDown.setItemEnabled(MESSAGES.exportAllProjectsMenuItem(),
numSelectedProjects > 0);
return;
}
setButtonEnabled(WIDGET_NAME_DELETE, numSelectedProjects > 0);
setButtonEnabled(WIDGET_NAME_PUBLISH_OR_UPDATE, numSelectedProjects == 1);
if(numSelectedProjects == 1 && ProjectListBox.getProjectListBox().getProjectList()
.getSelectedProjects().get(0).isPublished()){
setButtonText(WIDGET_NAME_PUBLISH_OR_UPDATE, MESSAGES.updateGalleryAppButton());
}else{
setButtonText(WIDGET_NAME_PUBLISH_OR_UPDATE, MESSAGES.publishToGalleryButton());
}
Ode.getInstance().getTopToolbar().fileDropDown.setItemEnabled(MESSAGES.deleteProjectMenuItem(),
numSelectedProjects > 0);
Ode.getInstance().getTopToolbar().fileDropDown.setItemEnabled(MESSAGES.exportProjectMenuItem(),
numSelectedProjects > 0);
Ode.getInstance().getTopToolbar().fileDropDown.setItemEnabled(MESSAGES.exportAllProjectsMenuItem(),
numSelectedProjects > 0);
}
// If we started a project, then the start button was disabled (to avoid
// a second press while the new project wizard was starting (aka we "debounce"
// the button). When the person switches to the projects list view again (here)
// we re-enable it.
public void enableStartButton() {
if (!isReadOnly) {
setButtonEnabled(WIDGET_NAME_NEW, true);
}
}
}