/* * 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.net.URL; import java.util.ArrayList; import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jface.wizard.IWizardContainer; import org.eclipse.osgi.util.NLS; import com.motorola.studio.android.codeutils.CodeUtilsActivator; import com.motorola.studio.android.codeutils.i18n.CodeUtilsNLS; import com.motorola.studio.android.common.exception.AndroidException; import com.motorola.studio.android.common.log.StudioLogger; import com.motorola.studio.android.common.log.UsageDataConstants; import com.motorola.studio.android.common.utilities.FileUtil; import com.motorola.studio.android.manifest.AndroidProjectManifestFile; import com.motorola.studio.android.model.java.WidgetProviderClass; 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.ApplicationNode; 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.MetadataNode; import com.motorola.studio.android.model.manifest.dom.ReceiverNode; 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.model.resources.types.StringNode; import com.motorola.studio.android.resources.AndroidProjectResources; /** * Android WidgetProvider abstraction. */ public class WidgetProvider extends Launcher { /** * Constant used to define the name of the super class of the widget provider building block. * We can't use the AndroidConstansts class since there's no definition there for this yet. */ public static final String WIDGET_PROVIDER_SUPER_CLASS = "android.appwidget.AppWidgetProvider"; // Constants for monitor progress private static final int MANIFEST_UPDATING_STEPS = 6; private static final int RESOURCES_UPDATING_STEPS = 3; private static final String COPY_WIDGET_TEMPLATES_TASK_NAME = "Copying widget template files."; // Metadata node name private static final String METADATA_NODE_NAME = "android.appwidget.provider"; // Action node name private static final String ACTION_NODE_NAME = "android.appwidget.action.APPWIDGET_UPDATE"; // Widget info xml file private static final String WIDGET_INFO_FILE_NAME = "widget_info"; private static final String WIDGET_PROVIDER_RESOURCE_LABEL_SUFFIX = "WidgetProviderLabel"; // Directory constants private static final String RES_DIR = "res/"; private static final String XML_DIR = "xml/"; private static final String LAYOUT_DIR = "layout/"; private static final String DRAWABLE = "drawable"; private static final String WIDGET_TEMPLATE_FOLDER = "templates/widget_project/"; private static final String WIDGET_INITIAL_LAYOUT_XML = "widget_initial_layout.xml"; private static final String WIDGET_INFO_XML = "widget_info.xml"; private static final String ICON = "icon.png"; private static final String WIDGET_ICON_PATH = "icons/obj16/plate16.png"; /** * Constructor */ public WidgetProvider() { super(WIDGET_PROVIDER_SUPER_CLASS); } /* * (non-Javadoc) * @see com.motorola.studio.android.model.BuildingBlockModel#getStatus() */ @Override public IStatus getStatus() { return super.getStatus(); } /* * (non-Javadoc) * @see com.motorola.studio.android.model.IWizardModel#needMoreInformation() */ @Override public boolean needMoreInformation() { return false; } /** * Create the wigdet class and add it to the manifest file. * @return True if the widget class was successfully create and added to the manifest file. Otherwise, returns false. * */ @Override public boolean save(IWizardContainer container, IProgressMonitor monitor) throws AndroidException { boolean classCreated = createWidgetProviderClass(monitor); boolean addedOnManifest = false; boolean createdWidgetInfoFiles = false; if (classCreated) { addedOnManifest = createWidgetProviderOnManifest(monitor); // Logs to UDC the widget provider creation StudioLogger.collectUsageData(UsageDataConstants.WHAT_WIDGETPROVIDER_CREATED, //$NON-NLS-1$ UsageDataConstants.KIND_WIDGETPROVIDER, "", //$NON-NLS-1$ CodeUtilsActivator.PLUGIN_ID, CodeUtilsActivator.getDefault().getBundle() .getVersion().toString()); } if (addedOnManifest) { createdWidgetInfoFiles = createWidgetInfoFiles(monitor); } // Logs all permissions used in UDC log super.save(container, monitor); return classCreated && addedOnManifest && createdWidgetInfoFiles; } /* * Creates the WidgetProvider java class * * @param monitor the progress monitor * * @return true if the class has been created or false otherwise * @throws AndroidException */ private boolean createWidgetProviderClass(IProgressMonitor monitor) throws AndroidException { boolean created = false; monitor.subTask(CodeUtilsNLS.UI_WidgetProvider_CreatingTheWidgetProviderJavaClass); WidgetProviderClass widgetProviderClass = new WidgetProviderClass(getName(), getPackageFragment().getElementName()); try { createJavaClassFile(widgetProviderClass, monitor); created = true; } catch (JavaModelException e) { String errMsg = NLS.bind(CodeUtilsNLS.EXC_Receiver_CannotCreateTheReceiverClass, getName(), e.getLocalizedMessage()); throw new AndroidException(errMsg); } catch (AndroidException e) { String errMsg = NLS.bind(CodeUtilsNLS.EXC_Receiver_CannotCreateTheReceiverClass, getName(), e.getLocalizedMessage()); throw new AndroidException(errMsg); } return created; } /* * Creates the Widget Provider 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 createWidgetProviderOnManifest(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()) { permissionsNames.add(i.getName()); } for (String intentFilterPermission : getIntentFilterPermissionsAsArray()) { if (!permissionsNames.contains(intentFilterPermission)) { manifestNode.addChild(new UsesPermissionNode(intentFilterPermission)); } } boolean widgetProviderExists = false; String classQualifier = (getPackageFragment().getElementName() .equals(manifestNode.getPackageName()) ? "" : getPackageFragment() .getElementName()) + "."; // Check if the building block already exists in the manifest file for (ReceiverNode receiverNode : applicationNode.getReceiverNodes()) { if (receiverNode.getName().equals(getName())) { widgetProviderExists = true; break; } } monitor.worked(1); // Create the receiver node that declares the widget provider if (!widgetProviderExists) { ReceiverNode widgetProviderNode = new ReceiverNode(classQualifier + getName()); String widgetProviderLabel = createWidgetProviderLabel(monitor); if (widgetProviderLabel != null) { widgetProviderNode.setLabel(AndroidProjectResources.STRING_CALL_PREFIX + widgetProviderLabel); } // Add a intent filter node with the correct action to the receiver node IntentFilterNode intentFilterNode = new IntentFilterNode(); ActionNode actionNode = new ActionNode(ACTION_NODE_NAME); intentFilterNode.addActionNode(actionNode); widgetProviderNode.addIntentFilterNode(intentFilterNode); // Add a metadada node to the receiver node MetadataNode metadataNode = new MetadataNode(METADATA_NODE_NAME); metadataNode.setResource(AndroidProjectResources.XML_CALL_PREFIX + WIDGET_INFO_FILE_NAME); widgetProviderNode.addMetadataNode(metadataNode); applicationNode.addReceiverNode(widgetProviderNode); monitor.worked(1); monitor.subTask(CodeUtilsNLS.UI_Common_SavingTheAndroidManifestXMLFile); AndroidProjectManifestFile.saveToProject(getProject(), androidManifestFile, true); created = true; monitor.worked(1); } } } catch (AndroidException e) { String errMsg = NLS.bind(CodeUtilsNLS.EXC_Receiver_CannotUpdateTheManifestFile, getName(), e.getLocalizedMessage()); throw new AndroidException(errMsg); } catch (CoreException e) { String errMsg = NLS.bind(CodeUtilsNLS.EXC_Receiver_CannotUpdateTheManifestFile, getName(), e.getLocalizedMessage()); throw new AndroidException(errMsg); } finally { monitor.done(); } return created; } /* * Adds the Widget 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 createWidgetProviderLabel(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() + WIDGET_PROVIDER_RESOURCE_LABEL_SUFFIX); StringNode strNode = new 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; } private boolean createWidgetInfoFiles(IProgressMonitor monitor) throws AndroidException { boolean created = false; try { CodeUtilsActivator.getDefault(); monitor.beginTask("", 100); monitor.subTask(COPY_WIDGET_TEMPLATES_TASK_NAME); monitor.worked(10); IFolder resFolder = getProject().getFolder(RES_DIR); // get project folders IResource[] resList = resFolder.members(IResource.FOLDER); // looks forward icon.png resource boolean iconExist = false; for (int i = 0; i < resList.length; i++) { // inside drawable folders if (resList[i].getName().indexOf(DRAWABLE) >= 0) { IFile iconFile = ((IFolder) resList[i]).getFile(ICON); if (iconFile.exists()) { iconExist = true; break; } } } // creates the icon if it does not exist if (!iconExist) { IFile imageFile = getProject().getFile( RES_DIR + IPath.SEPARATOR + DRAWABLE + IPath.SEPARATOR + ICON); URL imgUrl = CodeUtilsActivator.getDefault().getBundle().getEntry(WIDGET_ICON_PATH); if (imgUrl != null) { IFolder drawablefolder = getProject().getFolder(RES_DIR + DRAWABLE + IPath.SEPARATOR); // creates drawable folder if it does not exist if (!drawablefolder.exists()) { FileUtil.createProjectFolder(getProject(), RES_DIR, DRAWABLE + IPath.SEPARATOR, monitor); } imageFile.create(imgUrl.openStream(), IResource.NONE, new SubProgressMonitor( monitor, 1000)); } } // Create an "xml" folder inside the "res" folder of the project FileUtil.createProjectFolder(getProject(), RES_DIR, XML_DIR, monitor); // Copy the "widget_info" file to the xml folder and the "widget_initial_layout" file to the layout folder IFolder layoutfolder = getProject().getFolder(RES_DIR + LAYOUT_DIR); IFolder xmlFolder = getProject().getFolder(RES_DIR + XML_DIR); if (!layoutfolder.exists()) { FileUtil.createProjectFolder(getProject(), RES_DIR, LAYOUT_DIR + IPath.SEPARATOR, monitor); } if (!xmlFolder.exists()) { FileUtil.createProjectFolder(getProject(), RES_DIR, XML_DIR + IPath.SEPARATOR, monitor); } IFile initialLayoutFile = layoutfolder.getFile(WIDGET_INITIAL_LAYOUT_XML); if (!initialLayoutFile.exists()) { // Retrieve template file and create it at destination URL templateURL = CodeUtilsActivator.getDefault().getBundle() .getEntry(WIDGET_TEMPLATE_FOLDER + WIDGET_INITIAL_LAYOUT_XML); if (templateURL != null) { initialLayoutFile.create(templateURL.openStream(), false, monitor); } else { throw new AndroidException(); } monitor.worked(20); } IFile widgetInfoFile = xmlFolder.getFile(WIDGET_INFO_XML); if (!widgetInfoFile.exists()) { // Retrieve template file and create it at destination URL templateURL = CodeUtilsActivator.getDefault().getBundle() .getEntry(WIDGET_TEMPLATE_FOLDER + WIDGET_INFO_XML); if (templateURL != null) { widgetInfoFile.create(templateURL.openStream(), false, monitor); } else { throw new AndroidException(); } monitor.worked(20); } created = true; } catch (Exception e) { String errMsg = NLS.bind(CodeUtilsNLS.EXC_WidgetProvider_CannotCopyTemplateFiles, getName(), e.getLocalizedMessage()); throw new AndroidException(errMsg); } finally { monitor.done(); } return created; } }