auto import from //depot/cupcake/@136594
This commit is contained in:
@@ -174,14 +174,16 @@ public class SoftKeyboard extends InputMethodService
|
|||||||
// We now look for a few special variations of text that will
|
// We now look for a few special variations of text that will
|
||||||
// modify our behavior.
|
// modify our behavior.
|
||||||
int variation = attribute.inputType & EditorInfo.TYPE_MASK_VARIATION;
|
int variation = attribute.inputType & EditorInfo.TYPE_MASK_VARIATION;
|
||||||
if (variation == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD) {
|
if (variation == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD ||
|
||||||
|
variation == EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) {
|
||||||
// Do not display predictions / what the user is typing
|
// Do not display predictions / what the user is typing
|
||||||
// when they are entering a password.
|
// when they are entering a password.
|
||||||
mPredictionOn = false;
|
mPredictionOn = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
|
if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
|
||||||
|| variation == EditorInfo.TYPE_TEXT_VARIATION_URI) {
|
|| variation == EditorInfo.TYPE_TEXT_VARIATION_URI
|
||||||
|
|| variation == EditorInfo.TYPE_TEXT_VARIATION_FILTER) {
|
||||||
// Our predictions are not useful for e-mail addresses
|
// Our predictions are not useful for e-mail addresses
|
||||||
// or URIs.
|
// or URIs.
|
||||||
mPredictionOn = false;
|
mPredictionOn = false;
|
||||||
@@ -207,6 +209,7 @@ public class SoftKeyboard extends InputMethodService
|
|||||||
// For all unknown input types, default to the alphabetic
|
// For all unknown input types, default to the alphabetic
|
||||||
// keyboard with no special features.
|
// keyboard with no special features.
|
||||||
mCurKeyboard = mQwertyKeyboard;
|
mCurKeyboard = mQwertyKeyboard;
|
||||||
|
updateShiftKeyState(attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the label on the enter key, depending on what the application
|
// Update the label on the enter key, depending on what the application
|
||||||
@@ -428,7 +431,11 @@ public class SoftKeyboard extends InputMethodService
|
|||||||
private void updateShiftKeyState(EditorInfo attr) {
|
private void updateShiftKeyState(EditorInfo attr) {
|
||||||
if (attr != null
|
if (attr != null
|
||||||
&& mInputView != null && mQwertyKeyboard == mInputView.getKeyboard()) {
|
&& mInputView != null && mQwertyKeyboard == mInputView.getKeyboard()) {
|
||||||
int caps = getCurrentInputConnection().getCursorCapsMode(attr.inputType);
|
int caps = 0;
|
||||||
|
EditorInfo ei = getCurrentInputEditorInfo();
|
||||||
|
if (ei != null && ei.inputType != EditorInfo.TYPE_NULL) {
|
||||||
|
caps = getCurrentInputConnection().getCursorCapsMode(attr.inputType);
|
||||||
|
}
|
||||||
mInputView.setShifted(mCapsLock || caps != 0);
|
mInputView.setShifted(mCapsLock || caps != 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -553,7 +560,7 @@ public class SoftKeyboard extends InputMethodService
|
|||||||
final int length = mComposing.length();
|
final int length = mComposing.length();
|
||||||
if (length > 1) {
|
if (length > 1) {
|
||||||
mComposing.delete(length - 1, length);
|
mComposing.delete(length - 1, length);
|
||||||
getCurrentInputConnection().setComposingText(mComposing, mComposing.length());
|
getCurrentInputConnection().setComposingText(mComposing, 1);
|
||||||
updateCandidates();
|
updateCandidates();
|
||||||
} else if (length > 0) {
|
} else if (length > 0) {
|
||||||
mComposing.setLength(0);
|
mComposing.setLength(0);
|
||||||
@@ -594,7 +601,7 @@ public class SoftKeyboard extends InputMethodService
|
|||||||
}
|
}
|
||||||
if (isAlphabet(primaryCode) && mPredictionOn) {
|
if (isAlphabet(primaryCode) && mPredictionOn) {
|
||||||
mComposing.append((char) primaryCode);
|
mComposing.append((char) primaryCode);
|
||||||
getCurrentInputConnection().setComposingText(mComposing, mComposing.length());
|
getCurrentInputConnection().setComposingText(mComposing, 1);
|
||||||
updateShiftKeyState(getCurrentInputEditorInfo());
|
updateShiftKeyState(getCurrentInputEditorInfo());
|
||||||
updateCandidates();
|
updateCandidates();
|
||||||
} else {
|
} else {
|
||||||
@@ -605,7 +612,7 @@ public class SoftKeyboard extends InputMethodService
|
|||||||
|
|
||||||
private void handleClose() {
|
private void handleClose() {
|
||||||
commitTyped(getCurrentInputConnection());
|
commitTyped(getCurrentInputConnection());
|
||||||
dismissSoftInput(0);
|
requestHideSelf(0);
|
||||||
mInputView.closing();
|
mInputView.closing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,11 +40,12 @@ public class Application {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String... args) {
|
public static void main(final String... args) {
|
||||||
initUserInterface();
|
initUserInterface();
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
MainFrame frame = new MainFrame();
|
String arg = args.length > 0 ? args[0] : null;
|
||||||
|
MainFrame frame = new MainFrame(arg);
|
||||||
frame.setDefaultCloseOperation(MainFrame.EXIT_ON_CLOSE);
|
frame.setDefaultCloseOperation(MainFrame.EXIT_ON_CLOSE);
|
||||||
frame.setLocationRelativeTo(null);
|
frame.setLocationRelativeTo(null);
|
||||||
frame.setVisible(true);
|
frame.setVisible(true);
|
||||||
|
|||||||
@@ -651,6 +651,7 @@ class ImageEditorPanel extends JPanel {
|
|||||||
|
|
||||||
private int lastPositionX;
|
private int lastPositionX;
|
||||||
private int lastPositionY;
|
private int lastPositionY;
|
||||||
|
private int currentButton;
|
||||||
private boolean showCursor;
|
private boolean showCursor;
|
||||||
|
|
||||||
private JLabel helpLabel;
|
private JLabel helpLabel;
|
||||||
@@ -687,16 +688,20 @@ class ImageEditorPanel extends JPanel {
|
|||||||
addMouseListener(new MouseAdapter() {
|
addMouseListener(new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void mousePressed(MouseEvent event) {
|
public void mousePressed(MouseEvent event) {
|
||||||
paint(event.getX(), event.getY(), event.isShiftDown() ? MouseEvent.BUTTON3 :
|
// Store the button here instead of retrieving it again in MouseDragged
|
||||||
event.getButton());
|
// below, because on linux, calling MouseEvent.getButton() for the drag
|
||||||
|
// event returns 0, which appears to be technically correct (no button
|
||||||
|
// changed state).
|
||||||
|
currentButton = event.isShiftDown() ? MouseEvent.BUTTON3 : event.getButton();
|
||||||
|
paint(event.getX(), event.getY(), currentButton);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
addMouseMotionListener(new MouseMotionAdapter() {
|
addMouseMotionListener(new MouseMotionAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void mouseDragged(MouseEvent event) {
|
public void mouseDragged(MouseEvent event) {
|
||||||
if (!checkLockedRegion(event.getX(), event.getY())) {
|
if (!checkLockedRegion(event.getX(), event.getY())) {
|
||||||
paint(event.getX(), event.getY(), event.isShiftDown() ? MouseEvent.BUTTON3 :
|
// use the stored button, see note above
|
||||||
event.getButton());
|
paint(event.getX(), event.getY(), currentButton);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,14 +40,24 @@ public class MainFrame extends JFrame {
|
|||||||
private JMenuItem saveMenuItem;
|
private JMenuItem saveMenuItem;
|
||||||
private ImageEditorPanel imageEditor;
|
private ImageEditorPanel imageEditor;
|
||||||
|
|
||||||
public MainFrame() throws HeadlessException {
|
public MainFrame(String path) throws HeadlessException {
|
||||||
super("Draw 9-patch");
|
super("Draw 9-patch");
|
||||||
|
|
||||||
buildActions();
|
buildActions();
|
||||||
buildMenuBar();
|
buildMenuBar();
|
||||||
buildContent();
|
buildContent();
|
||||||
|
|
||||||
showOpenFilePanel();
|
if (path == null) {
|
||||||
|
showOpenFilePanel();
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
File file = new File(path);
|
||||||
|
BufferedImage img = GraphicsUtilities.loadCompatibleImage(file.toURI().toURL());
|
||||||
|
showImageEditor(img, file.getAbsolutePath());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
showOpenFilePanel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// pack();
|
// pack();
|
||||||
setSize(1024, 600);
|
setSize(1024, 600);
|
||||||
|
|||||||
@@ -346,7 +346,7 @@ public class ApkBuilder extends BaseBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// also check the final file(s)!
|
// also check the final file(s)!
|
||||||
String finalPackageName = getFileName(project, null /*config*/);
|
String finalPackageName = ProjectHelper.getApkFilename(project, null /*config*/);
|
||||||
if (mBuildFinalPackage == false) {
|
if (mBuildFinalPackage == false) {
|
||||||
tmp = outputFolder.findMember(finalPackageName);
|
tmp = outputFolder.findMember(finalPackageName);
|
||||||
if (tmp == null || (tmp instanceof IFile &&
|
if (tmp == null || (tmp instanceof IFile &&
|
||||||
@@ -359,7 +359,7 @@ public class ApkBuilder extends BaseBuilder {
|
|||||||
Set<Entry<String, String>> entrySet = configs.entrySet();
|
Set<Entry<String, String>> entrySet = configs.entrySet();
|
||||||
|
|
||||||
for (Entry<String, String> entry : entrySet) {
|
for (Entry<String, String> entry : entrySet) {
|
||||||
String filename = getFileName(project, entry.getKey());
|
String filename = ProjectHelper.getApkFilename(project, entry.getKey());
|
||||||
|
|
||||||
tmp = outputFolder.findMember(filename);
|
tmp = outputFolder.findMember(filename);
|
||||||
if (tmp == null || (tmp instanceof IFile &&
|
if (tmp == null || (tmp instanceof IFile &&
|
||||||
@@ -409,7 +409,7 @@ public class ApkBuilder extends BaseBuilder {
|
|||||||
Set<Entry<String, String>> entrySet = configs.entrySet();
|
Set<Entry<String, String>> entrySet = configs.entrySet();
|
||||||
for (Entry<String, String> entry : entrySet) {
|
for (Entry<String, String> entry : entrySet) {
|
||||||
String packageFilepath = osBinPath + File.separator +
|
String packageFilepath = osBinPath + File.separator +
|
||||||
getFileName(project, entry.getKey());
|
ProjectHelper.getApkFilename(project, entry.getKey());
|
||||||
|
|
||||||
finalPackage = new File(packageFilepath);
|
finalPackage = new File(packageFilepath);
|
||||||
finalPackage.delete();
|
finalPackage.delete();
|
||||||
@@ -532,7 +532,7 @@ public class ApkBuilder extends BaseBuilder {
|
|||||||
|
|
||||||
// make the filename for the apk to generate
|
// make the filename for the apk to generate
|
||||||
String apkOsFilePath = osBinPath + File.separator +
|
String apkOsFilePath = osBinPath + File.separator +
|
||||||
getFileName(project, entry.getKey());
|
ProjectHelper.getApkFilename(project, entry.getKey());
|
||||||
if (finalPackage(resPath, classesDexPath, apkOsFilePath, javaProject,
|
if (finalPackage(resPath, classesDexPath, apkOsFilePath, javaProject,
|
||||||
referencedJavaProjects) == false) {
|
referencedJavaProjects) == false) {
|
||||||
return referencedProjects;
|
return referencedProjects;
|
||||||
@@ -1117,19 +1117,6 @@ public class ApkBuilder extends BaseBuilder {
|
|||||||
return list.toArray(new IJavaProject[list.size()]);
|
return list.toArray(new IJavaProject[list.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the apk filename for the given project
|
|
||||||
* @param project The project.
|
|
||||||
* @param config An optional config name. Can be null.
|
|
||||||
*/
|
|
||||||
private static String getFileName(IProject project, String config) {
|
|
||||||
if (config != null) {
|
|
||||||
return project.getName() + "-" + config + AndroidConstants.DOT_ANDROID_PACKAGE; //$NON-NLS-1$
|
|
||||||
}
|
|
||||||
|
|
||||||
return project.getName() + AndroidConstants.DOT_ANDROID_PACKAGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks a {@link IFile} to make sure it should be packaged as standard resources.
|
* Checks a {@link IFile} to make sure it should be packaged as standard resources.
|
||||||
* @param file the IFile representing the file.
|
* @param file the IFile representing the file.
|
||||||
|
|||||||
@@ -665,4 +665,17 @@ public final class ProjectHelper {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the apk filename for the given project
|
||||||
|
* @param project The project.
|
||||||
|
* @param config An optional config name. Can be null.
|
||||||
|
*/
|
||||||
|
public static String getApkFilename(IProject project, String config) {
|
||||||
|
if (config != null) {
|
||||||
|
return project.getName() + "-" + config + AndroidConstants.DOT_ANDROID_PACKAGE; //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
|
||||||
|
return project.getName() + AndroidConstants.DOT_ANDROID_PACKAGE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,20 @@ package com.android.ide.eclipse.adt.project.export;
|
|||||||
|
|
||||||
import com.android.ide.eclipse.adt.AdtPlugin;
|
import com.android.ide.eclipse.adt.AdtPlugin;
|
||||||
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||||
|
import com.android.ide.eclipse.common.project.BaseProjectHelper;
|
||||||
import com.android.jarutils.KeystoreHelper;
|
import com.android.jarutils.KeystoreHelper;
|
||||||
import com.android.jarutils.SignedJarBuilder;
|
import com.android.jarutils.SignedJarBuilder;
|
||||||
import com.android.jarutils.DebugKeyProvider.IKeyGenOutput;
|
import com.android.jarutils.DebugKeyProvider.IKeyGenOutput;
|
||||||
import com.android.jarutils.DebugKeyProvider.KeytoolException;
|
import com.android.jarutils.DebugKeyProvider.KeytoolException;
|
||||||
|
|
||||||
|
import org.eclipse.core.resources.IFolder;
|
||||||
import org.eclipse.core.resources.IProject;
|
import org.eclipse.core.resources.IProject;
|
||||||
import org.eclipse.core.resources.IResource;
|
import org.eclipse.core.resources.IResource;
|
||||||
|
import org.eclipse.core.resources.IncrementalProjectBuilder;
|
||||||
|
import org.eclipse.core.runtime.CoreException;
|
||||||
import org.eclipse.core.runtime.IAdaptable;
|
import org.eclipse.core.runtime.IAdaptable;
|
||||||
|
import org.eclipse.core.runtime.IProgressMonitor;
|
||||||
|
import org.eclipse.jface.operation.IRunnableWithProgress;
|
||||||
import org.eclipse.jface.resource.ImageDescriptor;
|
import org.eclipse.jface.resource.ImageDescriptor;
|
||||||
import org.eclipse.jface.viewers.IStructuredSelection;
|
import org.eclipse.jface.viewers.IStructuredSelection;
|
||||||
import org.eclipse.jface.wizard.Wizard;
|
import org.eclipse.jface.wizard.Wizard;
|
||||||
@@ -35,12 +41,14 @@ import org.eclipse.swt.events.VerifyListener;
|
|||||||
import org.eclipse.swt.widgets.Text;
|
import org.eclipse.swt.widgets.Text;
|
||||||
import org.eclipse.ui.IExportWizard;
|
import org.eclipse.ui.IExportWizard;
|
||||||
import org.eclipse.ui.IWorkbench;
|
import org.eclipse.ui.IWorkbench;
|
||||||
|
import org.eclipse.ui.PlatformUI;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
@@ -49,6 +57,9 @@ import java.security.KeyStore.PrivateKeyEntry;
|
|||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export wizard to export an apk signed with a release key/certificate.
|
* Export wizard to export an apk signed with a release key/certificate.
|
||||||
@@ -66,7 +77,12 @@ public final class ExportWizard extends Wizard implements IExportWizard {
|
|||||||
static final String PROPERTY_KEYSTORE = "keystore"; //$NON-NLS-1$
|
static final String PROPERTY_KEYSTORE = "keystore"; //$NON-NLS-1$
|
||||||
static final String PROPERTY_ALIAS = "alias"; //$NON-NLS-1$
|
static final String PROPERTY_ALIAS = "alias"; //$NON-NLS-1$
|
||||||
static final String PROPERTY_DESTINATION = "destination"; //$NON-NLS-1$
|
static final String PROPERTY_DESTINATION = "destination"; //$NON-NLS-1$
|
||||||
|
static final String PROPERTY_FILENAME = "baseFilename"; //$NON-NLS-1$
|
||||||
|
|
||||||
|
static final int APK_FILE_SOURCE = 0;
|
||||||
|
static final int APK_FILE_DEST = 1;
|
||||||
|
static final int APK_COUNT = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base page class for the ExportWizard page. This class add the {@link #onShow()} callback.
|
* Base page class for the ExportWizard page. This class add the {@link #onShow()} callback.
|
||||||
*/
|
*/
|
||||||
@@ -131,7 +147,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
|
|||||||
* Calls {@link #setErrorMessage(String)} and {@link #setPageComplete(boolean)} based on a
|
* Calls {@link #setErrorMessage(String)} and {@link #setPageComplete(boolean)} based on a
|
||||||
* {@link Throwable} object.
|
* {@link Throwable} object.
|
||||||
*/
|
*/
|
||||||
protected final void onException(Throwable t) {
|
protected void onException(Throwable t) {
|
||||||
String message = getExceptionMessage(t);
|
String message = getExceptionMessage(t);
|
||||||
|
|
||||||
setErrorMessage(message);
|
setErrorMessage(message);
|
||||||
@@ -155,9 +171,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
|
|||||||
private PrivateKey mPrivateKey;
|
private PrivateKey mPrivateKey;
|
||||||
private X509Certificate mCertificate;
|
private X509Certificate mCertificate;
|
||||||
|
|
||||||
private String mDestinationPath;
|
private File mDestinationParentFolder;
|
||||||
private String mApkFilePath;
|
|
||||||
private String mApkFileName;
|
|
||||||
|
|
||||||
private ExportWizardPage mKeystoreSelectionPage;
|
private ExportWizardPage mKeystoreSelectionPage;
|
||||||
private ExportWizardPage mKeyCreationPage;
|
private ExportWizardPage mKeyCreationPage;
|
||||||
@@ -168,6 +182,8 @@ public final class ExportWizard extends Wizard implements IExportWizard {
|
|||||||
|
|
||||||
private List<String> mExistingAliases;
|
private List<String> mExistingAliases;
|
||||||
|
|
||||||
|
private Map<String, String[]> mApkMap;
|
||||||
|
|
||||||
public ExportWizard() {
|
public ExportWizard() {
|
||||||
setHelpAvailable(false); // TODO have help
|
setHelpAvailable(false); // TODO have help
|
||||||
setWindowTitle("Export Android Application");
|
setWindowTitle("Export Android Application");
|
||||||
@@ -186,24 +202,46 @@ public final class ExportWizard extends Wizard implements IExportWizard {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean performFinish() {
|
public boolean performFinish() {
|
||||||
// first we make sure export is fine if the destination file already exists
|
|
||||||
File f = new File(mDestinationPath);
|
|
||||||
if (f.isFile()) {
|
|
||||||
if (AdtPlugin.displayPrompt("Export Wizard",
|
|
||||||
"File already exists. Do you want to overwrite it?") == false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// save the properties
|
// save the properties
|
||||||
ProjectHelper.saveStringProperty(mProject, PROPERTY_KEYSTORE, mKeystore);
|
ProjectHelper.saveStringProperty(mProject, PROPERTY_KEYSTORE, mKeystore);
|
||||||
ProjectHelper.saveStringProperty(mProject, PROPERTY_ALIAS, mKeyAlias);
|
ProjectHelper.saveStringProperty(mProject, PROPERTY_ALIAS, mKeyAlias);
|
||||||
ProjectHelper.saveStringProperty(mProject, PROPERTY_DESTINATION, mDestinationPath);
|
ProjectHelper.saveStringProperty(mProject, PROPERTY_DESTINATION,
|
||||||
|
mDestinationParentFolder.getAbsolutePath());
|
||||||
|
ProjectHelper.saveStringProperty(mProject, PROPERTY_FILENAME,
|
||||||
|
mApkMap.get(null)[APK_FILE_DEST]);
|
||||||
|
|
||||||
|
// run the export in an UI runnable.
|
||||||
|
IWorkbench workbench = PlatformUI.getWorkbench();
|
||||||
|
final boolean[] result = new boolean[1];
|
||||||
try {
|
try {
|
||||||
|
workbench.getProgressService().busyCursorWhile(new IRunnableWithProgress() {
|
||||||
|
public void run(IProgressMonitor monitor) throws InvocationTargetException,
|
||||||
|
InterruptedException {
|
||||||
|
try {
|
||||||
|
result[0] = doExport(monitor);
|
||||||
|
} finally {
|
||||||
|
monitor.done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
return false;
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean doExport(IProgressMonitor monitor) {
|
||||||
|
try {
|
||||||
|
// first we make sure the project is built
|
||||||
|
mProject.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, monitor);
|
||||||
|
|
||||||
|
// if needed, create the keystore and/or key.
|
||||||
if (mKeystoreCreationMode || mKeyCreationMode) {
|
if (mKeystoreCreationMode || mKeyCreationMode) {
|
||||||
final ArrayList<String> output = new ArrayList<String>();
|
final ArrayList<String> output = new ArrayList<String>();
|
||||||
if (KeystoreHelper.createNewStore(
|
boolean createdStore = KeystoreHelper.createNewStore(
|
||||||
mKeystore,
|
mKeystore,
|
||||||
null /*storeType*/,
|
null /*storeType*/,
|
||||||
mKeystorePassword,
|
mKeystorePassword,
|
||||||
@@ -218,7 +256,9 @@ public final class ExportWizard extends Wizard implements IExportWizard {
|
|||||||
public void out(String message) {
|
public void out(String message) {
|
||||||
output.add(message);
|
output.add(message);
|
||||||
}
|
}
|
||||||
}) == false) {
|
});
|
||||||
|
|
||||||
|
if (createdStore == false) {
|
||||||
// keystore creation error!
|
// keystore creation error!
|
||||||
displayError(output.toArray(new String[output.size()]));
|
displayError(output.toArray(new String[output.size()]));
|
||||||
return false;
|
return false;
|
||||||
@@ -245,20 +285,42 @@ public final class ExportWizard extends Wizard implements IExportWizard {
|
|||||||
|
|
||||||
// check the private key/certificate again since it may have been created just above.
|
// check the private key/certificate again since it may have been created just above.
|
||||||
if (mPrivateKey != null && mCertificate != null) {
|
if (mPrivateKey != null && mCertificate != null) {
|
||||||
FileOutputStream fos = new FileOutputStream(mDestinationPath);
|
// get the output folder of the project to export.
|
||||||
SignedJarBuilder builder = new SignedJarBuilder(fos, mPrivateKey, mCertificate);
|
// this is where we'll find the built apks to resign and export.
|
||||||
|
IFolder outputIFolder = BaseProjectHelper.getOutputFolder(mProject);
|
||||||
// get the input file.
|
if (outputIFolder == null) {
|
||||||
FileInputStream fis = new FileInputStream(mApkFilePath);
|
return false;
|
||||||
try {
|
|
||||||
builder.writeZip(fis, null /* filter */);
|
|
||||||
} finally {
|
|
||||||
fis.close();
|
|
||||||
}
|
}
|
||||||
|
String outputOsPath = outputIFolder.getLocation().toOSString();
|
||||||
builder.close();
|
|
||||||
fos.close();
|
|
||||||
|
|
||||||
|
// now generate the packages.
|
||||||
|
Set<Entry<String, String[]>> set = mApkMap.entrySet();
|
||||||
|
for (Entry<String, String[]> entry : set) {
|
||||||
|
String[] defaultApk = entry.getValue();
|
||||||
|
String srcFilename = defaultApk[APK_FILE_SOURCE];
|
||||||
|
String destFilename = defaultApk[APK_FILE_DEST];
|
||||||
|
|
||||||
|
FileOutputStream fos = new FileOutputStream(
|
||||||
|
new File(mDestinationParentFolder, destFilename));
|
||||||
|
SignedJarBuilder builder = new SignedJarBuilder(fos, mPrivateKey, mCertificate);
|
||||||
|
|
||||||
|
// get the input file.
|
||||||
|
FileInputStream fis = new FileInputStream(new File(outputOsPath, srcFilename));
|
||||||
|
|
||||||
|
// add the content of the source file to the output file, and sign it at
|
||||||
|
// the same time.
|
||||||
|
try {
|
||||||
|
builder.writeZip(fis, null /* filter */);
|
||||||
|
// close the builder: write the final signature files, and close the archive.
|
||||||
|
builder.close();
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
fis.close();
|
||||||
|
} finally {
|
||||||
|
fos.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
@@ -271,6 +333,8 @@ public final class ExportWizard extends Wizard implements IExportWizard {
|
|||||||
displayError(e);
|
displayError(e);
|
||||||
} catch (KeytoolException e) {
|
} catch (KeytoolException e) {
|
||||||
displayError(e);
|
displayError(e);
|
||||||
|
} catch (CoreException e) {
|
||||||
|
displayError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -282,10 +346,10 @@ public final class ExportWizard extends Wizard implements IExportWizard {
|
|||||||
// a private key/certificate or the creation mode. In creation mode, unless
|
// a private key/certificate or the creation mode. In creation mode, unless
|
||||||
// all the key/keystore info is valid, the user cannot reach the last page, so there's
|
// all the key/keystore info is valid, the user cannot reach the last page, so there's
|
||||||
// no need to check them again here.
|
// no need to check them again here.
|
||||||
return mApkFilePath != null &&
|
return mApkMap != null && mApkMap.size() > 0 &&
|
||||||
((mPrivateKey != null && mCertificate != null)
|
((mPrivateKey != null && mCertificate != null)
|
||||||
|| mKeystoreCreationMode || mKeyCreationMode) &&
|
|| mKeystoreCreationMode || mKeyCreationMode) &&
|
||||||
mDestinationPath != null;
|
mDestinationParentFolder != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -334,18 +398,12 @@ public final class ExportWizard extends Wizard implements IExportWizard {
|
|||||||
return mProject;
|
return mProject;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setProject(IProject project, String apkFilePath, String filename) {
|
void setProject(IProject project) {
|
||||||
mProject = project;
|
mProject = project;
|
||||||
mApkFilePath = apkFilePath;
|
|
||||||
mApkFileName = filename;
|
|
||||||
|
|
||||||
updatePageOnChange(ExportWizardPage.DATA_PROJECT);
|
updatePageOnChange(ExportWizardPage.DATA_PROJECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
String getApkFilename() {
|
|
||||||
return mApkFileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setKeystore(String path) {
|
void setKeystore(String path) {
|
||||||
mKeystore = path;
|
mKeystore = path;
|
||||||
mPrivateKey = null;
|
mPrivateKey = null;
|
||||||
@@ -444,10 +502,16 @@ public final class ExportWizard extends Wizard implements IExportWizard {
|
|||||||
mCertificate = certificate;
|
mCertificate = certificate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDestination(String path) {
|
void setDestination(File parentFolder, Map<String, String[]> apkMap) {
|
||||||
mDestinationPath = path;
|
mDestinationParentFolder = parentFolder;
|
||||||
|
mApkMap = apkMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void resetDestination() {
|
||||||
|
mDestinationParentFolder = null;
|
||||||
|
mApkMap = null;
|
||||||
|
}
|
||||||
|
|
||||||
void updatePageOnChange(int changeMask) {
|
void updatePageOnChange(int changeMask) {
|
||||||
for (ExportWizardPage page : mPages) {
|
for (ExportWizardPage page : mPages) {
|
||||||
page.projectDataChanged(changeMask);
|
page.projectDataChanged(changeMask);
|
||||||
@@ -484,7 +548,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
|
|||||||
* <p/>If no Throwable in the chain has a valid message, the canonical name of the first
|
* <p/>If no Throwable in the chain has a valid message, the canonical name of the first
|
||||||
* exception is returned.
|
* exception is returned.
|
||||||
*/
|
*/
|
||||||
private static String getExceptionMessage(Throwable t) {
|
static String getExceptionMessage(Throwable t) {
|
||||||
String message = t.getMessage();
|
String message = t.getMessage();
|
||||||
if (message == null) {
|
if (message == null) {
|
||||||
Throwable cause = t.getCause();
|
Throwable cause = t.getCause();
|
||||||
|
|||||||
@@ -18,13 +18,18 @@ package com.android.ide.eclipse.adt.project.export;
|
|||||||
|
|
||||||
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
import com.android.ide.eclipse.adt.project.ProjectHelper;
|
||||||
import com.android.ide.eclipse.adt.project.export.ExportWizard.ExportWizardPage;
|
import com.android.ide.eclipse.adt.project.export.ExportWizard.ExportWizardPage;
|
||||||
|
import com.android.ide.eclipse.adt.sdk.Sdk;
|
||||||
|
|
||||||
import org.eclipse.core.resources.IProject;
|
import org.eclipse.core.resources.IProject;
|
||||||
import org.eclipse.swt.SWT;
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.custom.ScrolledComposite;
|
||||||
|
import org.eclipse.swt.events.ControlAdapter;
|
||||||
|
import org.eclipse.swt.events.ControlEvent;
|
||||||
import org.eclipse.swt.events.ModifyEvent;
|
import org.eclipse.swt.events.ModifyEvent;
|
||||||
import org.eclipse.swt.events.ModifyListener;
|
import org.eclipse.swt.events.ModifyListener;
|
||||||
import org.eclipse.swt.events.SelectionAdapter;
|
import org.eclipse.swt.events.SelectionAdapter;
|
||||||
import org.eclipse.swt.events.SelectionEvent;
|
import org.eclipse.swt.events.SelectionEvent;
|
||||||
|
import org.eclipse.swt.graphics.Rectangle;
|
||||||
import org.eclipse.swt.layout.GridData;
|
import org.eclipse.swt.layout.GridData;
|
||||||
import org.eclipse.swt.layout.GridLayout;
|
import org.eclipse.swt.layout.GridLayout;
|
||||||
import org.eclipse.swt.widgets.Button;
|
import org.eclipse.swt.widgets.Button;
|
||||||
@@ -47,6 +52,10 @@ import java.security.KeyStore.PrivateKeyEntry;
|
|||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Final page of the wizard that checks the key and ask for the ouput location.
|
* Final page of the wizard that checks the key and ask for the ouput location.
|
||||||
@@ -59,6 +68,12 @@ final class KeyCheckPage extends ExportWizardPage {
|
|||||||
private Text mDestination;
|
private Text mDestination;
|
||||||
private boolean mFatalSigningError;
|
private boolean mFatalSigningError;
|
||||||
private FormText mDetailText;
|
private FormText mDetailText;
|
||||||
|
/** The Apk Config map for the current project */
|
||||||
|
private Map<String, String> mApkConfig;
|
||||||
|
private ScrolledComposite mScrolledComposite;
|
||||||
|
|
||||||
|
private String mKeyDetails;
|
||||||
|
private String mDestinationDetails;
|
||||||
|
|
||||||
protected KeyCheckPage(ExportWizard wizard, String pageName) {
|
protected KeyCheckPage(ExportWizard wizard, String pageName) {
|
||||||
super(pageName);
|
super(pageName);
|
||||||
@@ -86,7 +101,7 @@ final class KeyCheckPage extends ExportWizardPage {
|
|||||||
mDestination.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
mDestination.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
|
||||||
mDestination.addModifyListener(new ModifyListener() {
|
mDestination.addModifyListener(new ModifyListener() {
|
||||||
public void modifyText(ModifyEvent e) {
|
public void modifyText(ModifyEvent e) {
|
||||||
onDestinationChange();
|
onDestinationChange(false /*forceDetailUpdate*/);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
final Button browseButton = new Button(composite, SWT.PUSH);
|
final Button browseButton = new Button(composite, SWT.PUSH);
|
||||||
@@ -97,7 +112,10 @@ final class KeyCheckPage extends ExportWizardPage {
|
|||||||
FileDialog fileDialog = new FileDialog(browseButton.getShell(), SWT.SAVE);
|
FileDialog fileDialog = new FileDialog(browseButton.getShell(), SWT.SAVE);
|
||||||
|
|
||||||
fileDialog.setText("Destination file name");
|
fileDialog.setText("Destination file name");
|
||||||
fileDialog.setFileName(mWizard.getApkFilename());
|
// get a default apk name based on the project
|
||||||
|
String filename = ProjectHelper.getApkFilename(mWizard.getProject(),
|
||||||
|
null /*config*/);
|
||||||
|
fileDialog.setFileName(filename);
|
||||||
|
|
||||||
String saveLocation = fileDialog.open();
|
String saveLocation = fileDialog.open();
|
||||||
if (saveLocation != null) {
|
if (saveLocation != null) {
|
||||||
@@ -106,9 +124,21 @@ final class KeyCheckPage extends ExportWizardPage {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
mDetailText = new FormText(composite, SWT.NONE);
|
mScrolledComposite = new ScrolledComposite(composite, SWT.V_SCROLL);
|
||||||
mDetailText.setLayoutData(gd = new GridData(GridData.FILL_BOTH));
|
mScrolledComposite.setLayoutData(gd = new GridData(GridData.FILL_BOTH));
|
||||||
gd.horizontalSpan = 3;
|
gd.horizontalSpan = 3;
|
||||||
|
mScrolledComposite.setExpandHorizontal(true);
|
||||||
|
mScrolledComposite.setExpandVertical(true);
|
||||||
|
|
||||||
|
mDetailText = new FormText(mScrolledComposite, SWT.NONE);
|
||||||
|
mScrolledComposite.setContent(mDetailText);
|
||||||
|
|
||||||
|
mScrolledComposite.addControlListener(new ControlAdapter() {
|
||||||
|
@Override
|
||||||
|
public void controlResized(ControlEvent e) {
|
||||||
|
updateScrolling();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
setControl(composite);
|
setControl(composite);
|
||||||
}
|
}
|
||||||
@@ -119,11 +149,14 @@ final class KeyCheckPage extends ExportWizardPage {
|
|||||||
if ((mProjectDataChanged & DATA_PROJECT) != 0) {
|
if ((mProjectDataChanged & DATA_PROJECT) != 0) {
|
||||||
// reset the destination from the content of the project
|
// reset the destination from the content of the project
|
||||||
IProject project = mWizard.getProject();
|
IProject project = mWizard.getProject();
|
||||||
|
mApkConfig = Sdk.getCurrent().getProjectApkConfigs(project);
|
||||||
|
|
||||||
String destination = ProjectHelper.loadStringProperty(project,
|
String destination = ProjectHelper.loadStringProperty(project,
|
||||||
ExportWizard.PROPERTY_DESTINATION);
|
ExportWizard.PROPERTY_DESTINATION);
|
||||||
if (destination != null) {
|
String filename = ProjectHelper.loadStringProperty(project,
|
||||||
mDestination.setText(destination);
|
ExportWizard.PROPERTY_FILENAME);
|
||||||
|
if (destination != null && filename != null) {
|
||||||
|
mDestination.setText(destination + File.separator + filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,11 +167,14 @@ final class KeyCheckPage extends ExportWizardPage {
|
|||||||
// reset the wizard with no key/cert to make it not finishable, unless a valid
|
// reset the wizard with no key/cert to make it not finishable, unless a valid
|
||||||
// key/cert is found.
|
// key/cert is found.
|
||||||
mWizard.setSigningInfo(null, null);
|
mWizard.setSigningInfo(null, null);
|
||||||
|
mPrivateKey = null;
|
||||||
|
mCertificate = null;
|
||||||
|
mKeyDetails = null;
|
||||||
|
|
||||||
if (mWizard.getKeystoreCreationMode() || mWizard.getKeyCreationMode()) {
|
if (mWizard.getKeystoreCreationMode() || mWizard.getKeyCreationMode()) {
|
||||||
int validity = mWizard.getValidity();
|
int validity = mWizard.getValidity();
|
||||||
StringBuilder sb = new StringBuilder(
|
StringBuilder sb = new StringBuilder(
|
||||||
String.format("<form><p>Certificate expires in %d years.</p>",
|
String.format("<p>Certificate expires in %d years.</p>",
|
||||||
validity));
|
validity));
|
||||||
|
|
||||||
if (validity < 25) {
|
if (validity < 25) {
|
||||||
@@ -149,8 +185,7 @@ final class KeyCheckPage extends ExportWizardPage {
|
|||||||
sb.append("<p>Android Market currently requires certificates to be valid until 2033.</p>");
|
sb.append("<p>Android Market currently requires certificates to be valid until 2033.</p>");
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append("</form>");
|
mKeyDetails = sb.toString();
|
||||||
mDetailText.setText(sb.toString(), true /* parseTags */, true /* expandURLs */);
|
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||||
@@ -192,10 +227,9 @@ final class KeyCheckPage extends ExportWizardPage {
|
|||||||
Calendar today = Calendar.getInstance();
|
Calendar today = Calendar.getInstance();
|
||||||
|
|
||||||
if (expirationCalendar.before(today)) {
|
if (expirationCalendar.before(today)) {
|
||||||
mDetailText.setText(String.format(
|
mKeyDetails = String.format(
|
||||||
"<form><p>Certificate expired on %s</p></form>",
|
"<p>Certificate expired on %s</p>",
|
||||||
mCertificate.getNotAfter().toString()),
|
mCertificate.getNotAfter().toString());
|
||||||
true /* parseTags */, true /* expandURLs */);
|
|
||||||
|
|
||||||
// fatal error = nothing can make the page complete.
|
// fatal error = nothing can make the page complete.
|
||||||
mFatalSigningError = true;
|
mFatalSigningError = true;
|
||||||
@@ -207,7 +241,7 @@ final class KeyCheckPage extends ExportWizardPage {
|
|||||||
mWizard.setSigningInfo(mPrivateKey, mCertificate);
|
mWizard.setSigningInfo(mPrivateKey, mCertificate);
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder(String.format(
|
StringBuilder sb = new StringBuilder(String.format(
|
||||||
"<form><p>Certificate expires on %s.</p>",
|
"<p>Certificate expires on %s.</p>",
|
||||||
mCertificate.getNotAfter().toString()));
|
mCertificate.getNotAfter().toString()));
|
||||||
|
|
||||||
int expirationYear = expirationCalendar.get(Calendar.YEAR);
|
int expirationYear = expirationCalendar.get(Calendar.YEAR);
|
||||||
@@ -232,11 +266,8 @@ final class KeyCheckPage extends ExportWizardPage {
|
|||||||
sb.append("<p>Android Market currently requires certificates to be valid until 2033.</p>");
|
sb.append("<p>Android Market currently requires certificates to be valid until 2033.</p>");
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.append("</form>");
|
mKeyDetails = sb.toString();
|
||||||
|
|
||||||
mDetailText.setText(sb.toString(), true /* parseTags */, true /* expandURLs */);
|
|
||||||
}
|
}
|
||||||
mDetailText.getParent().layout();
|
|
||||||
} else {
|
} else {
|
||||||
// fatal error = nothing can make the page complete.
|
// fatal error = nothing can make the page complete.
|
||||||
mFatalSigningError = true;
|
mFatalSigningError = true;
|
||||||
@@ -244,10 +275,15 @@ final class KeyCheckPage extends ExportWizardPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onDestinationChange();
|
onDestinationChange(true /*forceDetailUpdate*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDestinationChange() {
|
/**
|
||||||
|
* Callback for destination field edition
|
||||||
|
* @param forceDetailUpdate if true, the detail {@link FormText} is updated even if a fatal
|
||||||
|
* error has happened in the signing.
|
||||||
|
*/
|
||||||
|
private void onDestinationChange(boolean forceDetailUpdate) {
|
||||||
if (mFatalSigningError == false) {
|
if (mFatalSigningError == false) {
|
||||||
// reset messages for now.
|
// reset messages for now.
|
||||||
setErrorMessage(null);
|
setErrorMessage(null);
|
||||||
@@ -257,7 +293,8 @@ final class KeyCheckPage extends ExportWizardPage {
|
|||||||
|
|
||||||
if (path.length() == 0) {
|
if (path.length() == 0) {
|
||||||
setErrorMessage("Enter destination for the APK file.");
|
setErrorMessage("Enter destination for the APK file.");
|
||||||
mWizard.setDestination(null); // this is to reset canFinish in the wizard
|
// reset canFinish in the wizard.
|
||||||
|
mWizard.resetDestination();
|
||||||
setPageComplete(false);
|
setPageComplete(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -265,27 +302,140 @@ final class KeyCheckPage extends ExportWizardPage {
|
|||||||
File file = new File(path);
|
File file = new File(path);
|
||||||
if (file.isDirectory()) {
|
if (file.isDirectory()) {
|
||||||
setErrorMessage("Destination is a directory.");
|
setErrorMessage("Destination is a directory.");
|
||||||
mWizard.setDestination(null); // this is to reset canFinish in the wizard
|
// reset canFinish in the wizard.
|
||||||
|
mWizard.resetDestination();
|
||||||
setPageComplete(false);
|
setPageComplete(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
File parentFile = file.getParentFile();
|
File parentFolder = file.getParentFile();
|
||||||
if (parentFile == null || parentFile.isDirectory() == false) {
|
if (parentFolder == null || parentFolder.isDirectory() == false) {
|
||||||
setErrorMessage("Not a valid directory.");
|
setErrorMessage("Not a valid directory.");
|
||||||
mWizard.setDestination(null); // this is to reset canFinish in the wizard
|
// reset canFinish in the wizard.
|
||||||
|
mWizard.resetDestination();
|
||||||
setPageComplete(false);
|
setPageComplete(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// display the list of files that will actually be created
|
||||||
|
Map<String, String[]> apkFileMap = getApkFileMap(file);
|
||||||
|
|
||||||
|
// display them
|
||||||
|
boolean fileExists = false;
|
||||||
|
StringBuilder sb = new StringBuilder(String.format(
|
||||||
|
"<p>This will create the following files:</p>"));
|
||||||
|
|
||||||
|
Set<Entry<String, String[]>> set = apkFileMap.entrySet();
|
||||||
|
for (Entry<String, String[]> entry : set) {
|
||||||
|
String[] apkArray = entry.getValue();
|
||||||
|
String filename = apkArray[ExportWizard.APK_FILE_DEST];
|
||||||
|
File f = new File(parentFolder, filename);
|
||||||
|
if (f.isFile()) {
|
||||||
|
fileExists = true;
|
||||||
|
sb.append(String.format("<li>%1$s (WARNING: already exists)</li>", filename));
|
||||||
|
} else if (f.isDirectory()) {
|
||||||
|
setErrorMessage(String.format("%1$s is a directory.", filename));
|
||||||
|
// reset canFinish in the wizard.
|
||||||
|
mWizard.resetDestination();
|
||||||
|
setPageComplete(false);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
sb.append(String.format("<li>%1$s</li>", filename));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mDestinationDetails = sb.toString();
|
||||||
|
|
||||||
// no error, set the destination in the wizard.
|
// no error, set the destination in the wizard.
|
||||||
mWizard.setDestination(path);
|
mWizard.setDestination(parentFolder, apkFileMap);
|
||||||
setPageComplete(true);
|
setPageComplete(true);
|
||||||
|
|
||||||
// However, we should also test if the file already exists.
|
// However, we should also test if the file already exists.
|
||||||
if (file.isFile()) {
|
if (fileExists) {
|
||||||
setMessage("Destination file already exists.", WARNING);
|
setMessage("A destination file already exists.", WARNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateDetailText();
|
||||||
|
} else if (forceDetailUpdate) {
|
||||||
|
updateDetailText();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the scrollbar to match the content of the {@link FormText} or the new size
|
||||||
|
* of the {@link ScrolledComposite}.
|
||||||
|
*/
|
||||||
|
private void updateScrolling() {
|
||||||
|
if (mDetailText != null) {
|
||||||
|
Rectangle r = mScrolledComposite.getClientArea();
|
||||||
|
mScrolledComposite.setMinSize(mDetailText.computeSize(r.width, SWT.DEFAULT));
|
||||||
|
mScrolledComposite.layout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDetailText() {
|
||||||
|
StringBuilder sb = new StringBuilder("<form>");
|
||||||
|
if (mKeyDetails != null) {
|
||||||
|
sb.append(mKeyDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDestinationDetails != null && mFatalSigningError == false) {
|
||||||
|
sb.append(mDestinationDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append("</form>");
|
||||||
|
|
||||||
|
mDetailText.setText(sb.toString(), true /* parseTags */,
|
||||||
|
true /* expandURLs */);
|
||||||
|
|
||||||
|
mDetailText.getParent().layout();
|
||||||
|
|
||||||
|
updateScrolling();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the list of destination filenames based on the content of the destination field
|
||||||
|
* and the list of APK configurations for the project.
|
||||||
|
* @param file
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private Map<String, String[]> getApkFileMap(File file) {
|
||||||
|
String filename = file.getName();
|
||||||
|
|
||||||
|
HashMap<String, String[]> map = new HashMap<String, String[]>();
|
||||||
|
|
||||||
|
// add the default APK filename
|
||||||
|
String[] apkArray = new String[ExportWizard.APK_COUNT];
|
||||||
|
apkArray[ExportWizard.APK_FILE_SOURCE] = ProjectHelper.getApkFilename(
|
||||||
|
mWizard.getProject(), null /*config*/);
|
||||||
|
apkArray[ExportWizard.APK_FILE_DEST] = filename;
|
||||||
|
map.put(null, apkArray);
|
||||||
|
|
||||||
|
// add the APKs for each APK configuration.
|
||||||
|
if (mApkConfig != null && mApkConfig.size() > 0) {
|
||||||
|
// remove the extension.
|
||||||
|
int index = filename.lastIndexOf('.');
|
||||||
|
String base = filename.substring(0, index);
|
||||||
|
String extension = filename.substring(index);
|
||||||
|
|
||||||
|
Set<Entry<String, String>> set = mApkConfig.entrySet();
|
||||||
|
for (Entry<String, String> entry : set) {
|
||||||
|
apkArray = new String[ExportWizard.APK_COUNT];
|
||||||
|
apkArray[ExportWizard.APK_FILE_SOURCE] = ProjectHelper.getApkFilename(
|
||||||
|
mWizard.getProject(), entry.getKey());
|
||||||
|
apkArray[ExportWizard.APK_FILE_DEST] = base + "-" + entry.getKey() + extension;
|
||||||
|
map.put(entry.getKey(), apkArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onException(Throwable t) {
|
||||||
|
super.onException(t);
|
||||||
|
|
||||||
|
mKeyDetails = String.format("ERROR: %1$s", ExportWizard.getExceptionMessage(t));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ final class ProjectCheckPage extends ExportWizardPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update the wizard with the new project
|
// update the wizard with the new project
|
||||||
mWizard.setProject(null, null, null);
|
mWizard.setProject(null);
|
||||||
|
|
||||||
//test the project name first!
|
//test the project name first!
|
||||||
String text = mProjectText.getText().trim();
|
String text = mProjectText.getText().trim();
|
||||||
@@ -289,7 +289,7 @@ final class ProjectCheckPage extends ExportWizardPage {
|
|||||||
setErrorMessage(null);
|
setErrorMessage(null);
|
||||||
|
|
||||||
// update the wizard with the new project
|
// update the wizard with the new project
|
||||||
setApkFilePathInWizard(found);
|
mWizard.setProject(found);
|
||||||
|
|
||||||
// now rebuild the error ui.
|
// now rebuild the error ui.
|
||||||
buildErrorUi(found);
|
buildErrorUi(found);
|
||||||
@@ -299,24 +299,4 @@ final class ProjectCheckPage extends ExportWizardPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setApkFilePathInWizard(IProject project) {
|
|
||||||
if (project != null) {
|
|
||||||
IFolder outputIFolder = BaseProjectHelper.getOutputFolder(project);
|
|
||||||
if (outputIFolder != null) {
|
|
||||||
String outputOsPath = outputIFolder.getLocation().toOSString();
|
|
||||||
String apkFilePath = outputOsPath + File.separator + project.getName() +
|
|
||||||
AndroidConstants.DOT_ANDROID_PACKAGE;
|
|
||||||
|
|
||||||
File f = new File(apkFilePath);
|
|
||||||
if (f.isFile()) {
|
|
||||||
mWizard.setProject(project, apkFilePath, f.getName());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mWizard.setProject(null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -352,11 +352,17 @@ public class NewProjectWizard extends Wizard implements INewWizard {
|
|||||||
// Create the resource folders in the project if they don't already exist.
|
// Create the resource folders in the project if they don't already exist.
|
||||||
addDefaultDirectories(project, RES_DIRECTORY, RES_DIRECTORIES, monitor);
|
addDefaultDirectories(project, RES_DIRECTORY, RES_DIRECTORIES, monitor);
|
||||||
|
|
||||||
// Setup class path
|
// Setup class path: mark folders as source folders
|
||||||
IJavaProject javaProject = JavaCore.create(project);
|
IJavaProject javaProject = JavaCore.create(project);
|
||||||
for (String sourceFolder : sourceFolders) {
|
for (String sourceFolder : sourceFolders) {
|
||||||
setupSourceFolder(javaProject, sourceFolder, monitor);
|
setupSourceFolder(javaProject, sourceFolder, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mark the gen source folder as derived
|
||||||
|
IFolder genSrcFolder = project.getFolder(AndroidConstants.WS_ROOT + GEN_SRC_DIRECTORY);
|
||||||
|
if (genSrcFolder.exists()) {
|
||||||
|
genSrcFolder.setDerived(true);
|
||||||
|
}
|
||||||
|
|
||||||
if (((Boolean) parameters.get(PARAM_IS_NEW_PROJECT)).booleanValue()) {
|
if (((Boolean) parameters.get(PARAM_IS_NEW_PROJECT)).booleanValue()) {
|
||||||
// Create files in the project if they don't already exist
|
// Create files in the project if they don't already exist
|
||||||
|
|||||||
@@ -328,7 +328,7 @@ public abstract class AndroidContentAssist implements IContentAssistProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (currAttrNode != null) {
|
if (currAttrNode != null) {
|
||||||
choices = currAttrNode.getPossibleValues();
|
choices = currAttrNode.getPossibleValues(value);
|
||||||
|
|
||||||
if (currAttrNode instanceof UiFlagAttributeNode) {
|
if (currAttrNode instanceof UiFlagAttributeNode) {
|
||||||
// A "flag" can consist of several values separated by "or" (|).
|
// A "flag" can consist of several values separated by "or" (|).
|
||||||
|
|||||||
@@ -616,7 +616,7 @@ public class UiClassAttributeNode extends UiTextAttributeNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getPossibleValues() {
|
public String[] getPossibleValues(String prefix) {
|
||||||
// TODO: compute a list of existing classes for content assist completion
|
// TODO: compute a list of existing classes for content assist completion
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -311,7 +311,7 @@ public class UiPackageAttributeNode extends UiTextAttributeNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getPossibleValues() {
|
public String[] getPossibleValues(String prefix) {
|
||||||
// TODO: compute a list of existing packages for content assist completion
|
// TODO: compute a list of existing packages for content assist completion
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public class ListValueCellEditor extends ComboBoxCellEditor {
|
|||||||
UiListAttributeNode uiListAttribute = (UiListAttributeNode)value;
|
UiListAttributeNode uiListAttribute = (UiListAttributeNode)value;
|
||||||
|
|
||||||
// set the possible values in the combo
|
// set the possible values in the combo
|
||||||
String[] items = uiListAttribute.getPossibleValues();
|
String[] items = uiListAttribute.getPossibleValues(null);
|
||||||
mItems = new String[items.length];
|
mItems = new String[items.length];
|
||||||
System.arraycopy(items, 0, mItems, 0, items.length);
|
System.arraycopy(items, 0, mItems, 0, items.length);
|
||||||
setItems(mItems);
|
setItems(mItems);
|
||||||
|
|||||||
@@ -117,9 +117,13 @@ public abstract class UiAttributeNode {
|
|||||||
* <p/>
|
* <p/>
|
||||||
* Implementations that do not have any known values should return null.
|
* Implementations that do not have any known values should return null.
|
||||||
*
|
*
|
||||||
* @return A list of possible completion values or null.
|
* @param prefix An optional prefix string, which is whatever the user has already started
|
||||||
|
* typing. Can be null or an empty string. The implementation can use this to filter choices
|
||||||
|
* and only return strings that match this prefix. A lazy or default implementation can
|
||||||
|
* simply ignore this and return everything.
|
||||||
|
* @return A list of possible completion values, and empty array or null.
|
||||||
*/
|
*/
|
||||||
public abstract String[] getPossibleValues();
|
public abstract String[] getPossibleValues(String prefix);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the XML is being loaded or has changed to
|
* Called when the XML is being loaded or has changed to
|
||||||
|
|||||||
@@ -124,9 +124,11 @@ public class UiFlagAttributeNode extends UiTextAttributeNode {
|
|||||||
/**
|
/**
|
||||||
* Get the flag names, either from the initial names set in the attribute
|
* Get the flag names, either from the initial names set in the attribute
|
||||||
* or by querying the framework resource parser.
|
* or by querying the framework resource parser.
|
||||||
|
*
|
||||||
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String[] getPossibleValues() {
|
public String[] getPossibleValues(String prefix) {
|
||||||
String attr_name = getDescriptor().getXmlLocalName();
|
String attr_name = getDescriptor().getXmlLocalName();
|
||||||
String element_name = getUiParent().getDescriptor().getXmlName();
|
String element_name = getUiParent().getDescriptor().getXmlName();
|
||||||
|
|
||||||
@@ -242,7 +244,7 @@ public class UiFlagAttributeNode extends UiTextAttributeNode {
|
|||||||
final TableColumn column = new TableColumn(mTable, SWT.NONE);
|
final TableColumn column = new TableColumn(mTable, SWT.NONE);
|
||||||
|
|
||||||
// List all the expected flag names and check those which are currently used
|
// List all the expected flag names and check those which are currently used
|
||||||
String[] names = getPossibleValues();
|
String[] names = getPossibleValues(null);
|
||||||
if (names != null) {
|
if (names != null) {
|
||||||
for (String name : names) {
|
for (String name : names) {
|
||||||
TableItem item = new TableItem(mTable, SWT.NONE);
|
TableItem item = new TableItem(mTable, SWT.NONE);
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ public class UiListAttributeNode extends UiAbstractTextAttributeNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void fillCombo() {
|
protected void fillCombo() {
|
||||||
String[] values = getPossibleValues();
|
String[] values = getPossibleValues(null);
|
||||||
|
|
||||||
if (values == null) {
|
if (values == null) {
|
||||||
AdtPlugin.log(IStatus.ERROR,
|
AdtPlugin.log(IStatus.ERROR,
|
||||||
@@ -124,9 +124,11 @@ public class UiListAttributeNode extends UiAbstractTextAttributeNode {
|
|||||||
/**
|
/**
|
||||||
* Get the list values, either from the initial values set in the attribute
|
* Get the list values, either from the initial values set in the attribute
|
||||||
* or by querying the framework resource parser.
|
* or by querying the framework resource parser.
|
||||||
|
*
|
||||||
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String[] getPossibleValues() {
|
public String[] getPossibleValues(String prefix) {
|
||||||
AttributeDescriptor descriptor = getDescriptor();
|
AttributeDescriptor descriptor = getDescriptor();
|
||||||
UiElementNode uiParent = getUiParent();
|
UiElementNode uiParent = getUiParent();
|
||||||
|
|
||||||
@@ -134,13 +136,13 @@ public class UiListAttributeNode extends UiAbstractTextAttributeNode {
|
|||||||
String element_name = uiParent.getDescriptor().getXmlName();
|
String element_name = uiParent.getDescriptor().getXmlName();
|
||||||
|
|
||||||
// FrameworkResourceManager expects a specific prefix for the attribute.
|
// FrameworkResourceManager expects a specific prefix for the attribute.
|
||||||
String prefix = "";
|
String nsPrefix = "";
|
||||||
if (SdkConstants.NS_RESOURCES.equals(descriptor.getNamespaceUri())) {
|
if (SdkConstants.NS_RESOURCES.equals(descriptor.getNamespaceUri())) {
|
||||||
prefix = "android:"; //$NON-NLS-1$
|
nsPrefix = "android:"; //$NON-NLS-1$
|
||||||
} else if (XmlnsAttributeDescriptor.XMLNS_URI.equals(descriptor.getNamespaceUri())) {
|
} else if (XmlnsAttributeDescriptor.XMLNS_URI.equals(descriptor.getNamespaceUri())) {
|
||||||
prefix = "xmlns:"; //$NON-NLS-1$
|
nsPrefix = "xmlns:"; //$NON-NLS-1$
|
||||||
}
|
}
|
||||||
attr_name = prefix + attr_name;
|
attr_name = nsPrefix + attr_name;
|
||||||
|
|
||||||
String[] values = null;
|
String[] values = null;
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.android.ide.eclipse.editors.uimodel;
|
|||||||
|
|
||||||
import com.android.ide.eclipse.adt.sdk.AndroidTargetData;
|
import com.android.ide.eclipse.adt.sdk.AndroidTargetData;
|
||||||
import com.android.ide.eclipse.common.resources.IResourceRepository;
|
import com.android.ide.eclipse.common.resources.IResourceRepository;
|
||||||
|
import com.android.ide.eclipse.common.resources.ResourceItem;
|
||||||
import com.android.ide.eclipse.common.resources.ResourceType;
|
import com.android.ide.eclipse.common.resources.ResourceType;
|
||||||
import com.android.ide.eclipse.editors.AndroidEditor;
|
import com.android.ide.eclipse.editors.AndroidEditor;
|
||||||
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
|
import com.android.ide.eclipse.editors.descriptors.AttributeDescriptor;
|
||||||
@@ -44,6 +45,10 @@ import org.eclipse.ui.forms.IManagedForm;
|
|||||||
import org.eclipse.ui.forms.widgets.FormToolkit;
|
import org.eclipse.ui.forms.widgets.FormToolkit;
|
||||||
import org.eclipse.ui.forms.widgets.TableWrapData;
|
import org.eclipse.ui.forms.widgets.TableWrapData;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an XML attribute for a resource that can be modified using a simple text field or
|
* Represents an XML attribute for a resource that can be modified using a simple text field or
|
||||||
* a dialog to choose an existing resource.
|
* a dialog to choose an existing resource.
|
||||||
@@ -154,8 +159,81 @@ public class UiResourceAttributeNode extends UiTextAttributeNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getPossibleValues() {
|
public String[] getPossibleValues(String prefix) {
|
||||||
// TODO: compute a list of existing resources for content assist completion
|
IResourceRepository repository = null;
|
||||||
return null;
|
boolean isSystem = false;
|
||||||
|
|
||||||
|
UiElementNode uiNode = getUiParent();
|
||||||
|
AndroidEditor editor = uiNode.getEditor();
|
||||||
|
|
||||||
|
if (prefix == null || prefix.indexOf("android:") < 0) {
|
||||||
|
IProject project = editor.getProject();
|
||||||
|
if (project != null) {
|
||||||
|
// get the resource repository for this project and the system resources.
|
||||||
|
repository = ResourceManager.getInstance().getProjectResources(project);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If there's a prefix with "android:" in it, use the system resources
|
||||||
|
AndroidTargetData data = editor.getTargetData();
|
||||||
|
repository = data.getSystemResources();
|
||||||
|
isSystem = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get list of potential resource types, either specific to this project
|
||||||
|
// or the generic list.
|
||||||
|
ResourceType[] resTypes = (repository != null) ?
|
||||||
|
repository.getAvailableResourceTypes() :
|
||||||
|
ResourceType.values();
|
||||||
|
|
||||||
|
// Get the type name from the prefix, if any. It's any word before the / if there's one
|
||||||
|
String typeName = null;
|
||||||
|
if (prefix != null) {
|
||||||
|
Matcher m = Pattern.compile(".*?([a-z]+)/.*").matcher(prefix);
|
||||||
|
if (m.matches()) {
|
||||||
|
typeName = m.group(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now collect results
|
||||||
|
ArrayList<String> results = new ArrayList<String>();
|
||||||
|
|
||||||
|
if (typeName == null) {
|
||||||
|
// This prefix does not have a / in it, so the resource string is either empty
|
||||||
|
// or does not have the resource type in it. Simply offer the list of potential
|
||||||
|
// resource types.
|
||||||
|
|
||||||
|
for (ResourceType resType : resTypes) {
|
||||||
|
results.add("@" + resType.getName() + "/");
|
||||||
|
if (resType == ResourceType.ID) {
|
||||||
|
// Also offer the + version to create an id from scratch
|
||||||
|
results.add("@+" + resType.getName() + "/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (repository != null) {
|
||||||
|
// We have a style name and a repository. Find all resources that match this
|
||||||
|
// type and recreate suggestions out of them.
|
||||||
|
|
||||||
|
ResourceType resType = ResourceType.getEnum(typeName);
|
||||||
|
if (resType != null) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append('@');
|
||||||
|
if (prefix.indexOf('+') >= 0) {
|
||||||
|
sb.append('+');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSystem) {
|
||||||
|
sb.append("android:");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append(typeName).append('/');
|
||||||
|
String base = sb.toString();
|
||||||
|
|
||||||
|
for (ResourceItem item : repository.getResources(resType)) {
|
||||||
|
results.add(base + item.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results.toArray(new String[results.size()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,9 +96,13 @@ public class UiSeparatorAttributeNode extends UiAttributeNode {
|
|||||||
sep.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
|
sep.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** No completion values for this UI attribute. */
|
/**
|
||||||
|
* No completion values for this UI attribute.
|
||||||
|
*
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String[] getPossibleValues() {
|
public String[] getPossibleValues(String prefix) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,9 +70,13 @@ public class UiTextAttributeNode extends UiAbstractTextAttributeNode {
|
|||||||
setTextWidget(text);
|
setTextWidget(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** No completion values for this UI attribute. */
|
/**
|
||||||
|
* No completion values for this UI attribute.
|
||||||
|
*
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String[] getPossibleValues() {
|
public String[] getPossibleValues(String prefix) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import java.awt.Color;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Insets;
|
import java.awt.Insets;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
class LayoutRenderer extends JComponent {
|
class LayoutRenderer extends JComponent {
|
||||||
@@ -34,14 +36,23 @@ class LayoutRenderer extends JComponent {
|
|||||||
|
|
||||||
private boolean showExtras;
|
private boolean showExtras;
|
||||||
private ViewHierarchyScene scene;
|
private ViewHierarchyScene scene;
|
||||||
|
private JComponent sceneView;
|
||||||
|
|
||||||
LayoutRenderer(ViewHierarchyScene scene) {
|
LayoutRenderer(ViewHierarchyScene scene, JComponent sceneView) {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
|
this.sceneView = sceneView;
|
||||||
|
|
||||||
setOpaque(true);
|
setOpaque(true);
|
||||||
setBorder(BorderFactory.createEmptyBorder(0, 0, 12, 0));
|
setBorder(BorderFactory.createEmptyBorder(0, 0, 12, 0));
|
||||||
setBackground(Color.BLACK);
|
setBackground(Color.BLACK);
|
||||||
setForeground(Color.WHITE);
|
setForeground(Color.WHITE);
|
||||||
|
|
||||||
|
addMouseListener(new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mousePressed(MouseEvent event) {
|
||||||
|
selectChild(event.getX(), event.getY());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -118,4 +129,49 @@ class LayoutRenderer extends JComponent {
|
|||||||
this.showExtras = showExtras;
|
this.showExtras = showExtras;
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void selectChild(int x, int y) {
|
||||||
|
|
||||||
|
if (scene == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewNode root = scene.getRoot();
|
||||||
|
if (root == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Insets insets = getInsets();
|
||||||
|
|
||||||
|
int xoffset = (getWidth() - insets.left - insets.right - root.width) / 2 + insets.left + 1;
|
||||||
|
int yoffset = (getHeight() - insets.top - insets.bottom - root.height) / 2 + insets.top + 1;
|
||||||
|
|
||||||
|
x -= xoffset;
|
||||||
|
y -= yoffset;
|
||||||
|
if (x >= 0 && x < EMULATED_SCREEN_WIDTH && y >= 0 && y < EMULATED_SCREEN_HEIGHT) {
|
||||||
|
ViewNode hit = findChild(root, root, x, y);
|
||||||
|
scene.setFocusedObject(hit);
|
||||||
|
sceneView.repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ViewNode findChild(ViewNode root, ViewNode besthit, int x, int y) {
|
||||||
|
ViewNode hit = besthit;
|
||||||
|
for (ViewNode node : root.children) {
|
||||||
|
|
||||||
|
if (node.left <= x && x < node.left + node.width &&
|
||||||
|
node.top <= y && y < node.top + node.height) {
|
||||||
|
if (node.width <= hit.width && node.height <= hit.height) {
|
||||||
|
hit = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.children.size() > 0) {
|
||||||
|
hit = findChild(node, hit,
|
||||||
|
x - (node.left - node.parent.scrollX),
|
||||||
|
y - (node.top - node.parent.scrollY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ import java.awt.event.ActionEvent;
|
|||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.event.MouseMotionAdapter;
|
import java.awt.event.MouseMotionAdapter;
|
||||||
|
import java.awt.event.MouseWheelEvent;
|
||||||
|
import java.awt.event.MouseWheelListener;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
class ScreenViewer extends JPanel implements ActionListener {
|
class ScreenViewer extends JPanel implements ActionListener {
|
||||||
@@ -69,6 +71,8 @@ class ScreenViewer extends JPanel implements ActionListener {
|
|||||||
private Timer timer;
|
private Timer timer;
|
||||||
private ViewNode node;
|
private ViewNode node;
|
||||||
|
|
||||||
|
private JSlider zoomSlider;
|
||||||
|
|
||||||
ScreenViewer(Workspace workspace, Device device, int spacing) {
|
ScreenViewer(Workspace workspace, Device device, int spacing) {
|
||||||
setLayout(new BorderLayout());
|
setLayout(new BorderLayout());
|
||||||
setOpaque(false);
|
setOpaque(false);
|
||||||
@@ -95,6 +99,7 @@ class ScreenViewer extends JPanel implements ActionListener {
|
|||||||
|
|
||||||
private JPanel buildLoupePanel(int spacing) {
|
private JPanel buildLoupePanel(int spacing) {
|
||||||
loupe = new LoupeViewer();
|
loupe = new LoupeViewer();
|
||||||
|
loupe.addMouseWheelListener(new WheelZoomListener());
|
||||||
CrosshairPanel crosshairPanel = new CrosshairPanel(loupe);
|
CrosshairPanel crosshairPanel = new CrosshairPanel(loupe);
|
||||||
|
|
||||||
JPanel loupePanel = new JPanel(new BorderLayout());
|
JPanel loupePanel = new JPanel(new BorderLayout());
|
||||||
@@ -106,9 +111,20 @@ class ScreenViewer extends JPanel implements ActionListener {
|
|||||||
return loupePanel;
|
return loupePanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class WheelZoomListener implements MouseWheelListener {
|
||||||
|
public void mouseWheelMoved(MouseWheelEvent e) {
|
||||||
|
if (zoomSlider != null) {
|
||||||
|
int val = zoomSlider.getValue();
|
||||||
|
val -= e.getWheelRotation() * 2;
|
||||||
|
zoomSlider.setValue(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private JPanel buildViewerAndControls() {
|
private JPanel buildViewerAndControls() {
|
||||||
JPanel panel = new JPanel(new GridBagLayout());
|
JPanel panel = new JPanel(new GridBagLayout());
|
||||||
crosshair = new Crosshair(new ScreenshotViewer());
|
crosshair = new Crosshair(new ScreenshotViewer());
|
||||||
|
crosshair.addMouseWheelListener(new WheelZoomListener());
|
||||||
panel.add(crosshair,
|
panel.add(crosshair,
|
||||||
new GridBagConstraints(0, y++, 2, 1, 1.0f, 0.0f,
|
new GridBagConstraints(0, y++, 2, 1, 1.0f, 0.0f,
|
||||||
GridBagConstraints.FIRST_LINE_START, GridBagConstraints.NONE,
|
GridBagConstraints.FIRST_LINE_START, GridBagConstraints.NONE,
|
||||||
@@ -131,7 +147,8 @@ class ScreenViewer extends JPanel implements ActionListener {
|
|||||||
timer.restart();
|
timer.restart();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
buildSlider(panel, "Zoom:", "2x", "24x", 2, 24, 8, 2).addChangeListener(
|
zoomSlider = buildSlider(panel, "Zoom:", "2x", "24x", 2, 24, 8, 2);
|
||||||
|
zoomSlider.addChangeListener(
|
||||||
new ChangeListener() {
|
new ChangeListener() {
|
||||||
public void stateChanged(ChangeEvent event) {
|
public void stateChanged(ChangeEvent event) {
|
||||||
zoom = ((JSlider) event.getSource()).getValue();
|
zoom = ((JSlider) event.getSource()).getValue();
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ import javax.swing.JMenuItem;
|
|||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JProgressBar;
|
import javax.swing.JProgressBar;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JScrollBar;
|
||||||
import javax.swing.JSlider;
|
import javax.swing.JSlider;
|
||||||
import javax.swing.JSplitPane;
|
import javax.swing.JSplitPane;
|
||||||
import javax.swing.JTable;
|
import javax.swing.JTable;
|
||||||
@@ -105,6 +106,8 @@ import java.awt.event.ActionEvent;
|
|||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.event.MouseWheelEvent;
|
||||||
|
import java.awt.event.MouseWheelListener;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -173,6 +176,7 @@ public class Workspace extends JFrame {
|
|||||||
add(buildMainPanel());
|
add(buildMainPanel());
|
||||||
setJMenuBar(buildMenuBar());
|
setJMenuBar(buildMenuBar());
|
||||||
|
|
||||||
|
devices.changeSelection(0, 0, false, false);
|
||||||
currentDeviceChanged();
|
currentDeviceChanged();
|
||||||
|
|
||||||
pack();
|
pack();
|
||||||
@@ -648,6 +652,7 @@ public class Workspace extends JFrame {
|
|||||||
|
|
||||||
sceneView = scene.createView();
|
sceneView = scene.createView();
|
||||||
sceneView.addMouseListener(new NodeClickListener());
|
sceneView.addMouseListener(new NodeClickListener());
|
||||||
|
sceneView.addMouseWheelListener(new WheelZoomListener());
|
||||||
sceneScroller.setViewportView(sceneView);
|
sceneScroller.setViewportView(sceneView);
|
||||||
|
|
||||||
if (extrasPanel != null) {
|
if (extrasPanel != null) {
|
||||||
@@ -678,7 +683,10 @@ public class Workspace extends JFrame {
|
|||||||
|
|
||||||
private JPanel buildExtrasPanel() {
|
private JPanel buildExtrasPanel() {
|
||||||
extrasPanel = new JPanel(new BorderLayout());
|
extrasPanel = new JPanel(new BorderLayout());
|
||||||
extrasPanel.add(new JScrollPane(layoutView = new LayoutRenderer(scene)));
|
JScrollPane p = new JScrollPane(layoutView = new LayoutRenderer(scene, sceneView));
|
||||||
|
JScrollBar b = p.getVerticalScrollBar();
|
||||||
|
b.setUnitIncrement(10);
|
||||||
|
extrasPanel.add(p);
|
||||||
extrasPanel.add(scene.createSatelliteView(), BorderLayout.SOUTH);
|
extrasPanel.add(scene.createSatelliteView(), BorderLayout.SOUTH);
|
||||||
extrasPanel.add(buildLayoutViewControlButtons(), BorderLayout.NORTH);
|
extrasPanel.add(buildLayoutViewControlButtons(), BorderLayout.NORTH);
|
||||||
return extrasPanel;
|
return extrasPanel;
|
||||||
@@ -1231,6 +1239,15 @@ public class Workspace extends JFrame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class WheelZoomListener implements MouseWheelListener {
|
||||||
|
public void mouseWheelMoved(MouseWheelEvent e) {
|
||||||
|
if (zoomSlider != null) {
|
||||||
|
int val = zoomSlider.getValue();
|
||||||
|
val -= e.getWheelRotation() * 10;
|
||||||
|
zoomSlider.setValue(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
private class DevicesTableModel extends DefaultTableModel implements
|
private class DevicesTableModel extends DefaultTableModel implements
|
||||||
AndroidDebugBridge.IDeviceChangeListener {
|
AndroidDebugBridge.IDeviceChangeListener {
|
||||||
|
|
||||||
|
|||||||
@@ -48,11 +48,49 @@ public final class AvdManager {
|
|||||||
private final static String AVD_INFO_PATH = "path";
|
private final static String AVD_INFO_PATH = "path";
|
||||||
private final static String AVD_INFO_TARGET = "target";
|
private final static String AVD_INFO_TARGET = "target";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AVD/config.ini key name representing the SDK-relative path of the skin folder, if any,
|
||||||
|
* or a 320x480 like constant for a numeric skin size.
|
||||||
|
*
|
||||||
|
* @see #NUMERIC_SKIN_SIZE
|
||||||
|
*/
|
||||||
public final static String AVD_INI_SKIN_PATH = "skin.path";
|
public final static String AVD_INI_SKIN_PATH = "skin.path";
|
||||||
|
/**
|
||||||
|
* AVD/config.ini key name representing an UI name for the skin.
|
||||||
|
* This config key is ignored by the emulator. It is only used by the SDK manager or
|
||||||
|
* tools to give a friendlier name to the skin.
|
||||||
|
* If missing, use the {@link #AVD_INI_SKIN_PATH} key instead.
|
||||||
|
*/
|
||||||
public final static String AVD_INI_SKIN_NAME = "skin.name";
|
public final static String AVD_INI_SKIN_NAME = "skin.name";
|
||||||
|
/**
|
||||||
|
* AVD/config.ini key name representing the path to the sdcard file.
|
||||||
|
* If missing, the default name "sdcard.img" will be used for the sdcard, if there's such
|
||||||
|
* a file.
|
||||||
|
*
|
||||||
|
* @see #SDCARD_IMG
|
||||||
|
*/
|
||||||
public final static String AVD_INI_SDCARD_PATH = "sdcard.path";
|
public final static String AVD_INI_SDCARD_PATH = "sdcard.path";
|
||||||
|
/**
|
||||||
|
* AVD/config.ini key name representing the size of the SD card.
|
||||||
|
* This property is for UI purposes only. It is not used by the emulator.
|
||||||
|
*
|
||||||
|
* @see #SDCARD_SIZE_PATTERN
|
||||||
|
*/
|
||||||
public final static String AVD_INI_SDCARD_SIZE = "sdcard.size";
|
public final static String AVD_INI_SDCARD_SIZE = "sdcard.size";
|
||||||
|
/**
|
||||||
|
* AVD/config.ini key name representing the first path where the emulator looks
|
||||||
|
* for system images. Typically this is the path to the add-on system image or
|
||||||
|
* the path to the platform system image if there's no add-on.
|
||||||
|
* <p/>
|
||||||
|
* The emulator looks at {@link #AVD_INI_IMAGES_1} before {@link #AVD_INI_IMAGES_2}.
|
||||||
|
*/
|
||||||
public final static String AVD_INI_IMAGES_1 = "image.sysdir.1";
|
public final static String AVD_INI_IMAGES_1 = "image.sysdir.1";
|
||||||
|
/**
|
||||||
|
* AVD/config.ini key name representing the second path where the emulator looks
|
||||||
|
* for system images. Typically this is the path to the platform system image.
|
||||||
|
*
|
||||||
|
* @see #AVD_INI_IMAGES_1
|
||||||
|
*/
|
||||||
public final static String AVD_INI_IMAGES_2 = "image.sysdir.2";
|
public final static String AVD_INI_IMAGES_2 = "image.sysdir.2";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,6 +107,9 @@ public final class AvdManager {
|
|||||||
private final static Pattern INI_NAME_PATTERN = Pattern.compile("(.+)\\" + INI_EXTENSION + "$",
|
private final static Pattern INI_NAME_PATTERN = Pattern.compile("(.+)\\" + INI_EXTENSION + "$",
|
||||||
Pattern.CASE_INSENSITIVE);
|
Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pattern for matching SD Card sizes, e.g. "4K" or "16M".
|
||||||
|
*/
|
||||||
private final static Pattern SDCARD_SIZE_PATTERN = Pattern.compile("\\d+[MK]?");
|
private final static Pattern SDCARD_SIZE_PATTERN = Pattern.compile("\\d+[MK]?");
|
||||||
|
|
||||||
/** An immutable structure describing an Android Virtual Device. */
|
/** An immutable structure describing an Android Virtual Device. */
|
||||||
|
|||||||
Reference in New Issue
Block a user