diff options
11 files changed, 245 insertions, 632 deletions
diff --git a/anttasks/src/com/android/ant/AaptExecLoopTask.java b/anttasks/src/com/android/ant/AaptExecLoopTask.java index ef74fe7..47b8f48 100644 --- a/anttasks/src/com/android/ant/AaptExecLoopTask.java +++ b/anttasks/src/com/android/ant/AaptExecLoopTask.java @@ -17,6 +17,7 @@ package com.android.ant; import com.android.sdklib.internal.project.ApkConfigurationHelper; +import com.android.sdklib.internal.project.ApkSettings; import com.android.sdklib.internal.project.ProjectProperties; import com.android.sdklib.internal.project.ProjectProperties.PropertyType; @@ -28,7 +29,6 @@ import org.apache.tools.ant.types.Path; import java.io.File; import java.util.Map; -import java.util.Set; import java.util.Map.Entry; /** @@ -38,7 +38,7 @@ import java.util.Map.Entry; * */ public final class AaptExecLoopTask extends Task { - + private String mExecutable; private String mCommand; private String mManifest; @@ -55,7 +55,7 @@ public final class AaptExecLoopTask extends Task { public void setExecutable(String executable) { mExecutable = executable; } - + /** * Sets the value of the "command" attribute. * @param command the value. @@ -63,7 +63,7 @@ public final class AaptExecLoopTask extends Task { public void setCommand(String command) { mCommand = command; } - + /** * Sets the value of the "manifest" attribute. * @param manifest the value. @@ -71,7 +71,7 @@ public final class AaptExecLoopTask extends Task { public void setManifest(Path manifest) { mManifest = manifest.toString(); } - + /** * Sets the value of the "resources" attribute. * @param resources the value. @@ -79,7 +79,7 @@ public final class AaptExecLoopTask extends Task { public void setResources(Path resources) { mResources = resources.toString(); } - + /** * Sets the value of the "assets" attribute. * @param assets the value. @@ -87,7 +87,7 @@ public final class AaptExecLoopTask extends Task { public void setAssets(Path assets) { mAssets = assets.toString(); } - + /** * Sets the value of the "androidjar" attribute. * @param androidJar the value. @@ -95,7 +95,7 @@ public final class AaptExecLoopTask extends Task { public void setAndroidjar(Path androidJar) { mAndroidJar = androidJar.toString(); } - + /** * Sets the value of the "outfolder" attribute. * @param outFolder the value. @@ -103,7 +103,7 @@ public final class AaptExecLoopTask extends Task { public void setOutfolder(Path outFolder) { mOutFolder = outFolder.toString(); } - + /** * Sets the value of the "basename" attribute. * @param baseName the value. @@ -111,19 +111,19 @@ public final class AaptExecLoopTask extends Task { public void setBasename(String baseName) { mBaseName = baseName; } - + /* * (non-Javadoc) - * + * * Executes the loop. Based on the values inside default.properties, this will * create alternate temporary ap_ files. - * + * * @see org.apache.tools.ant.Task#execute() */ @Override public void execute() throws BuildException { Project taskProject = getProject(); - + // first do a full resource package createPackage(null /*configName*/, null /*resourceFilter*/); @@ -132,12 +132,15 @@ public final class AaptExecLoopTask extends Task { File baseDir = taskProject.getBaseDir(); ProjectProperties properties = ProjectProperties.load(baseDir.getAbsolutePath(), PropertyType.DEFAULT); - - Map<String, String> apkConfigs = ApkConfigurationHelper.getConfigs(properties); - if (apkConfigs.size() > 0) { - Set<Entry<String, String>> entrySet = apkConfigs.entrySet(); - for (Entry<String, String> entry : entrySet) { - createPackage(entry.getKey(), entry.getValue()); + + + ApkSettings apkSettings = ApkConfigurationHelper.getSettings(properties); + if (apkSettings != null) { + Map<String, String> apkFilters = apkSettings.getResourceFilters(); + if (apkFilters.size() > 0) { + for (Entry<String, String> entry : apkFilters.entrySet()) { + createPackage(entry.getKey(), entry.getValue()); + } } } } @@ -164,19 +167,19 @@ public final class AaptExecLoopTask extends Task { ExecTask task = new ExecTask(); task.setExecutable(mExecutable); task.setFailonerror(true); - + // aapt command. Only "package" is supported at this time really. task.createArg().setValue(mCommand); - + // filters if needed if (configName != null && resourceFilter != null) { task.createArg().setValue("-c"); task.createArg().setValue(resourceFilter); } - + // force flag task.createArg().setValue("-f"); - + // manifest location task.createArg().setValue("-M"); task.createArg().setValue(mManifest); @@ -187,18 +190,18 @@ public final class AaptExecLoopTask extends Task { task.createArg().setValue("-S"); task.createArg().setValue(mResources); } - + // assets location. This may not exists, and aapt doesn't like it, so we check first. File assets = new File(mAssets); if (assets.isDirectory()) { task.createArg().setValue("-A"); task.createArg().setValue(mAssets); } - + // android.jar task.createArg().setValue("-I"); task.createArg().setValue(mAndroidJar); - + // out file. This is based on the outFolder, baseName, and the configName (if applicable) String filename; if (configName != null && resourceFilter != null) { @@ -206,15 +209,15 @@ public final class AaptExecLoopTask extends Task { } else { filename = mBaseName + ".ap_"; } - + File file = new File(mOutFolder, filename); task.createArg().setValue("-F"); task.createArg().setValue(file.getAbsolutePath()); - + // final setup of the task task.setProject(taskProject); task.setOwningTarget(getOwningTarget()); - + // execute it. task.execute(); } diff --git a/anttasks/src/com/android/ant/ApkBuilderTask.java b/anttasks/src/com/android/ant/ApkBuilderTask.java index 3a15368..b2c445d 100644 --- a/anttasks/src/com/android/ant/ApkBuilderTask.java +++ b/anttasks/src/com/android/ant/ApkBuilderTask.java @@ -20,6 +20,7 @@ import com.android.apkbuilder.ApkBuilder.ApkCreationException; import com.android.apkbuilder.internal.ApkBuilderImpl; import com.android.apkbuilder.internal.ApkBuilderImpl.ApkFile; import com.android.sdklib.internal.project.ApkConfigurationHelper; +import com.android.sdklib.internal.project.ApkSettings; import com.android.sdklib.internal.project.ProjectProperties; import com.android.sdklib.internal.project.ProjectProperties.PropertyType; @@ -35,7 +36,6 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.Map; -import java.util.Set; import java.util.Map.Entry; public class ApkBuilderTask extends Task { @@ -214,11 +214,13 @@ public class ApkBuilderTask extends Task { ProjectProperties properties = ProjectProperties.load(baseDir.getAbsolutePath(), PropertyType.DEFAULT); - Map<String, String> apkConfigs = ApkConfigurationHelper.getConfigs(properties); - if (apkConfigs.size() > 0) { - Set<Entry<String, String>> entrySet = apkConfigs.entrySet(); - for (Entry<String, String> entry : entrySet) { - createApk(apkBuilder, entry.getKey(), entry.getValue(), path); + ApkSettings apkSettings = ApkConfigurationHelper.getSettings(properties); + if (apkSettings != null) { + Map<String, String> apkFilters = apkSettings.getResourceFilters(); + if (apkFilters.size() > 0) { + for (Entry<String, String> entry : apkFilters.entrySet()) { + createApk(apkBuilder, entry.getKey(), entry.getValue(), path); + } } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilder.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilder.java index b49ee6e..1ed5123 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilder.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilder.java @@ -34,6 +34,7 @@ import com.android.jarutils.SignedJarBuilder.IZipEntryFilter; import com.android.prefs.AndroidLocation.AndroidLocationException; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.SdkConstants; +import com.android.sdklib.internal.project.ApkSettings; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; @@ -303,11 +304,15 @@ public class ApkBuilder extends BaseBuilder { return referencedProjects; } - // get the extra configs for the project. - // The map contains (name, filter) where 'name' is a name to be used in the apk filename, - // and filter is the resource filter to be used in the aapt -c parameters to restrict - // which resource configurations to package in the apk. - Map<String, String> configs = Sdk.getCurrent().getProjectApkConfigs(project); + // get the APK configs for the project. + ApkSettings apkSettings = Sdk.getCurrent().getApkSettings(project); + Set<Entry<String, String>> apkfilters = null; + if (apkSettings != null) { + Map<String, String> filterMap = apkSettings.getResourceFilters(); + if (filterMap != null && filterMap.size() > 0) { + apkfilters = filterMap.entrySet(); + } + } // do some extra check, in case the output files are not present. This // will force to recreate them. @@ -320,19 +325,20 @@ public class ApkBuilder extends BaseBuilder { mPackageResources = true; mBuildFinalPackage = true; } else { - // if the full package is present, we check the filtered resource packages as well - if (configs != null) { - Set<Entry<String, String>> entrySet = configs.entrySet(); - - for (Entry<String, String> entry : entrySet) { + // if the full package is present, we check the filtered resource packages + // as well + if (apkfilters != null) { + for (Entry<String, String> entry : apkfilters) { String filename = String.format(AndroidConstants.FN_RESOURCES_S_AP_, entry.getKey()); tmp = outputFolder.findMember(filename); if (tmp == null || (tmp instanceof IFile && tmp.exists() == false)) { - String msg = String.format(Messages.s_Missing_Repackaging, filename); - AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg); + String msg = String.format(Messages.s_Missing_Repackaging, + filename); + AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, + project, msg); mPackageResources = true; mBuildFinalPackage = true; break; @@ -360,11 +366,9 @@ public class ApkBuilder extends BaseBuilder { String msg = String.format(Messages.s_Missing_Repackaging, finalPackageName); AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg); mBuildFinalPackage = true; - } else if (configs != null) { + } else if (apkfilters != null) { // if the full apk is present, we check the filtered apk as well - Set<Entry<String, String>> entrySet = configs.entrySet(); - - for (Entry<String, String> entry : entrySet) { + for (Entry<String, String> entry : apkfilters) { String filename = ProjectHelper.getApkFilename(project, entry.getKey()); tmp = outputFolder.findMember(filename); @@ -411,9 +415,8 @@ public class ApkBuilder extends BaseBuilder { // notified. finalPackage.delete(); - if (configs != null) { - Set<Entry<String, String>> entrySet = configs.entrySet(); - for (Entry<String, String> entry : entrySet) { + if (apkfilters != null) { + for (Entry<String, String> entry : apkfilters) { String packageFilepath = osBinPath + File.separator + ProjectHelper.getApkFilename(project, entry.getKey()); @@ -477,9 +480,8 @@ public class ApkBuilder extends BaseBuilder { } // now do the same thing for all the configured resource packages. - if (configs != null) { - Set<Entry<String, String>> entrySet = configs.entrySet(); - for (Entry<String, String> entry : entrySet) { + if (apkfilters != null) { + for (Entry<String, String> entry : apkfilters) { String outPathFormat = osBinPath + File.separator + AndroidConstants.FN_RESOURCES_S_AP_; String outPath = String.format(outPathFormat, entry.getKey()); @@ -527,12 +529,11 @@ public class ApkBuilder extends BaseBuilder { } // now do the same thing for all the configured resource packages. - if (configs != null) { + if (apkfilters != null) { String resPathFormat = osBinPath + File.separator + AndroidConstants.FN_RESOURCES_S_AP_; - Set<Entry<String, String>> entrySet = configs.entrySet(); - for (Entry<String, String> entry : entrySet) { + for (Entry<String, String> entry : apkfilters) { // make the filename for the resource package. String resPath = String.format(resPathFormat, entry.getKey()); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/properties/AndroidPropertyPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/properties/AndroidPropertyPage.java index 7dc4ae2..a88f864 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/properties/AndroidPropertyPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/properties/AndroidPropertyPage.java @@ -18,7 +18,7 @@ package com.android.ide.eclipse.adt.internal.properties; import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.sdklib.IAndroidTarget; -import com.android.sdkuilib.internal.widgets.ApkConfigWidget; +import com.android.sdklib.internal.project.ApkSettings; import com.android.sdkuilib.internal.widgets.SdkTargetSelector; import org.eclipse.core.resources.IProject; @@ -27,14 +27,14 @@ 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.Control; +import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.ui.IWorkbenchPropertyPage; import org.eclipse.ui.dialogs.PropertyPage; -import java.util.Map; - /** * Property page for "Android" project. * This is accessible from the Package Explorer when right clicking a project and choosing @@ -45,7 +45,7 @@ public class AndroidPropertyPage extends PropertyPage implements IWorkbenchPrope private IProject mProject; private SdkTargetSelector mSelector; - private ApkConfigWidget mApkConfigWidget; + private Button mSplitByDensity; public AndroidPropertyPage() { // pass @@ -72,13 +72,13 @@ public class AndroidPropertyPage extends PropertyPage implements IWorkbenchPrope mSelector = new SdkTargetSelector(top, targets); - l = new Label(top, SWT.SEPARATOR | SWT.HORIZONTAL); - l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - - l = new Label(top, SWT.NONE); - l.setText("Project APK Configurations"); + Group g = new Group(top, SWT.NONE); + g.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + g.setLayout(new GridLayout(1, false)); + g.setText("APK Generation"); - mApkConfigWidget = new ApkConfigWidget(top); + mSplitByDensity = new Button(g, SWT.CHECK); + mSplitByDensity.setText("One APK per density"); // fill the ui Sdk currentSdk = Sdk.getCurrent(); @@ -89,9 +89,9 @@ public class AndroidPropertyPage extends PropertyPage implements IWorkbenchPrope mSelector.setSelection(target); } - // get the apk configurations - Map<String, String> configs = currentSdk.getProjectApkConfigs(mProject); - mApkConfigWidget.fillTable(configs); + // get the project settings + ApkSettings settings = currentSdk.getApkSettings(mProject); + mSplitByDensity.setSelection(settings.isSplitByDpi()); } mSelector.setSelectionListener(new SelectionAdapter() { @@ -114,8 +114,10 @@ public class AndroidPropertyPage extends PropertyPage implements IWorkbenchPrope public boolean performOk() { Sdk currentSdk = Sdk.getCurrent(); if (currentSdk != null) { - currentSdk.setProject(mProject, mSelector.getSelected(), - mApkConfigWidget.getApkConfigs()); + ApkSettings apkSettings = new ApkSettings(); + apkSettings.setSplitByDensity(mSplitByDensity.getSelection()); + + currentSdk.setProject(mProject, mSelector.getSelected(), apkSettings); } return true; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java index f2e883d..f93b6c5 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java @@ -30,6 +30,7 @@ import com.android.sdklib.SdkConstants; import com.android.sdklib.SdkManager; import com.android.sdklib.internal.avd.AvdManager; import com.android.sdklib.internal.project.ApkConfigurationHelper; +import com.android.sdklib.internal.project.ApkSettings; import com.android.sdklib.internal.project.ProjectProperties; import com.android.sdklib.internal.project.ProjectProperties.PropertyType; @@ -68,8 +69,8 @@ public class Sdk implements IProjectListener { new HashMap<IProject, IAndroidTarget>(); private final HashMap<IAndroidTarget, AndroidTargetData> mTargetDataMap = new HashMap<IAndroidTarget, AndroidTargetData>(); - private final HashMap<IProject, Map<String, String>> mProjectApkConfigMap = - new HashMap<IProject, Map<String, String>>(); + private final HashMap<IProject, ApkSettings> mApkSettingsMap = + new HashMap<IProject, ApkSettings>(); private final String mDocBaseUrl; /** @@ -193,11 +194,9 @@ public class Sdk implements IProjectListener { * apk configurations should not be updated. */ public void setProject(IProject project, IAndroidTarget target, - Map<String, String> apkConfigMap) { + ApkSettings settings) { synchronized (AdtPlugin.getDefault().getSdkLockObject()) { boolean resolveProject = false; - boolean compileProject = false; - boolean cleanProject = false; ProjectProperties properties = ProjectProperties.load( project.getLocation().toOSString(), PropertyType.DEFAULT); @@ -222,15 +221,17 @@ public class Sdk implements IProjectListener { } } - if (apkConfigMap != null) { - // save the apk configs in the project persistent property - cleanProject = ApkConfigurationHelper.setConfigs(properties, apkConfigMap); + // if there's no settings, force default values (to reset possibly changed + // values in a previous call. + if (settings == null) { + settings = new ApkSettings(); + } - // put it in a local map for easy access. - mProjectApkConfigMap.put(project, apkConfigMap); + // save the project settings into the project persistent property + ApkConfigurationHelper.setProperties(properties, settings); - compileProject = true; - } + // put it in a local map for easy access. + mApkSettingsMap.put(project, settings); // we are done with the modification. Save the property file. try { @@ -242,17 +243,14 @@ public class Sdk implements IProjectListener { if (resolveProject) { // force a resolve of the project by updating the classpath container. + // This will also force a recompile. IJavaProject javaProject = JavaCore.create(project); AndroidClasspathContainerInitializer.updateProjects( new IJavaProject[] { javaProject }); - } else if (compileProject) { - // If there was removed configs, we clean instead of build - // (to remove the obsolete ap_ and apk file from removed configs). + } else { + // always do a full clean/build. try { - project.build(cleanProject ? - IncrementalProjectBuilder.CLEAN_BUILD : - IncrementalProjectBuilder.FULL_BUILD, - null); + project.build(IncrementalProjectBuilder.CLEAN_BUILD, null); } catch (CoreException e) { // failed to build? force resolve instead. IJavaProject javaProject = JavaCore.create(project); @@ -316,10 +314,10 @@ public class Sdk implements IProjectListener { if (sdkStorage != null) { synchronized (AdtPlugin.getDefault().getSdkLockObject()) { - Map<String, String> configMap = ApkConfigurationHelper.getConfigs(properties); + ApkSettings settings = ApkConfigurationHelper.getSettings(properties); - if (configMap != null) { - sdkStorage.mProjectApkConfigMap.put(project, configMap); + if (settings != null) { + sdkStorage.mApkSettingsMap.put(project, settings); } } } @@ -392,13 +390,11 @@ public class Sdk implements IProjectListener { } /** - * Returns the configuration map for a given project. - * <p/>The Map key are name to be used in the apk filename, while the values are comma separated - * config values. The config value can be passed directly to aapt through the -c option. + * Returns the APK settings for a given project. */ - public Map<String, String> getProjectApkConfigs(IProject project) { + public ApkSettings getApkSettings(IProject project) { synchronized (AdtPlugin.getDefault().getSdkLockObject()) { - return mProjectApkConfigMap.get(project); + return mApkSettingsMap.get(project); } } @@ -505,7 +501,7 @@ public class Sdk implements IProjectListener { // now remove the project for the maps. mProjectTargetMap.remove(project); - mProjectApkConfigMap.remove(project); + mApkSettingsMap.remove(project); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/KeyCheckPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/KeyCheckPage.java index 0f630c4..5f8a8d5 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/KeyCheckPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/KeyCheckPage.java @@ -19,6 +19,7 @@ package com.android.ide.eclipse.adt.internal.wizards.export; import com.android.ide.eclipse.adt.internal.project.ProjectHelper; import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.ide.eclipse.adt.internal.wizards.export.ExportWizard.ExportWizardPage; +import com.android.sdklib.internal.project.ApkSettings; import org.eclipse.core.resources.IProject; import org.eclipse.swt.SWT; @@ -68,17 +69,17 @@ final class KeyCheckPage extends ExportWizardPage { private Text mDestination; private boolean mFatalSigningError; private FormText mDetailText; - /** The Apk Config map for the current project */ - private Map<String, String> mApkConfig; private ScrolledComposite mScrolledComposite; - + + private ApkSettings mApkSettings; + private String mKeyDetails; private String mDestinationDetails; protected KeyCheckPage(ExportWizard wizard, String pageName) { super(pageName); mWizard = wizard; - + setTitle("Destination and key/certificate checks"); setDescription(""); // TODO } @@ -93,7 +94,7 @@ final class KeyCheckPage extends ExportWizardPage { GridLayout gl = new GridLayout(3, false); gl.verticalSpacing *= 3; composite.setLayout(gl); - + GridData gd; new Label(composite, SWT.NONE).setText("Destination APK file:"); @@ -110,26 +111,26 @@ final class KeyCheckPage extends ExportWizardPage { @Override public void widgetSelected(SelectionEvent e) { FileDialog fileDialog = new FileDialog(browseButton.getShell(), SWT.SAVE); - + fileDialog.setText("Destination file name"); // get a default apk name based on the project String filename = ProjectHelper.getApkFilename(mWizard.getProject(), null /*config*/); fileDialog.setFileName(filename); - + String saveLocation = fileDialog.open(); if (saveLocation != null) { mDestination.setText(saveLocation); } } }); - + mScrolledComposite = new ScrolledComposite(composite, SWT.V_SCROLL); mScrolledComposite.setLayoutData(gd = new GridData(GridData.FILL_BOTH)); gd.horizontalSpan = 3; mScrolledComposite.setExpandHorizontal(true); mScrolledComposite.setExpandVertical(true); - + mDetailText = new FormText(mScrolledComposite, SWT.NONE); mScrolledComposite.setContent(mDetailText); @@ -139,18 +140,18 @@ final class KeyCheckPage extends ExportWizardPage { updateScrolling(); } }); - + setControl(composite); } - + @Override void onShow() { // fill the texts with information loaded from the project. if ((mProjectDataChanged & DATA_PROJECT) != 0) { // reset the destination from the content of the project IProject project = mWizard.getProject(); - mApkConfig = Sdk.getCurrent().getProjectApkConfigs(project); - + mApkSettings = Sdk.getCurrent().getApkSettings(project); + String destination = ProjectHelper.loadStringProperty(project, ExportWizard.PROPERTY_DESTINATION); String filename = ProjectHelper.loadStringProperty(project, @@ -159,7 +160,7 @@ final class KeyCheckPage extends ExportWizardPage { mDestination.setText(destination + File.separator + filename); } } - + // if anything change we basically reload the data. if (mProjectDataChanged != 0) { mFatalSigningError = false; @@ -170,7 +171,7 @@ final class KeyCheckPage extends ExportWizardPage { mPrivateKey = null; mCertificate = null; mKeyDetails = null; - + if (mWizard.getKeystoreCreationMode() || mWizard.getKeyCreationMode()) { int validity = mWizard.getValidity(); StringBuilder sb = new StringBuilder( @@ -196,13 +197,13 @@ final class KeyCheckPage extends ExportWizardPage { mWizard.getKeyAlias(), new KeyStore.PasswordProtection( mWizard.getKeyPassword().toCharArray())); - + if (entry != null) { mPrivateKey = entry.getPrivateKey(); mCertificate = (X509Certificate)entry.getCertificate(); } else { setErrorMessage("Unable to find key."); - + setPageComplete(false); } } catch (FileNotFoundException e) { @@ -220,33 +221,33 @@ final class KeyCheckPage extends ExportWizardPage { } catch (IOException e) { onException(e); } - + if (mPrivateKey != null && mCertificate != null) { Calendar expirationCalendar = Calendar.getInstance(); expirationCalendar.setTime(mCertificate.getNotAfter()); Calendar today = Calendar.getInstance(); - + if (expirationCalendar.before(today)) { mKeyDetails = String.format( "<p>Certificate expired on %s</p>", mCertificate.getNotAfter().toString()); - + // fatal error = nothing can make the page complete. mFatalSigningError = true; - + setErrorMessage("Certificate is expired."); setPageComplete(false); } else { // valid, key/cert: put it in the wizard so that it can be finished mWizard.setSigningInfo(mPrivateKey, mCertificate); - + StringBuilder sb = new StringBuilder(String.format( "<p>Certificate expires on %s.</p>", mCertificate.getNotAfter().toString())); - + int expirationYear = expirationCalendar.get(Calendar.YEAR); int thisYear = today.get(Calendar.YEAR); - + if (thisYear + 25 < expirationYear) { // do nothing } else { @@ -258,14 +259,14 @@ final class KeyCheckPage extends ExportWizardPage { "<p>The Certificate expires in %1$s %2$s.</p>", count, count == 1 ? "year" : "years")); } - + sb.append("<p>Make sure the certificate is valid for the planned lifetime of the product.</p>"); sb.append("<p>If the certificate expires, you will be forced to sign your application with a different one.</p>"); sb.append("<p>Applications cannot be upgraded if their certificate changes from one version to another, "); sb.append("forcing a full uninstall/install, which will make the user lose his/her data.</p>"); sb.append("<p>Android Market currently requires certificates to be valid until 2033.</p>"); } - + mKeyDetails = sb.toString(); } } else { @@ -277,7 +278,7 @@ final class KeyCheckPage extends ExportWizardPage { onDestinationChange(true /*forceDetailUpdate*/); } - + /** * Callback for destination field edition * @param forceDetailUpdate if true, the detail {@link FormText} is updated even if a fatal @@ -319,12 +320,12 @@ final class KeyCheckPage extends ExportWizardPage { // 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(); @@ -360,7 +361,7 @@ final class KeyCheckPage extends ExportWizardPage { updateDetailText(); } } - + /** * Updates the scrollbar to match the content of the {@link FormText} or the new size * of the {@link ScrolledComposite}. @@ -372,41 +373,40 @@ final class KeyCheckPage extends ExportWizardPage { 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 File name from the destination field * @return A list of destination filenames based <code>file</code> and the list of APK * configurations for the project. */ 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( @@ -415,29 +415,32 @@ final class KeyCheckPage extends ExportWizardPage { 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); + if (mApkSettings != null) { + Map<String, String> apkFilters = mApkSettings.getResourceFilters(); + if (apkFilters.size() > 0) { + // remove the extension from the user-chosen filename + int index = filename.lastIndexOf('.'); + String base = filename.substring(0, index); + String extension = filename.substring(index); + + for (Entry<String, String> entry : apkFilters.entrySet()) { + apkArray = new String[ExportWizard.APK_COUNT]; + apkArray[ExportWizard.APK_FILE_SOURCE] = ProjectHelper.getApkFilename( + mWizard.getProject(), entry.getKey()); + apkArray[ExportWizard.APK_FILE_DEST] = base + "-" + //$NON-NLS-1$ + 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)); } } diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkConfigurationHelper.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkConfigurationHelper.java index eeab46a..4ba6fa6 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkConfigurationHelper.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkConfigurationHelper.java @@ -16,91 +16,33 @@ package com.android.sdklib.internal.project; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.Map.Entry; /** * Helper class to read and write Apk Configuration into a {@link ProjectProperties} file. */ public class ApkConfigurationHelper { - /** Prefix for property names for config definition. This prevents having config named - * after other valid properties such as "target". */ - final static String CONFIG_PREFIX = "apk-config-"; - /** - * Reads the Apk Configurations from a {@link ProjectProperties} file and returns them as a map. - * <p/>If there are no defined configurations, the returned map will be empty. - * @return a map of apk configurations. The map contains (name, filter) where name is - * the name of the configuration (a-zA-Z0-9 only), and filter is the comma separated list of - * resource configuration to include in the apk (see aapt -c) + * Reads the project settings from a {@link ProjectProperties} file and returns them as a + * {@link ApkSettings} object. */ - public static Map<String, String> getConfigs(ProjectProperties properties) { - HashMap<String, String> configMap = new HashMap<String, String>(); + public static ApkSettings getSettings(ProjectProperties properties) { + ApkSettings apkSettings = new ApkSettings(); + + boolean splitByDensity = Boolean.parseBoolean(properties.getProperty( + ProjectProperties.PROPERTY_SPLIT_BY_DENSITY)); + apkSettings.setSplitByDensity(splitByDensity); + - // get the list of configs. - String configList = properties.getProperty(ProjectProperties.PROPERTY_APK_CONFIGS); - if (configList != null) { - // this is a comma separated list - String[] configs = configList.split(","); //$NON-NLS-1$ - - // read the value of each config and store it in a map - for (String config : configs) { - config = config.trim(); - String configValue = properties.getProperty(CONFIG_PREFIX + config); - if (configValue != null) { - configMap.put(config, configValue); - } - } - } - - return configMap; + return apkSettings; } - + /** - * Writes the Apk Configurations from a given map into a {@link ProjectProperties}. - * @param properties the {@link ProjectProperties} in which to store the apk configurations. - * @param configMap a map of apk configurations. The map contains (name, filter) where name is - * the name of the configuration (a-zA-Z0-9 only), and filter is the comma separated list of - * resource configuration to include in the apk (see aapt -c) - * @return true if the {@link ProjectProperties} contained Apk Configuration that were not - * present in the map. + * Sets the content of a {@link ApkSettings} into a {@link ProjectProperties}. + * @param properties the {@link ProjectProperties} in which to store the settings. + * @param settings the project settings to store. */ - public static boolean setConfigs(ProjectProperties properties, Map<String, String> configMap) { - // load the current configs, in order to remove the value properties for each of them - // in case a config was removed. - - // get the list of configs. - String configList = properties.getProperty(ProjectProperties.PROPERTY_APK_CONFIGS); - - boolean hasRemovedConfig = false; - - if (configList != null) { - // this is a comma separated list - String[] configs = configList.split(","); //$NON-NLS-1$ - - for (String config : configs) { - config = config.trim(); - if (configMap.containsKey(config) == false) { - hasRemovedConfig = true; - properties.removeProperty(CONFIG_PREFIX + config); - } - } - } - - // now add the properties. - Set<Entry<String, String>> entrySet = configMap.entrySet(); - StringBuilder sb = new StringBuilder(); - for (Entry<String, String> entry : entrySet) { - if (sb.length() > 0) { - sb.append(","); - } - sb.append(entry.getKey()); - properties.setProperty(CONFIG_PREFIX + entry.getKey(), entry.getValue()); - } - properties.setProperty(ProjectProperties.PROPERTY_APK_CONFIGS, sb.toString()); - - return hasRemovedConfig; + public static void setProperties(ProjectProperties properties, ApkSettings settings) { + properties.setProperty(ProjectProperties.PROPERTY_SPLIT_BY_DENSITY, + Boolean.toString(settings.isSplitByDpi())); } } diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkSettings.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkSettings.java new file mode 100644 index 0000000..c96e223 --- /dev/null +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkSettings.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2009 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.android.sdklib.internal.project; + +import java.util.HashMap; +import java.util.Map; + +/** + * Settings for multiple APK generation. + */ +public class ApkSettings { + private boolean mSplitByDpi = false; + + public ApkSettings() { + } + + /** + * Returns a map of configuration filters to be used by the -c option of aapt. + * <p/>The map stores (key, value) pairs where the keys is a filename modifier and the value + * is the parameter to pass to aapt through the -c option. + * @return a map, always. It can however be empty. + */ + public Map<String, String> getResourceFilters() { + Map<String, String> map = new HashMap<String, String>(); + if (mSplitByDpi) { + map.put("hdpi", "hdpi,nodpi"); + map.put("mdpi", "mdpi,nodpi"); + map.put("ldpi", "ldpi,nodpi"); + } + + return map; + } + + /** + * Indicates whether APKs should be generate for each dpi level. + */ + public boolean isSplitByDpi() { + return mSplitByDpi; + } + + public void setSplitByDensity(boolean split) { + mSplitByDpi = split; + } +} diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java index b3c172b..5b35d50 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java @@ -35,14 +35,17 @@ import java.util.Map.Entry; public final class ProjectProperties { /** The property name for the project target */ public final static String PROPERTY_TARGET = "target"; - public final static String PROPERTY_APK_CONFIGS = "apk.configurations"; + public final static String PROPERTY_SDK = "sdk.dir"; // LEGACY - compatibility with 1.6 and before public final static String PROPERTY_SDK_LEGACY = "sdk-location"; + public final static String PROPERTY_APP_PACKAGE = "application.package"; // LEGACY - compatibility with 1.6 and before public final static String PROPERTY_APP_PACKAGE_LEGACY = "application-package"; + public final static String PROPERTY_SPLIT_BY_DENSITY = "split.density"; + public static enum PropertyType { BUILD("build.properties", BUILD_HEADER), DEFAULT("default.properties", DEFAULT_HEADER), @@ -107,17 +110,8 @@ public final class ProjectProperties { // 1-------10--------20--------30--------40--------50--------60--------70--------80 COMMENT_MAP.put(PROPERTY_TARGET, "# Project target.\n"); - COMMENT_MAP.put(PROPERTY_APK_CONFIGS, - "# apk configurations. This property allows creation of APK files with limited\n" + - "# resources. For example, if your application contains many locales and\n" + - "# you wish to release multiple smaller apks instead of a large one, you can\n" + - "# define configuration to create apks with limited language sets.\n" + - "# Format is a comma separated list of configuration names. For each\n" + - "# configuration, a property will declare the resource configurations to\n" + - "# include. Example:\n" + - "# " + PROPERTY_APK_CONFIGS +"=european,northamerica\n" + - "# " + ApkConfigurationHelper.CONFIG_PREFIX + "european=en,fr,it,de,es\n" + - "# " + ApkConfigurationHelper.CONFIG_PREFIX + "northamerica=en,es\n"); + COMMENT_MAP.put(PROPERTY_SPLIT_BY_DENSITY, + "# Indicates whether an apk should be generated for each density.\n"); COMMENT_MAP.put(PROPERTY_SDK, "# location of the SDK. This is only used by Ant\n" + "# For customization when using a Version Control System, please read the\n" + diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigEditDialog.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigEditDialog.java deleted file mode 100644 index be241a5..0000000 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigEditDialog.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2009 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.android.sdkuilib.internal.widgets; - -import org.eclipse.jface.dialogs.Dialog; -import org.eclipse.jface.dialogs.IDialogConstants; -import org.eclipse.jface.window.Window; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.VerifyEvent; -import org.eclipse.swt.events.VerifyListener; -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.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; - -/** - * Edit dialog to create/edit APK configuration. The dialog displays 2 text fields for the config - * name and its filter. - */ -class ApkConfigEditDialog extends Dialog implements ModifyListener, VerifyListener { - - private String mName; - private String mFilter; - private Text mNameField; - private Text mFilterField; - private Button mOkButton; - - /** - * Creates an edit dialog with optional initial values for the name and filter. - * @param name optional value for the name. Can be null. - * @param filter optional value for the filter. Can be null. - * @param parentShell the parent shell. - */ - protected ApkConfigEditDialog(String name, String filter, Shell parentShell) { - super(parentShell); - mName = name; - mFilter = filter; - } - - /** - * Returns the name of the config. This is only valid if the user clicked OK and {@link #open()} - * returned {@link Window#OK} - */ - public String getName() { - return mName; - } - - /** - * Returns the filter for the config. This is only valid if the user clicked OK and - * {@link #open()} returned {@link Window#OK} - */ - public String getFilter() { - return mFilter; - } - - @Override - protected Control createContents(Composite parent) { - Control control = super.createContents(parent); - - mOkButton = getButton(IDialogConstants.OK_ID); - validateButtons(); - - return control; - } - - @Override - protected Control createDialogArea(Composite parent) { - Composite composite = new Composite(parent, SWT.NONE); - GridLayout layout; - composite.setLayout(layout = new GridLayout(2, false)); - layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN); - layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN); - layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING); - layout.horizontalSpacing = convertHorizontalDLUsToPixels( - IDialogConstants.HORIZONTAL_SPACING); - - composite.setLayoutData(new GridData(GridData.FILL_BOTH)); - - Label l = new Label(composite, SWT.NONE); - l.setText("Name"); - - mNameField = new Text(composite, SWT.BORDER); - mNameField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - mNameField.addVerifyListener(this); - if (mName != null) { - mNameField.setText(mName); - } - mNameField.addModifyListener(this); - - l = new Label(composite, SWT.NONE); - l.setText("Filter"); - - mFilterField = new Text(composite, SWT.BORDER); - mFilterField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - if (mFilter != null) { - mFilterField.setText(mFilter); - } - mFilterField.addVerifyListener(this); - mFilterField.addModifyListener(this); - - applyDialogFont(composite); - return composite; - } - - /** - * Validates the OK button based on the content of the 2 text fields. - */ - private void validateButtons() { - mOkButton.setEnabled(mNameField.getText().trim().length() > 0 && - mFilterField.getText().trim().length() > 0); - } - - @Override - protected void okPressed() { - mName = mNameField.getText(); - mFilter = mFilterField.getText().trim(); - super.okPressed(); - } - - /** - * Callback for text modification in the 2 text fields. - */ - public void modifyText(ModifyEvent e) { - validateButtons(); - } - - /** - * Callback to ensure the content of the text field are proper. - */ - public void verifyText(VerifyEvent e) { - Text source = ((Text)e.getSource()); - if (source == mNameField) { - // check for a-zA-Z0-9. - final String text = e.text; - final int len = text.length(); - for (int i = 0 ; i < len; i++) { - char letter = text.charAt(i); - if (letter > 255 || Character.isLetterOrDigit(letter) == false) { - e.doit = false; - return; - } - } - } else if (source == mFilterField) { - // we can't validate the content as its typed, but we can at least ensure the characters - // are valid. Same as mNameFiled + the comma. - final String text = e.text; - final int len = text.length(); - for (int i = 0 ; i < len; i++) { - char letter = text.charAt(i); - if (letter > 255 || (Character.isLetterOrDigit(letter) == false && letter != ',')) { - e.doit = false; - return; - } - } - } - } -} diff --git a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigWidget.java b/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigWidget.java deleted file mode 100644 index a05f9bd..0000000 --- a/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigWidget.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) 2009 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.android.sdkuilib.internal.widgets; - -import org.eclipse.jface.dialogs.Dialog; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ControlAdapter; -import org.eclipse.swt.events.ControlEvent; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.Rectangle; -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.Table; -import org.eclipse.swt.widgets.TableColumn; -import org.eclipse.swt.widgets.TableItem; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** - * The APK Configuration widget is a table that is added to the given parent composite. - * <p/> - * To use, create it using {@link #ApkConfigWidget(Composite)} then - * call {@link #fillTable(Map)} to set the initial list of configurations. - */ -public class ApkConfigWidget { - private final static int INDEX_NAME = 0; - private final static int INDEX_FILTER = 1; - - private Table mApkConfigTable; - private Button mEditButton; - private Button mDelButton; - - public ApkConfigWidget(final Composite parent) { - final Composite apkConfigComp = new Composite(parent, SWT.NONE); - apkConfigComp.setLayoutData(new GridData(GridData.FILL_BOTH)); - apkConfigComp.setLayout(new GridLayout(2, false)); - - mApkConfigTable = new Table(apkConfigComp, SWT.FULL_SELECTION | SWT.SINGLE | SWT.BORDER); - mApkConfigTable.setHeaderVisible(true); - mApkConfigTable.setLinesVisible(true); - - GridData data = new GridData(); - data.grabExcessVerticalSpace = true; - data.grabExcessHorizontalSpace = true; - data.horizontalAlignment = GridData.FILL; - data.verticalAlignment = GridData.FILL; - mApkConfigTable.setLayoutData(data); - - // create the table columns - final TableColumn column0 = new TableColumn(mApkConfigTable, SWT.NONE); - column0.setText("Name"); - column0.setWidth(100); - final TableColumn column1 = new TableColumn(mApkConfigTable, SWT.NONE); - column1.setText("Configuration"); - column1.setWidth(100); - - Composite buttonComp = new Composite(apkConfigComp, SWT.NONE); - buttonComp.setLayoutData(new GridData(GridData.FILL_VERTICAL)); - GridLayout gl; - buttonComp.setLayout(gl = new GridLayout(1, false)); - gl.marginHeight = gl.marginWidth = 0; - - Button newButton = new Button(buttonComp, SWT.PUSH | SWT.FLAT); - newButton.setText("New..."); - newButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - - mEditButton = new Button(buttonComp, SWT.PUSH | SWT.FLAT); - mEditButton.setText("Edit..."); - mEditButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - - mDelButton = new Button(buttonComp, SWT.PUSH | SWT.FLAT); - mDelButton.setText("Delete"); - mDelButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - - newButton.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - ApkConfigEditDialog dlg = new ApkConfigEditDialog(null /*name*/, null /*filter*/, - apkConfigComp.getShell()); - if (dlg.open() == Dialog.OK) { - TableItem item = new TableItem(mApkConfigTable, SWT.NONE); - item.setText(INDEX_NAME, dlg.getName()); - item.setText(INDEX_FILTER, dlg.getFilter()); - - onSelectionChanged(); - } - } - }); - - mEditButton.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - // get the current selection (single mode so we don't care about any item beyond - // index 0). - TableItem[] items = mApkConfigTable.getSelection(); - if (items.length != 0) { - ApkConfigEditDialog dlg = new ApkConfigEditDialog( - items[0].getText(INDEX_NAME), items[0].getText(INDEX_FILTER), - apkConfigComp.getShell()); - if (dlg.open() == Dialog.OK) { - items[0].setText(INDEX_NAME, dlg.getName()); - items[0].setText(INDEX_FILTER, dlg.getFilter()); - } - } - } - }); - - mDelButton.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - // get the current selection (single mode so we don't care about any item beyond - // index 0). - int[] indices = mApkConfigTable.getSelectionIndices(); - if (indices.length != 0) { - TableItem item = mApkConfigTable.getItem(indices[0]); - if (MessageDialog.openQuestion(parent.getShell(), - "Apk Configuration deletion", - String.format( - "Are you sure you want to delete configuration '%1$s'?", - item.getText(INDEX_NAME)))) { - // delete the item. - mApkConfigTable.remove(indices[0]); - - onSelectionChanged(); - } - } - } - }); - - // Add a listener to resize the column to the full width of the table - mApkConfigTable.addControlListener(new ControlAdapter() { - @Override - public void controlResized(ControlEvent e) { - Rectangle r = mApkConfigTable.getClientArea(); - column0.setWidth(r.width * 30 / 100); // 30% - column1.setWidth(r.width * 70 / 100); // 70% - } - }); - - // add a selection listener on the table, to enable/disable buttons. - mApkConfigTable.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - onSelectionChanged(); - } - }); - } - - public void fillTable(Map<String, String> apkConfigMap) { - // get the names in a list so that we can sort them. - if (apkConfigMap != null) { - Set<String> keys = apkConfigMap.keySet(); - String[] keyArray = keys.toArray(new String[keys.size()]); - Arrays.sort(keyArray); - - for (String key : keyArray) { - TableItem item = new TableItem(mApkConfigTable, SWT.NONE); - item.setText(INDEX_NAME, key); - item.setText(INDEX_FILTER, apkConfigMap.get(key)); - } - } - - onSelectionChanged(); - } - - public Map<String, String> getApkConfigs() { - // go through all the items from the table and fill a new map - HashMap<String, String> map = new HashMap<String, String>(); - - TableItem[] items = mApkConfigTable.getItems(); - for (TableItem item : items) { - map.put(item.getText(INDEX_NAME), item.getText(INDEX_FILTER)); - } - - return map; - } - - /** - * Handles table selection changes. - */ - private void onSelectionChanged() { - if (mApkConfigTable.getSelectionCount() > 0) { - mEditButton.setEnabled(true); - mDelButton.setEnabled(true); - } else { - mEditButton.setEnabled(false); - mDelButton.setEnabled(false); - } - } -} |