/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.motorola.studio.android.model;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.wizard.IWizardContainer;
import org.eclipse.osgi.util.NLS;
import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS;
import com.motorola.studio.android.common.IAndroidConstants;
import com.motorola.studio.android.common.exception.AndroidException;
import com.motorola.studio.android.manifest.AndroidProjectManifestFile;
import com.motorola.studio.android.model.java.ActivityClass;
import com.motorola.studio.android.model.manifest.AndroidManifestFile;
import com.motorola.studio.android.model.manifest.dom.ActionNode;
import com.motorola.studio.android.model.manifest.dom.ActivityNode;
import com.motorola.studio.android.model.manifest.dom.ApplicationNode;
import com.motorola.studio.android.model.manifest.dom.CategoryNode;
import com.motorola.studio.android.model.manifest.dom.IntentFilterNode;
import com.motorola.studio.android.model.manifest.dom.ManifestNode;
import com.motorola.studio.android.model.manifest.dom.UsesPermissionNode;
import com.motorola.studio.android.model.resources.ResourceFile;
import com.motorola.studio.android.model.resources.types.AbstractResourceNode.NodeType;
import com.motorola.studio.android.model.resources.types.ResourcesNode;
import com.motorola.studio.android.resources.AndroidProjectResources;
/**
* Activity Controller Model.
* As part of a MVC architecture, this class should communicate with the Wizard UI
* to provide all needed information to create a functional Activity.
*/
public class Activity extends Launcher
{
private static final String INTENT_ACTION_MAIN_NAME = "android.intent.action.MAIN";
private static final String INTENT_CATEGORY_LAUNCHER_NAME = "android.intent.category.LAUNCHER";
private static final String ACTIVITY_RESOURCE_LABEL_SUFFIX = "ActivityLabel"; //$NON-NLS-1$
private static final int MANIFEST_UPDATING_STEPS = 6;
private static final int RESOURCES_UPDATING_STEPS = 3;
private boolean onStart = false;
HashMap<String, String> workspaceResourceNames = null;
/**
* Boolean flag to tell if the Activity will be set as MAIN or not in the AndroidManifest.
*/
private boolean isMainActivity = false;
/**
* Check if the onStart Method should be created
* @return
*/
public boolean isOnStart()
{
return onStart;
}
/**
* Change the onStart create property
* @param onStart
*/
public void setOnStart(boolean onStart)
{
this.onStart = onStart;
}
/**
* Constructor for the Activity.
*/
public Activity()
{
super(IAndroidConstants.CLASS_ACTIVITY);
}
/*
* Enables finish button when page is filled.
*/
public boolean needMoreInformation()
{
boolean needInfo = false;
IStatus status = getStatus();
if (status.getSeverity() > IStatus.WARNING)
{
needInfo = true;
}
return needInfo;
}
/**
* Package name (based on project name declared on manifest)
* @param project
* @return
* @throws CoreException Exception thrown in case there are problems handling the android project
* @throws AndroidException Exception thrown in case there are problems handling the android project
*/
protected String getManifestPackageName(IProject project) throws AndroidException,
CoreException
{
// get android application name
AndroidManifestFile androidManifestFile =
AndroidProjectManifestFile.getFromProject(project);
String appNamespace = "";
if (androidManifestFile != null)
{
ManifestNode manifestNode = androidManifestFile.getManifestNode();
appNamespace = manifestNode.getPackageName();
}
// return the android application name along with a persistence constant
return appNamespace;
}
/* (non-Javadoc)
* @see com.motorola.studio.android.model.IWizardModel#save(org.eclipse.jface.wizard.IWizardContainer, org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
public boolean save(IWizardContainer container, IProgressMonitor monitor)
throws AndroidException
{
workspaceResourceNames = new HashMap<String, String>();
boolean classCreated = createActivityClass(monitor);
boolean addedOnManifest = false;
if (classCreated)
{
addedOnManifest = createActivityOnManifest(monitor);
}
// Logs all permissions used in UDC log
super.save(container, monitor);
try
{
ResourcesPlugin.getWorkspace().getRoot()
.refreshLocal(IResource.DEPTH_INFINITE, monitor);
}
catch (CoreException e)
{
// do nothing
}
return classCreated && addedOnManifest;
}
/**
* Creates the Activity java class
*
* @param monitor the progress monitor
*
* @return true if the class has been created or false otherwise
* @throws AndroidException
*/
private boolean createActivityClass(IProgressMonitor monitor) throws AndroidException
{
boolean created = false;
monitor.subTask(CodeUtilsNLS.UI_Activity_CreatingTheActivityJavaClass);
ActivityClass activityClass = null;
try
{
activityClass =
new ActivityClass(getName(), getPackageFragment().getElementName(), onStart);
createJavaClassFile(activityClass, monitor);
created = true;
}
catch (JavaModelException e)
{
String errMsg =
NLS.bind(CodeUtilsNLS.EXC_Activity_CannotCreateTheActivityClass, getName(),
e.getLocalizedMessage());
throw new AndroidException(errMsg);
}
catch (AndroidException e)
{
String errMsg =
NLS.bind(CodeUtilsNLS.EXC_Activity_CannotCreateTheActivityClass, getName(),
e.getLocalizedMessage());
throw new AndroidException(errMsg);
}
return created;
}
/**
* Creates the Activity class entry on AndroidManifest.xml file
*
* @param monitor the progress monitor
*
* @return true if the entry has been added or false otherwise
* @throws AndroidException
*/
private boolean createActivityOnManifest(IProgressMonitor monitor) throws AndroidException
{
boolean created = false;
try
{
int totalWork = MANIFEST_UPDATING_STEPS + RESOURCES_UPDATING_STEPS;
monitor.beginTask("", totalWork);
monitor.subTask(CodeUtilsNLS.UI_Common_UpdatingTheAndroidManifestXMLFile);
AndroidManifestFile androidManifestFile =
AndroidProjectManifestFile.getFromProject(getProject());
monitor.worked(1);
ManifestNode manifestNode =
androidManifestFile != null ? androidManifestFile.getManifestNode() : null;
ApplicationNode applicationNode =
manifestNode != null ? manifestNode.getApplicationNode() : null;
monitor.worked(1);
if (applicationNode != null)
{
// Adds the added permission nodes to manifest file
List<String> permissionsNames = new ArrayList<String>();
for (UsesPermissionNode i : manifestNode.getUsesPermissionNodes())
{
if (!permissionsNames.contains(i.getName()))
{
permissionsNames.add(i.getName());
}
}
for (String intentFilterPermission : getIntentFilterPermissionsAsArray())
{
if (!permissionsNames.contains(intentFilterPermission))
{
manifestNode.addChild(new UsesPermissionNode(intentFilterPermission));
}
}
boolean activityExists = false;
// Existing activity, if exists
ActivityNode existingActivity = null;
String classQualifier =
(getPackageFragment().getElementName()
.equals(manifestNode.getPackageName()) ? "" : getPackageFragment() //$NON-NLS-1$
.getElementName()) + "."; //$NON-NLS-1$
for (ActivityNode activityNode : applicationNode.getActivityNodes())
{
if (activityNode.getName()
.substring(activityNode.getName().lastIndexOf('.') + 1)
.equals(getName()))
{
activityExists = true;
existingActivity = activityNode;
break;
}
}
if (isMainActivity)
{
boolean actionRemoved = false;
//check if there is a main activity. If so, removes actions and intent filter
for (ActivityNode activityNode : applicationNode.getActivityNodes())
{
if ((existingActivity != null) && existingActivity.equals(activityNode))
{
continue;
}
List<IntentFilterNode> intentList = activityNode.getIntentFilterNodes();
for (IntentFilterNode currentIntent : intentList)
{
actionRemoved = false;
List<ActionNode> actionList = currentIntent.getActionNodes();
for (ActionNode currentAction : actionList)
{
if (currentAction.getName().equals(INTENT_ACTION_MAIN_NAME))
{
currentIntent.removeActionNode(currentAction);
actionRemoved = true;
}
}
//if INTENT_ACTION_MAIN_NAME is found remove INTENT_CATEGORY_LAUNCHER_NAME too
if (actionRemoved)
{
List<CategoryNode> categoryList = currentIntent.getCategoryNodes();
for (CategoryNode currentCategory : categoryList)
{
if (currentCategory.getName().equals(
INTENT_CATEGORY_LAUNCHER_NAME))
{
currentIntent.removeCategoryNode(currentCategory);
}
}
}
//remove intent filter if empty
if (actionRemoved && (currentIntent.getChildren().length == 0))
{
activityNode.removeIntentFilterNode(currentIntent);
}
}
}
}
monitor.worked(1);
if (!activityExists)
{
ActivityNode activityNode = new ActivityNode(classQualifier + getName());
String activityLabel = createActivityLabel(monitor);
if (activityLabel != null)
{
activityNode.setLabel(AndroidProjectResources.STRING_CALL_PREFIX
+ activityLabel);
}
IntentFilterNode intentFilterNode = new IntentFilterNode();
for (String intentFilterAction : getIntentFilterActionsAsArray())
{
intentFilterNode.addActionNode(new ActionNode(intentFilterAction));
}
for (String intentFilterCategory : getIntentFilterCategoriesAsArray())
{
intentFilterNode.addCategoryNode(new CategoryNode(intentFilterCategory));
}
// Check if we need to insert a filter action and filter category setting this activity as MAIN
if (isMainActivity)
{
intentFilterNode.addActionNode(new ActionNode(INTENT_ACTION_MAIN_NAME));
intentFilterNode.addCategoryNode(new CategoryNode(
INTENT_CATEGORY_LAUNCHER_NAME));
}
if (intentFilterNode.getChildren().length > 0)
{
activityNode.addIntentFilterNode(intentFilterNode);
}
applicationNode.addActivityNode(activityNode);
monitor.worked(1);
monitor.subTask(CodeUtilsNLS.UI_Common_SavingTheAndroidManifestXMLFile);
AndroidProjectManifestFile.saveToProject(getProject(), androidManifestFile,
true);
created = true;
monitor.worked(1);
}
else
{
if (isMainActivity)
{
boolean hasMainAction = false;
boolean hasLauncherCategory = false;
// Check if the existing activity already has the MAIN and LAUNCHER intents
if (existingActivity != null)
{
// Retrieve list of intent nodes
List<IntentFilterNode> intentFilterNodeList =
existingActivity.getIntentFilterNodes();
// Create a intent filter in case it does not exist
if (intentFilterNodeList.size() < 1)
{
IntentFilterNode intentNode = new IntentFilterNode();
intentFilterNodeList.add(intentNode);
existingActivity.addIntentFilterNode(intentNode);
}
for (IntentFilterNode intentFilterNode : intentFilterNodeList)
{
// Retrieve a list of intent actions
List<ActionNode> actionNodes = intentFilterNode.getActionNodes();
for (ActionNode actionNode : actionNodes)
{
if (actionNode.getName().equals(INTENT_ACTION_MAIN_NAME))
{
hasMainAction = true;
}
break;
}
// Retrieve a list of intent categories
List<CategoryNode> categoryNodes =
intentFilterNode.getCategoryNodes();
for (CategoryNode categoryNode : categoryNodes)
{
if (categoryNode.getName()
.equals(INTENT_CATEGORY_LAUNCHER_NAME))
{
hasLauncherCategory = true;
}
break;
}
// If both the action and launcher are missing, insert them and break the loop to avoid duplicates
if (!hasMainAction && !hasLauncherCategory)
{
intentFilterNode.addActionNode(new ActionNode(
INTENT_ACTION_MAIN_NAME));
intentFilterNode.addCategoryNode(new CategoryNode(
INTENT_CATEGORY_LAUNCHER_NAME));
break;
}
}
}
monitor.subTask(CodeUtilsNLS.UI_Common_SavingTheAndroidManifestXMLFile);
AndroidProjectManifestFile.saveToProject(getProject(), androidManifestFile,
true);
created = true;
monitor.worked(1);
}
else
{
created = true;
}
}
}
}
catch (AndroidException e)
{
String errMsg =
NLS.bind(CodeUtilsNLS.EXC_Activity_CannotUpdateTheManifestFile, getName(),
e.getLocalizedMessage());
throw new AndroidException(errMsg);
}
catch (CoreException e)
{
String errMsg =
NLS.bind(CodeUtilsNLS.EXC_Activity_CannotUpdateTheManifestFile, getName(),
e.getLocalizedMessage());
throw new AndroidException(errMsg);
}
finally
{
monitor.done();
}
return created;
}
/**
* Adds the Activity label value on the strings resource file
*
* @param monitor the progress monitor
*
* @return The label value if it has been added to the strings resource file or null otherwise
* @throws AndroidException
*/
private String createActivityLabel(IProgressMonitor monitor) throws AndroidException
{
String resLabel = null;
if ((getLabel() != null) && (getLabel().trim().length() > 0))
{
try
{
monitor.subTask(CodeUtilsNLS.UI_Common_UpdatingTheStringsResourceFile);
ResourceFile stringsFile =
AndroidProjectResources.getResourceFile(getProject(), NodeType.String);
monitor.worked(1);
if (stringsFile.getResourcesNode() == null)
{
stringsFile.addResourceEntry(new ResourcesNode());
}
resLabel =
stringsFile.getNewResourceName(getName() + ACTIVITY_RESOURCE_LABEL_SUFFIX);
com.motorola.studio.android.model.resources.types.StringNode strNode =
new com.motorola.studio.android.model.resources.types.StringNode(resLabel);
strNode.setNodeValue(getLabel());
stringsFile.getResourcesNode().addChildNode(strNode);
monitor.worked(1);
AndroidProjectResources
.saveResourceFile(getProject(), stringsFile, NodeType.String);
monitor.worked(1);
}
catch (CoreException e)
{
String errMsg =
NLS.bind(CodeUtilsNLS.EXC_Activity_CannotCreateTheActivityLabel,
e.getLocalizedMessage());
throw new AndroidException(errMsg);
}
catch (AndroidException e)
{
String errMsg =
NLS.bind(CodeUtilsNLS.EXC_Activity_CannotCreateTheActivityLabel,
e.getLocalizedMessage());
throw new AndroidException(errMsg);
}
}
return resLabel;
}
/**
* @return The default onStart() method signature including return value and visibility level.
*/
public String getOnStartMessage()
{
return "protected void onStart()"; //$NON-NLS-1$
}
/**
* @return True if this activity is to be set as main activity. Otherwise, returns false.
*/
public boolean isMainActivity()
{
return isMainActivity;
}
/**
* @param isMainActivity Set to true if this activity is to be set as main activity.
*/
public void setMainActivity(boolean isMainActivity)
{
this.isMainActivity = isMainActivity;
}
}