diff --git a/plugins/android/resources/messages/AndroidBundle.properties b/plugins/android/resources/messages/AndroidBundle.properties index 8735fe1b5a3c..750c67f782a8 100644 --- a/plugins/android/resources/messages/AndroidBundle.properties +++ b/plugins/android/resources/messages/AndroidBundle.properties @@ -168,13 +168,14 @@ android.dest.directory.title=De&stination directory android.generate.r.java.by.res.dir=Use res&ource directory specified at "Structure" section android.use.custom.r.java.source.dir=Use custo&m resource directory android.regenerate.idl.when.changed.checkbox=Regenerate &Java files when AIDL files are changed -android.wizard.application.project.button=A&pplication +android.wizard.application.project.button=&Application android.wizard.lib.project.button=&Library android.wizard.test.project.button=&Test android.wizard.tested.project.label=Tested &module: android.wizard.specify.tested.module.error=Please specify tested module android.wizard.tested.module.without.facet.error=Tested module must contain Android facet android.wizard.cannot.find.module.parent.dir.error=Cannot find module parent directory: {0} +android.wizard.invalid.sdk.error=Android SDK is invalid android.facet.editor.is.library.checkbox=&Is Library project android.run.sdk.manager.action.text=Android SDK and AVD Manager android.choose.sdk.label=Please choose Android SDK location @@ -284,4 +285,14 @@ android.renderscript.file.type.description=Android renderscript files android.renderscript.compiler.description=Android Renderscript Compiler android.extended.device.chooser.running.device.radio=&Choose a running device android.extended.device.chooser.launch.emulator.radio=&Launch emulator -android.extended.device.chooser.avd.label=&Android virtual device: \ No newline at end of file +android.extended.device.chooser.avd.label=&Android virtual device: +deployment.target.settings.show.dialog=Show &chooser dialog +deployment.target.settings.usb.device=&USB device +deployment.target.settings.emulator=Emulato&r +deployment.target.settings.prefer.avd.label=&Prefer Android Virtual Device: +deployment.target.settings.prefer.avd.label.wizard=Prefer &Android Virtual Device: +deployment.target.settings.title=Target Device +deployment.target.settings.wizard.configure.later=&Do not create run configuration +deployment.target.settings.wizard.show.dialog=&Show device chooser dialog +deployment.target.settings.wizard.usb.device=&USB device +deployment.target.settings.wizard.emulator=Emulat&or \ No newline at end of file diff --git a/plugins/android/src/org/jetbrains/android/actions/RunAndroidSdkManagerAction.java b/plugins/android/src/org/jetbrains/android/actions/RunAndroidSdkManagerAction.java index cb619249ca86..6defa8fef404 100644 --- a/plugins/android/src/org/jetbrains/android/actions/RunAndroidSdkManagerAction.java +++ b/plugins/android/src/org/jetbrains/android/actions/RunAndroidSdkManagerAction.java @@ -78,12 +78,12 @@ public class RunAndroidSdkManagerAction extends AnAction { } sdkPath = sdks[index]; } - runTool(project, sdkPath); + runTool(sdkPath); } - public static void runTool(@NotNull Project project, @NotNull String sdkPath) { + public static void runTool(@NotNull String sdkPath) { GeneralCommandLine commandLine = new GeneralCommandLine(); commandLine.setExePath(sdkPath + File.separator + AndroidUtils.toolPath(SdkConstants.androidCmdName())); - AndroidUtils.runExternalToolInSeparateThread(project, commandLine); + AndroidUtils.runExternalToolInSeparateThread(null, commandLine); } } diff --git a/plugins/android/src/org/jetbrains/android/facet/AndroidFrameworkDetector.java b/plugins/android/src/org/jetbrains/android/facet/AndroidFrameworkDetector.java index 2032b3e7dd6a..5660249a4289 100644 --- a/plugins/android/src/org/jetbrains/android/facet/AndroidFrameworkDetector.java +++ b/plugins/android/src/org/jetbrains/android/facet/AndroidFrameworkDetector.java @@ -63,7 +63,7 @@ public class AndroidFrameworkDetector extends FacetBasedFrameworkDetector= 0); - } - - private void addRunConfiguration(AndroidFacet facet) { + private void addRunConfiguration(@NotNull AndroidFacet facet, + @NotNull TargetSelectionMode targetSelectionMode, + @Nullable String targetAvd) { + String activityClass; if (isHelloAndroid()) { - String activityClass = myPackageName + '.' + myActivityName; - Module module = facet.getModule(); - AndroidUtils.addRunConfiguration(module.getProject(), facet, activityClass, !isFirstModule(module)); + activityClass = myPackageName + '.' + myActivityName; } + else { + activityClass = null; + } + Module module = facet.getModule(); + AndroidUtils.addRunConfiguration(module.getProject(), facet, activityClass, false, targetSelectionMode, targetAvd); } - private static void addTestRunConfiguration(final AndroidFacet facet) { - String moduleName = facet.getModule().getName(); + private static void addTestRunConfiguration(final AndroidFacet facet, @NotNull TargetSelectionMode mode, @Nullable String preferredAvd) { Project project = facet.getModule().getProject(); - int result = Messages.showYesNoDialog(project, AndroidBundle.message("create.run.configuration.question", moduleName), - AndroidBundle.message("create.run.configuration.title"), Messages.getQuestionIcon()); - if (result == 0) { - RunManagerEx runManager = RunManagerEx.getInstanceEx(project); - Module module = facet.getModule(); - RunnerAndConfigurationSettings settings = runManager - .createRunConfiguration(module.getName(), AndroidTestRunConfigurationType.getInstance().getFactory()); - AndroidTestRunConfiguration configuration = (AndroidTestRunConfiguration)settings.getConfiguration(); - configuration.setModule(module); - runManager.addConfiguration(settings, false); - runManager.setActiveConfiguration(settings); + RunManagerEx runManager = RunManagerEx.getInstanceEx(project); + Module module = facet.getModule(); + RunnerAndConfigurationSettings settings = runManager + .createRunConfiguration(module.getName(), AndroidTestRunConfigurationType.getInstance().getFactory()); + + AndroidTestRunConfiguration configuration = (AndroidTestRunConfiguration)settings.getConfiguration(); + configuration.setModule(module); + configuration.setTargetSelectionMode(mode); + if (preferredAvd != null) { + configuration.PREFERRED_AVD = preferredAvd; } + + runManager.addConfiguration(settings, false); + runManager.setActiveConfiguration(settings); } private boolean isHelloAndroid() { @@ -512,4 +518,12 @@ public class AndroidModuleBuilder extends JavaModuleBuilder { public void setTestedModule(Module module) { myTestedModule = module; } + + public void setTargetSelectionMode(TargetSelectionMode targetSelectionMode) { + myTargetSelectionMode = targetSelectionMode; + } + + public void setPreferredAvd(String preferredAvd) { + myPreferredAvd = preferredAvd; + } } diff --git a/plugins/android/src/org/jetbrains/android/newProject/AndroidModuleWizardStep.form b/plugins/android/src/org/jetbrains/android/newProject/AndroidModuleWizardStep.form index 6117d6f510b3..94223f6073f5 100644 --- a/plugins/android/src/org/jetbrains/android/newProject/AndroidModuleWizardStep.form +++ b/plugins/android/src/org/jetbrains/android/newProject/AndroidModuleWizardStep.form @@ -1,16 +1,16 @@
- + - + - + @@ -87,6 +87,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -95,5 +162,11 @@ + + + + + +
diff --git a/plugins/android/src/org/jetbrains/android/newProject/AndroidModuleWizardStep.java b/plugins/android/src/org/jetbrains/android/newProject/AndroidModuleWizardStep.java index fd38b445b94e..1537e12a8884 100644 --- a/plugins/android/src/org/jetbrains/android/newProject/AndroidModuleWizardStep.java +++ b/plugins/android/src/org/jetbrains/android/newProject/AndroidModuleWizardStep.java @@ -16,23 +16,37 @@ package org.jetbrains.android.newProject; +import com.android.prefs.AndroidLocation; +import com.android.sdklib.SdkManager; +import com.android.sdklib.internal.avd.AvdManager; import com.intellij.ide.util.PropertiesComponent; import com.intellij.ide.util.projectWizard.ModuleWizardStep; import com.intellij.ide.util.projectWizard.WizardContext; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.ProjectJdkTable; import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.ui.LabeledComponent; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Disposer; +import com.intellij.ui.ComboboxWithBrowseButton; +import com.intellij.util.Alarm; +import com.intellij.util.ArrayUtil; import com.intellij.util.ui.UIUtil; -import org.jetbrains.android.sdk.AndroidSdkType; -import org.jetbrains.android.sdk.AndroidSdkUtils; +import org.jetbrains.android.actions.RunAndroidSdkManagerAction; +import org.jetbrains.android.run.TargetSelectionMode; +import org.jetbrains.android.sdk.*; import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.Arrays; /** * Created by IntelliJ IDEA. @@ -42,6 +56,7 @@ import java.awt.event.ActionListener; * To change this template use File | Settings | File Templates. */ public class AndroidModuleWizardStep extends ModuleWizardStep { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.newProject.AndroidModuleWizardStep"); private final AndroidAppPropertiesEditor myAppPropertiesEditor; @@ -59,13 +74,23 @@ public class AndroidModuleWizardStep extends ModuleWizardStep { private AndroidSdkComboBoxWithBrowseButton mySdkComboBoxWithBrowseButton; private JCheckBox myCreateDefaultStructure; private JPanel myApplicationPanel; + private JRadioButton myDoNotCreateConfigurationRadioButton; + private JRadioButton myShowDeviceChooserRadioButton; + private JRadioButton myUSBDeviceRadioButton; + private JRadioButton myEmulatorRadioButton; + private LabeledComponent myAvdComboComponent; + private JPanel myDeploymentTargetPanel; - private final WizardContext myWizardContext; + private final ComboboxWithBrowseButton myAvdCombo; + private final Alarm myAvdsUpdatingAlarm = new Alarm(); + + private String[] myOldAvds = ArrayUtil.EMPTY_STRING_ARRAY; + + @NonNls private static final String TARGET_SELECTION_MODE_FOR_NEW_MODULE_PROPERTY = "ANDROID_TARGET_SELECTION_MODE_FOR_NEW_MODULE2"; + @NonNls private static final String TARGET_AVD_FOR_NEW_MODULE_PROPERTY = "ANDROID_TARGET_AVD_FOR_NEW_MODULE2"; public AndroidModuleWizardStep(@NotNull AndroidModuleBuilder moduleBuilder, WizardContext context) { super(); - - myWizardContext = context; myApplicationProjectButton.setSelected(true); myAppPropertiesEditor = new AndroidAppPropertiesEditor(moduleBuilder.getName()); @@ -87,6 +112,7 @@ public class AndroidModuleWizardStep extends ModuleWizardStep { @Override public void actionPerformed(ActionEvent e) { updatePropertiesEditor(); + updateDeploymentTargetPanel(); } }; myApplicationProjectButton.addActionListener(listener); @@ -102,8 +128,92 @@ public class AndroidModuleWizardStep extends ModuleWizardStep { if (enabled) { updatePropertiesEditor(); } + updateDeploymentTargetPanel(); } }); + + final ActionListener l = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + myAvdComboComponent.setEnabled(myEmulatorRadioButton.isSelected()); + } + }; + + myEmulatorRadioButton.addActionListener(l); + myDoNotCreateConfigurationRadioButton.addActionListener(l); + myShowDeviceChooserRadioButton.addActionListener(l); + myUSBDeviceRadioButton.addActionListener(l); + + myAvdCombo = myAvdComboComponent.getComponent(); + + myAvdCombo.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + final Sdk selectedSdk = mySdkComboBoxWithBrowseButton.getSelectedSdk(); + if (selectedSdk == null || !(selectedSdk.getSdkType() instanceof AndroidSdkType)) { + Messages.showErrorDialog(myPanel, AndroidBundle.message("specify.platform.error")); + return; + } + + final AndroidSdkAdditionalData sdkAdditionalData = (AndroidSdkAdditionalData)selectedSdk.getSdkAdditionalData(); + if (sdkAdditionalData == null) { + Messages.showErrorDialog(myPanel, AndroidBundle.message("android.wizard.invalid.sdk.error")); + return; + } + + final AndroidPlatform platform = sdkAdditionalData.getAndroidPlatform(); + if (platform == null) { + Messages.showErrorDialog(myPanel, AndroidBundle.message("cannot.parse.sdk.error")); + return; + } + + RunAndroidSdkManagerAction.runTool(platform.getSdk().getLocation()); + } + }); + + myAvdCombo.setMinimumSize(new Dimension(100, myAvdCombo.getMinimumSize().height)); + + final PropertiesComponent properties = PropertiesComponent.getInstance(); + final String targetSelectionModeStr = properties.getValue(TARGET_SELECTION_MODE_FOR_NEW_MODULE_PROPERTY); + + if (targetSelectionModeStr != null) { + if (targetSelectionModeStr.length() > 0) { + try { + final TargetSelectionMode targetSelectionMode = TargetSelectionMode.valueOf(targetSelectionModeStr); + switch (targetSelectionMode) { + case SHOW_DIALOG: + myShowDeviceChooserRadioButton.setSelected(true); + break; + case EMULATOR: + myEmulatorRadioButton.setSelected(true); + break; + case USB_DEVICE: + myUSBDeviceRadioButton.setSelected(true); + break; + default: + assert false : "Unknown target selection mode " + targetSelectionMode; + } + } + catch (IllegalArgumentException ignored) { + } + } + else { + myDoNotCreateConfigurationRadioButton.setSelected(true); + } + } + } + + @Override + public void disposeUIResources() { + Disposer.dispose(myAvdsUpdatingAlarm); + } + + private void updateDeploymentTargetPanel() { + final boolean enabled = myCreateDefaultStructure.isSelected() && + (myApplicationProjectButton.isSelected() || myTestProjectButton.isSelected()); + UIUtil.setEnabled(myDeploymentTargetPanel, enabled, true); + if (enabled) { + myAvdComboComponent.setEnabled(myEmulatorRadioButton.isSelected()); + } } private void updatePropertiesEditor() { @@ -133,8 +243,10 @@ public class AndroidModuleWizardStep extends ModuleWizardStep { myAppPropertiesEditor.getApplicationNameField().setText(myModuleBuilder.getName()); Sdk selectedSdk = mySdkComboBoxWithBrowseButton.getSelectedSdk(); + final PropertiesComponent properties = PropertiesComponent.getInstance(); + if (selectedSdk == null) { - String defaultPlatformName = PropertiesComponent.getInstance().getValue(AndroidSdkUtils.DEFAULT_PLATFORM_NAME_PROPERTY); + String defaultPlatformName = properties.getValue(AndroidSdkUtils.DEFAULT_PLATFORM_NAME_PROPERTY); if (defaultPlatformName != null) { Sdk sdk = ProjectJdkTable.getInstance().findJdk(defaultPlatformName); if (sdk != null && sdk.getSdkType().equals(AndroidSdkType.getInstance())) { @@ -144,6 +256,16 @@ public class AndroidModuleWizardStep extends ModuleWizardStep { } mySdkComboBoxWithBrowseButton.rebuildSdksListAndSelectSdk(selectedSdk); + boolean shouldReset = myAvdCombo.getComboBox().getSelectedItem() == null; + startUpdatingAvds(); + if (shouldReset) { + final String targetAvd = properties.getValue(TARGET_AVD_FOR_NEW_MODULE_PROPERTY); + if (targetAvd != null && targetAvd.length() > 0) { + myAvdCombo.getComboBox().setSelectedItem(targetAvd); + } + } + + updateDeploymentTargetPanel(); return myPanel; } @@ -172,7 +294,9 @@ public class AndroidModuleWizardStep extends ModuleWizardStep { Sdk selectedSdk = mySdkComboBoxWithBrowseButton.getSelectedSdk(); assert selectedSdk != null; - PropertiesComponent.getInstance().setValue(AndroidSdkUtils.DEFAULT_PLATFORM_NAME_PROPERTY, selectedSdk.getName()); + final PropertiesComponent properties = PropertiesComponent.getInstance(); + + properties.setValue(AndroidSdkUtils.DEFAULT_PLATFORM_NAME_PROPERTY, selectedSdk.getName()); myModuleBuilder.setSdk(selectedSdk); if (!myCreateDefaultStructure.isSelected()) { @@ -190,10 +314,98 @@ public class AndroidModuleWizardStep extends ModuleWizardStep { assert myTestPropertiesEditor != null; myModuleBuilder.setTestedModule(myTestPropertiesEditor.getModule()); } + + if (myApplicationProjectButton.isSelected() || myTestProjectButton.isSelected()) { + String preferredAvdName = null; + TargetSelectionMode targetSelectionMode = null; + + if (myEmulatorRadioButton.isSelected()) { + preferredAvdName = (String)myAvdCombo.getComboBox().getSelectedItem(); + targetSelectionMode = TargetSelectionMode.EMULATOR; + } + else if (myShowDeviceChooserRadioButton.isSelected()) { + targetSelectionMode = TargetSelectionMode.SHOW_DIALOG; + } + else if (myUSBDeviceRadioButton.isSelected()) { + targetSelectionMode = TargetSelectionMode.USB_DEVICE; + } + + myModuleBuilder.setTargetSelectionMode(targetSelectionMode); + myModuleBuilder.setPreferredAvd(preferredAvdName); + + properties.setValue(TARGET_SELECTION_MODE_FOR_NEW_MODULE_PROPERTY, targetSelectionMode != null ? targetSelectionMode.name() : ""); + properties.setValue(TARGET_AVD_FOR_NEW_MODULE_PROPERTY, preferredAvdName != null ? preferredAvdName : ""); + } } @Override public String getHelpId() { return "reference.dialogs.new.project.fromScratch.android"; } + + private void startUpdatingAvds() { + if (!myAvdCombo.getComboBox().isPopupVisible()) { + doUpdateAvds(); + } + addUpdatingRequest(); + } + + private void addUpdatingRequest() { + if (myAvdsUpdatingAlarm.isDisposed()) { + return; + } + myAvdsUpdatingAlarm.cancelAllRequests(); + myAvdsUpdatingAlarm.addRequest(new Runnable() { + @Override + public void run() { + startUpdatingAvds(); + } + }, 500); + } + + private void doUpdateAvds() { + final Sdk selectedSdk = mySdkComboBoxWithBrowseButton.getSelectedSdk(); + + String[] newAvds = ArrayUtil.EMPTY_STRING_ARRAY; + + if (selectedSdk != null && selectedSdk.getSdkType() instanceof AndroidSdkType) { + final AndroidSdkAdditionalData sdkAdditionalData = (AndroidSdkAdditionalData)selectedSdk.getSdkAdditionalData(); + if (sdkAdditionalData != null) { + final AndroidPlatform androidPlatform = sdkAdditionalData.getAndroidPlatform(); + if (androidPlatform != null) { + newAvds = getAvds(androidPlatform); + } + } + } + + if (!Arrays.equals(myOldAvds, newAvds)) { + myOldAvds = newAvds; + final JComboBox combo = myAvdCombo.getComboBox(); + final Object selected = combo.getSelectedItem(); + combo.setModel(new DefaultComboBoxModel(newAvds)); + combo.setSelectedItem(selected); + } + } + + @NotNull + private static String[] getAvds(@NotNull AndroidPlatform androidPlatform) { + final AndroidSdk sdkObject = androidPlatform.getSdk(); + if (sdkObject instanceof AndroidSdkImpl) { + final SdkManager sdkManager = ((AndroidSdkImpl)sdkObject).getSdkManager(); + try { + final AvdManager avdManager = new AvdManager(sdkManager, new MessageBuildingSdkLog()); + final AvdManager.AvdInfo[] validAvds = avdManager.getValidAvds(); + + final String[] avdNames = new String[validAvds.length]; + for (int i = 0; i < validAvds.length; i++) { + avdNames[i] = validAvds[i].getName(); + } + return avdNames; + } + catch (AndroidLocation.AndroidLocationException e) { + LOG.info(e); + } + } + return ArrayUtil.EMPTY_STRING_ARRAY; + } } diff --git a/plugins/android/src/org/jetbrains/android/run/AndroidRunConfigurationEditor.form b/plugins/android/src/org/jetbrains/android/run/AndroidRunConfigurationEditor.form index c442f19b6ba7..3f54177154cf 100644 --- a/plugins/android/src/org/jetbrains/android/run/AndroidRunConfigurationEditor.form +++ b/plugins/android/src/org/jetbrains/android/run/AndroidRunConfigurationEditor.form @@ -71,14 +71,14 @@ - + - + @@ -86,7 +86,7 @@ - + @@ -94,7 +94,7 @@ - + @@ -105,7 +105,7 @@ - + diff --git a/plugins/android/src/org/jetbrains/android/run/AvdComboBox.java b/plugins/android/src/org/jetbrains/android/run/AvdComboBox.java index 5f28f68eeb61..7d4153f3f7b3 100644 --- a/plugins/android/src/org/jetbrains/android/run/AvdComboBox.java +++ b/plugins/android/src/org/jetbrains/android/run/AvdComboBox.java @@ -58,7 +58,7 @@ public abstract class AvdComboBox extends ComboboxWithBrowseButton { return; } - RunAndroidSdkManagerAction.runTool(module.getProject(), platform.getSdk().getLocation()); + RunAndroidSdkManagerAction.runTool(platform.getSdk().getLocation()); } }); diff --git a/plugins/android/src/org/jetbrains/android/util/AndroidUtils.java b/plugins/android/src/org/jetbrains/android/util/AndroidUtils.java index 5a10ea246d66..6d1a688e48f3 100644 --- a/plugins/android/src/org/jetbrains/android/util/AndroidUtils.java +++ b/plugins/android/src/org/jetbrains/android/util/AndroidUtils.java @@ -77,8 +77,7 @@ import org.jetbrains.android.dom.manifest.IntentFilter; import org.jetbrains.android.dom.manifest.Manifest; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.facet.AndroidFacetConfiguration; -import org.jetbrains.android.run.AndroidRunConfiguration; -import org.jetbrains.android.run.AndroidRunConfigurationType; +import org.jetbrains.android.run.*; import org.jetbrains.android.sdk.AndroidSdk; import org.jetbrains.android.sdk.AndroidSdkAdditionalData; import org.jetbrains.android.sdk.AndroidSdkType; @@ -305,7 +304,9 @@ public class AndroidUtils { public static void addRunConfiguration(final Project project, final AndroidFacet facet, @Nullable final String activityClass, - boolean ask) { + final boolean ask, + @Nullable final TargetSelectionMode targetSelectionMode, + @Nullable final String preferredAvdName) { final Runnable r = new Runnable() { public void run() { RunManagerEx runManager = RunManagerEx.getInstanceEx(project); @@ -321,6 +322,14 @@ public class AndroidUtils { else { configuration.MODE = AndroidRunConfiguration.LAUNCH_DEFAULT_ACTIVITY; } + + if (targetSelectionMode != null) { + configuration.setTargetSelectionMode(targetSelectionMode); + } + if (preferredAvdName != null) { + configuration.PREFERRED_AVD = preferredAvdName; + } + runManager.addConfiguration(settings, false); runManager.setActiveConfiguration(settings); } @@ -452,12 +461,12 @@ public class AndroidUtils { return exitCode == 0; } - public static void runExternalToolInSeparateThread(@NotNull final Project project, + public static void runExternalToolInSeparateThread(@Nullable final Project project, @NotNull final GeneralCommandLine commandLine) { runExternalToolInSeparateThread(project, commandLine, null); } - public static void runExternalToolInSeparateThread(@NotNull final Project project, + public static void runExternalToolInSeparateThread(@Nullable final Project project, @NotNull final GeneralCommandLine commandLine, @Nullable final ProcessHandler processHandler) { ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {