/*
* 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.wizards.installapp;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.dialogs.DialogPage;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.ui.PlatformUI;
import com.motorola.studio.android.AndroidPlugin;
import com.motorola.studio.android.common.log.StudioLogger;
import com.motorola.studio.android.i18n.AndroidNLS;
import com.motorola.studio.android.wizards.elements.FileChooser;
import com.motorola.studio.android.wizards.installapp.DeployWizard.INSTALL_TYPE;
/**
* Wizard Page used by Deploy Wizard
*/
public class DeployWizardPage extends WizardPage
{
private FileChooser fileChooser = null;
private String initialPackagePath = null;
private String packageSelectionMessage = null;
private String packagetext = null;
private final String packageExtension = "apk";
private Button overwiteRadio = null;
private Button uninstallRadio = null;
private Button doNothingRadio = null;
private static final String contextId = AndroidPlugin.PLUGIN_ID + ".install_app";
private static INSTALL_TYPE installType;
private final String DSA_FILE_EXTENSION = ".DSA";
private final String RSA_FILE_EXTENSION = ".RSA";
private final String SF_FILE_EXTENSION = ".SF";
private static String lastUsedPackage = null;
/**
* Constructor
*
* @param initialPackagePath
* @param selectPCKMessage
* Message asking for package selection
*/
public DeployWizardPage(String initialPackagePath, String selectPCKMessage,
String browseButtonText, String packagetext)
{
super("");
if ((browseButtonText == null) || (packagetext == null) || (selectPCKMessage == null))
{
throw new IllegalArgumentException("Could not create Deploy Wizard: null argument");
}
if (initialPackagePath == null)
{
this.initialPackagePath = lastUsedPackage != null ? lastUsedPackage : "";
}
else
{
this.initialPackagePath = initialPackagePath;
}
this.packagetext = packagetext;
packageSelectionMessage = selectPCKMessage;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets
* .Composite)
*/
public void createControl(Composite parent)
{
// Main composite for the UI
Composite mainComposite = new Composite(parent, SWT.FILL);
PlatformUI.getWorkbench().getHelpSystem().setHelp(mainComposite, contextId);
mainComposite.setLayout(new GridLayout());
mainComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL, GridData.FILL_VERTICAL,
true, true));
// Package group
Group packageGroup = new Group(mainComposite, SWT.NONE);
packageGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
packageGroup.setLayout(new GridLayout(3, false));
packageGroup.setText(packagetext);
fileChooser = new FileChooser(packageGroup, SWT.NONE, null);
fileChooser.setFilterExtensions(new String[]
{
"*." + packageExtension
});
fileChooser.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
fileChooser.addModifyListener(new ModifyListener()
{
public void modifyText(ModifyEvent e)
{
validateSelection();
}
});
createOptionButtons(mainComposite);
mainComposite.pack();
setPageComplete(false);
loadInitialValues();
setControl(mainComposite);
}
private void createOptionButtons(Composite mainComposite)
{
GridData data = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1);
overwiteRadio = new Button(mainComposite, SWT.RADIO);
overwiteRadio.setText(AndroidNLS.UI_DeployWizardPage_ReplaceApp);
overwiteRadio.setSelection(true);
overwiteRadio.setLayoutData(data);
overwiteRadio.setData(INSTALL_TYPE.OVERWRITE);
overwiteRadio.addSelectionListener(new SelectionAdapter()
{
/*
* (non-Javadoc)
*
* @see
* org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse
* .swt.events.SelectionEvent)
*/
@Override
public void widgetSelected(SelectionEvent e)
{
installType = (INSTALL_TYPE) overwiteRadio.getData();
}
});
uninstallRadio = new Button(mainComposite, SWT.RADIO);
uninstallRadio.setText(AndroidNLS.UI_DeployWizardPage_UninstallApp);
uninstallRadio.setSelection(true);
uninstallRadio.setLayoutData(data);
uninstallRadio.setData(INSTALL_TYPE.UNINSTALL);
uninstallRadio.addSelectionListener(new SelectionAdapter()
{
/*
* (non-Javadoc)
*
* @see
* org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse
* .swt.events.SelectionEvent)
*/
@Override
public void widgetSelected(SelectionEvent e)
{
installType = (INSTALL_TYPE) uninstallRadio.getData();
}
});
doNothingRadio = new Button(mainComposite, SWT.RADIO);
doNothingRadio.setText(AndroidNLS.UI_DeployWizardPage_DoNothingApp);
doNothingRadio.setSelection(true);
doNothingRadio.setLayoutData(data);
doNothingRadio.setData(INSTALL_TYPE.DO_NOTHING);
doNothingRadio.addSelectionListener(new SelectionAdapter()
{
/*
* (non-Javadoc)
*
* @see
* org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse
* .swt.events.SelectionEvent)
*/
@Override
public void widgetSelected(SelectionEvent e)
{
installType = (INSTALL_TYPE) doNothingRadio.getData();
}
});
overwiteRadio.setSelection(true);
uninstallRadio.setSelection(false);
doNothingRadio.setSelection(false);
installType = (INSTALL_TYPE.OVERWRITE);
}
/**
* Load the initial values to be filled in the wizard
*/
private void loadInitialValues()
{
if ((initialPackagePath != null) && (initialPackagePath.length() != 0))
{
fileChooser.setText(initialPackagePath);
validateSelection();
}
else
{
setMessage(packageSelectionMessage, DialogPage.NONE);
}
}
/**
* Validates the selected package and device instance setting the
* appropriated messages and errors
*/
private synchronized void validateSelection()
{
String packagePath = fileChooser.getText();
if (isValidPackage(packagePath))
{
setErrorMessage(null);
setPageComplete(true);
}
else
{
setPageComplete(false);
}
}
/**
* Verify if a package is valid MPKG
*
* @param packagePath
* @return TRUE if the package is valid or FALSE otherwise
*/
private boolean isValidPackage(String packagePath)
{
boolean result = false;
Path path = new Path(packagePath);
String extension = path.getFileExtension();
// testing if the entered path is a folder
result = path.toFile().isFile();
if (!result)
{
setErrorMessage(AndroidNLS.UI_DeployWizardPage_PackageIsAFolder);
}
else
{
result =
((extension != null) && extension.equals(packageExtension) && path
.isValidPath(path.toString()));
if (!result)
{
setErrorMessage(AndroidNLS.UI_DeployWizardPage_InvalidPath);
}
else
{
// Test if file exists
result = path.toFile().exists();
if (!result)
{
setErrorMessage(AndroidNLS.UI_DeployWizardPage_FileDoesNotExist);
}
}
}
if (result)
{
setMessage("", DialogPage.NONE);
//Test if the package is valid
result = isPackageSigned(packagePath);
if (!result)
{
setErrorMessage(AndroidNLS.UI_DeployWizardPage_NotSignedMessage);
}
}
return result;
}
/**
* Verify if the package is signed based on the
* existence of an .SF file and a corresponding
* DSA or RSA file.
*
* @param packagePath
* @return TRUE if the package is signed
*/
private synchronized boolean isPackageSigned(String packagePath)
{
// Temporary placeholders for the package entries
List<String> SFFiles = new ArrayList<String>();
List<String> RSAFiles = new ArrayList<String>();
List<String> DSAFiles = new ArrayList<String>();
//Temporary result
boolean result = false;
JarFile jar = null;
try
{
jar = new JarFile(packagePath, false);
Enumeration<JarEntry> enu = jar.entries();
//interact over the elements of the package
while (enu.hasMoreElements())
{
JarEntry entry = enu.nextElement();
if (entry.getName().toUpperCase().endsWith(SF_FILE_EXTENSION))
{
// Mounts the list of SF files
SFFiles.add(entry.getName().toUpperCase());
}
else if (entry.getName().toUpperCase().endsWith(RSA_FILE_EXTENSION))
{
// Mounts the list of RSA files
RSAFiles.add(entry.getName().toUpperCase());
}
else if (entry.getName().toUpperCase().endsWith(DSA_FILE_EXTENSION))
{
// Mounts the list of DSA files
DSAFiles.add(entry.getName().toUpperCase());
}
}
if (!SFFiles.isEmpty())
{
for (String sfFile : SFFiles)
{
// Interacts over the list of SF files until it ends or until a correspondent DSA or RSA is found
Path p = new Path(sfFile);
sfFile = p.removeFileExtension().toString();
result =
(DSAFiles.contains(sfFile + DSA_FILE_EXTENSION) || RSAFiles
.contains(sfFile + RSA_FILE_EXTENSION));
}
}
}
catch (Exception e)
{
// Could not read the jar file
StudioLogger.error(DeployWizardPage.class, "Deploy: Could not verify file "
+ packagePath, e);
}
finally
{
if (jar != null)
{
try
{
jar.close();
}
catch (IOException e)
{
StudioLogger.error(DeployWizardPage.class,
"Error closing package after verification", e);
}
}
}
return result;
}
/**
* Gets the selected package path
*
* @return the package Path
*/
public String getPackagePath()
{
String packagePath = fileChooser.getText();
if (isValidPackage(packagePath))
{
lastUsedPackage = packagePath;
}
return packagePath;
}
/**
* Return true if the application should be replaced in the case it is
* already installed on the device
*
*/
public INSTALL_TYPE canOverwrite()
{
return installType;
}
}