From 338bc1cc45b370dcb16f581bb5e9625dc67ec63a Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet Date: Mon, 24 Aug 2009 11:07:51 -0700 Subject: [PATCH] Move from arbitrary resource filters to fix ones Resource filters are used when generating additional APK containing only specific resources. The previous UI allowed for any type of filters, but we are moving to a simpler way with fixed filters. The first one is the density. Selecting the filter will generate 4 APKs per application: default (all resources), hdpi (only hdpi/nodpi and default resources), mdpi, ldpi. --- .../src/com/android/ant/AaptExecLoopTask.java | 61 ++--- .../src/com/android/ant/ApkBuilderTask.java | 14 +- .../adt/internal/build/ApkBuilder.java | 51 ++--- .../properties/AndroidPropertyPage.java | 32 +-- .../ide/eclipse/adt/internal/sdk/Sdk.java | 54 +++-- .../internal/wizards/export/KeyCheckPage.java | 111 ++++----- .../project/ApkConfigurationHelper.java | 92 ++------ .../sdklib/internal/project/ApkSettings.java | 58 +++++ .../internal/project/ProjectProperties.java | 18 +- .../internal/widgets/ApkConfigEditDialog.java | 177 --------------- .../internal/widgets/ApkConfigWidget.java | 211 ------------------ 11 files changed, 246 insertions(+), 633 deletions(-) create mode 100644 tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkSettings.java delete mode 100644 tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigEditDialog.java delete mode 100644 tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigWidget.java diff --git a/tools/anttasks/src/com/android/ant/AaptExecLoopTask.java b/tools/anttasks/src/com/android/ant/AaptExecLoopTask.java index ef74fe743..47b8f482e 100644 --- a/tools/anttasks/src/com/android/ant/AaptExecLoopTask.java +++ b/tools/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 apkConfigs = ApkConfigurationHelper.getConfigs(properties); - if (apkConfigs.size() > 0) { - Set> entrySet = apkConfigs.entrySet(); - for (Entry entry : entrySet) { - createPackage(entry.getKey(), entry.getValue()); + + + ApkSettings apkSettings = ApkConfigurationHelper.getSettings(properties); + if (apkSettings != null) { + Map apkFilters = apkSettings.getResourceFilters(); + if (apkFilters.size() > 0) { + for (Entry 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/tools/anttasks/src/com/android/ant/ApkBuilderTask.java b/tools/anttasks/src/com/android/ant/ApkBuilderTask.java index 3a15368e0..b2c445da6 100644 --- a/tools/anttasks/src/com/android/ant/ApkBuilderTask.java +++ b/tools/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 apkConfigs = ApkConfigurationHelper.getConfigs(properties); - if (apkConfigs.size() > 0) { - Set> entrySet = apkConfigs.entrySet(); - for (Entry entry : entrySet) { - createApk(apkBuilder, entry.getKey(), entry.getValue(), path); + ApkSettings apkSettings = ApkConfigurationHelper.getSettings(properties); + if (apkSettings != null) { + Map apkFilters = apkSettings.getResourceFilters(); + if (apkFilters.size() > 0) { + for (Entry entry : apkFilters.entrySet()) { + createApk(apkBuilder, entry.getKey(), entry.getValue(), path); + } } } diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilder.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilder.java index b49ee6e8a..1ed512309 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/ApkBuilder.java +++ b/tools/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 configs = Sdk.getCurrent().getProjectApkConfigs(project); + // get the APK configs for the project. + ApkSettings apkSettings = Sdk.getCurrent().getApkSettings(project); + Set> apkfilters = null; + if (apkSettings != null) { + Map 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> entrySet = configs.entrySet(); - - for (Entry entry : entrySet) { + // if the full package is present, we check the filtered resource packages + // as well + if (apkfilters != null) { + for (Entry 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> entrySet = configs.entrySet(); - - for (Entry entry : entrySet) { + for (Entry 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> entrySet = configs.entrySet(); - for (Entry entry : entrySet) { + if (apkfilters != null) { + for (Entry 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> entrySet = configs.entrySet(); - for (Entry entry : entrySet) { + if (apkfilters != null) { + for (Entry 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> entrySet = configs.entrySet(); - for (Entry entry : entrySet) { + for (Entry entry : apkfilters) { // make the filename for the resource package. String resPath = String.format(resPathFormat, entry.getKey()); diff --git a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/properties/AndroidPropertyPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/properties/AndroidPropertyPage.java index 7dc4ae21c..a88f86473 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/properties/AndroidPropertyPage.java +++ b/tools/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)); + Group g = new Group(top, SWT.NONE); + g.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + g.setLayout(new GridLayout(1, false)); + g.setText("APK Generation"); - l = new Label(top, SWT.NONE); - l.setText("Project APK Configurations"); - - 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 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/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java index f2e883dcb..f93b6c572 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/sdk/Sdk.java +++ b/tools/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(); private final HashMap mTargetDataMap = new HashMap(); - private final HashMap> mProjectApkConfigMap = - new HashMap>(); + private final HashMap mApkSettingsMap = + new HashMap(); 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 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,16 +221,18 @@ public class Sdk implements IProjectListener { } } - if (apkConfigMap != null) { - // save the apk configs in the project persistent property - cleanProject = ApkConfigurationHelper.setConfigs(properties, apkConfigMap); - - // put it in a local map for easy access. - mProjectApkConfigMap.put(project, apkConfigMap); - - compileProject = true; + // if there's no settings, force default values (to reset possibly changed + // values in a previous call. + if (settings == null) { + settings = new ApkSettings(); } + // save the project settings into the project persistent property + ApkConfigurationHelper.setProperties(properties, settings); + + // put it in a local map for easy access. + mApkSettingsMap.put(project, settings); + // we are done with the modification. Save the property file. try { properties.save(); @@ -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 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. - *

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 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/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/KeyCheckPage.java b/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/KeyCheckPage.java index 0f630c4b8..5f8a8d531 100644 --- a/tools/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/KeyCheckPage.java +++ b/tools/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 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( "

Certificate expired on %s

", 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( "

Certificate expires on %s.

", 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 { "

The Certificate expires in %1$s %2$s.

", count, count == 1 ? "year" : "years")); } - + sb.append("

Make sure the certificate is valid for the planned lifetime of the product.

"); sb.append("

If the certificate expires, you will be forced to sign your application with a different one.

"); sb.append("

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.

"); sb.append("

Android Market currently requires certificates to be valid until 2033.

"); } - + 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 apkFileMap = getApkFileMap(file); - + // display them boolean fileExists = false; StringBuilder sb = new StringBuilder(String.format( "

This will create the following files:

")); - + Set> set = apkFileMap.entrySet(); for (Entry 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("
"); if (mKeyDetails != null) { sb.append(mKeyDetails); } - + if (mDestinationDetails != null && mFatalSigningError == false) { sb.append(mDestinationDetails); } - + sb.append("
"); - + 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 file and the list of APK * configurations for the project. */ private Map getApkFileMap(File file) { String filename = file.getName(); - + HashMap map = new HashMap(); - + // 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> set = mApkConfig.entrySet(); - for (Entry 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 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 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/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkConfigurationHelper.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkConfigurationHelper.java index eeab46a22..4ba6fa663 100644 --- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkConfigurationHelper.java +++ b/tools/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. - *

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 getConfigs(ProjectProperties properties) { - HashMap configMap = new HashMap(); + public static ApkSettings getSettings(ProjectProperties properties) { + ApkSettings apkSettings = new ApkSettings(); - // 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; + boolean splitByDensity = Boolean.parseBoolean(properties.getProperty( + ProjectProperties.PROPERTY_SPLIT_BY_DENSITY)); + apkSettings.setSplitByDensity(splitByDensity); + + + 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 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> entrySet = configMap.entrySet(); - StringBuilder sb = new StringBuilder(); - for (Entry 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/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkSettings.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ApkSettings.java new file mode 100644 index 000000000..c96e2237f --- /dev/null +++ b/tools/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. + *

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 getResourceFilters() { + Map map = new HashMap(); + 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/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java index b3c172b2e..5b35d5019 100644 --- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/project/ProjectProperties.java +++ b/tools/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/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigEditDialog.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigEditDialog.java deleted file mode 100644 index be241a502..000000000 --- a/tools/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/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigWidget.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/ApkConfigWidget.java deleted file mode 100644 index a05f9bdc8..000000000 --- a/tools/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. - *

- * 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 apkConfigMap) { - // get the names in a list so that we can sort them. - if (apkConfigMap != null) { - Set 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 getApkConfigs() { - // go through all the items from the table and fill a new map - HashMap map = new HashMap(); - - 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); - } - } -}