diff --git a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java index 954da1715..925f321a8 100644 --- a/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java +++ b/tools/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java @@ -110,7 +110,7 @@ public final class AvdManager { /** * Pattern to match pixel-sized skin "names", e.g. "320x480". */ - public final static Pattern NUMERIC_SKIN_SIZE = Pattern.compile("[0-9]{2,}x[0-9]{2,}"); //$NON-NLS-1$ + public final static Pattern NUMERIC_SKIN_SIZE = Pattern.compile("([0-9]{2,})x([0-9]{2,})"); //$NON-NLS-1$ private final static String USERDATA_IMG = "userdata.img"; //$NON-NLS-1$ private final static String CONFIG_INI = "config.ini"; //$NON-NLS-1$ diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AvdManagerPage.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AvdManagerPage.java index 5716edf3e..6cd099666 100755 --- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AvdManagerPage.java +++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/AvdManagerPage.java @@ -56,6 +56,7 @@ public class AvdManagerPage extends Composite implements ISdkListener { mUpdaterData.getOsSdkRoot(), mUpdaterData.getAvdManager(), DisplayMode.MANAGER); + mAvdSelector.setSettingsController(mUpdaterData.getSettingsController()); } @Override diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ISettingsPage.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ISettingsPage.java index 0d7179ce6..739c479f8 100755 --- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ISettingsPage.java +++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/ISettingsPage.java @@ -52,6 +52,12 @@ public interface ISettingsPage { * Default: True. */ public static final String KEY_ASK_ADB_RESTART = "sdkman.ask.adb.restart"; //$NON-NLS-1$ + /** + * Setting to set the density of the monitor. + * Type: Integer. + * Default: -1 + */ + public static final String KEY_MONITOR_DENSITY = "sdkman.monitor.density"; //$NON-NLS-1$ /** Loads settings from the given {@link Properties} container and update the page UI. */ public abstract void loadSettings(Properties in_settings); diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SettingsController.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SettingsController.java index a4b1a05de..e12611ca1 100755 --- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SettingsController.java +++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/repository/SettingsController.java @@ -46,7 +46,7 @@ public class SettingsController { //--- Access to settings ------------ /** - * Returns the value of the ISettingsPage#KEY_FORCE_HTTP setting. + * Returns the value of the {@link ISettingsPage#KEY_FORCE_HTTP} setting. * @see ISettingsPage#KEY_FORCE_HTTP */ public boolean getForceHttp() { @@ -54,7 +54,7 @@ public class SettingsController { } /** - * Returns the value of the ISettingsPage#KEY_ASK_ADB_RESTART setting. + * Returns the value of the {@link ISettingsPage#KEY_ASK_ADB_RESTART} setting. * @see ISettingsPage#KEY_ASK_ADB_RESTART */ public boolean getAskBeforeAdbRestart() { @@ -66,7 +66,7 @@ public class SettingsController { } /** - * Returns the value of the ISettingsPage#KEY_SHOW_UPDATE_ONLY setting. + * Returns the value of the {@link ISettingsPage#KEY_SHOW_UPDATE_ONLY} setting. * @see ISettingsPage#KEY_SHOW_UPDATE_ONLY */ public boolean getShowUpdateOnly() { @@ -78,7 +78,7 @@ public class SettingsController { } /** - * Sets the value of the ISettingsPage#KEY_SHOW_UPDATE_ONLY setting. + * Sets the value of the {@link ISettingsPage#KEY_SHOW_UPDATE_ONLY} setting. * @param enabled True if only compatible update items should be shown. * @see ISettingsPage#KEY_SHOW_UPDATE_ONLY */ @@ -86,6 +86,32 @@ public class SettingsController { setSetting(ISettingsPage.KEY_SHOW_UPDATE_ONLY, enabled); } + /** + * Returns the value of the {@link ISettingsPage#KEY_MONITOR_DENSITY} setting + * @see ISettingsPage#KEY_MONITOR_DENSITY + */ + public int getMonitorDensity() { + String value = mProperties.getProperty(ISettingsPage.KEY_MONITOR_DENSITY, null); + if (value == null) { + return -1; + } + + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + return -1; + } + } + + /** + * Sets the value of the {@link ISettingsPage#KEY_MONITOR_DENSITY} setting. + * @param density the density of the monitor + * @see ISettingsPage#KEY_MONITOR_DENSITY + */ + public void setMonitorDensity(int density) { + mProperties.setProperty(ISettingsPage.KEY_MONITOR_DENSITY, Integer.toString(density)); + } + /** * Internal helper to set a boolean setting. */ diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java index ba1bb4c0c..533871780 100644 --- a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java +++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdSelector.java @@ -26,6 +26,7 @@ import com.android.sdklib.internal.avd.AvdManager.AvdInfo; import com.android.sdklib.internal.avd.AvdManager.AvdInfo.AvdStatus; import com.android.sdklib.internal.repository.ITask; import com.android.sdklib.internal.repository.ITaskMonitor; +import com.android.sdkuilib.internal.repository.SettingsController; import com.android.sdkuilib.internal.repository.icons.ImageFactory; import com.android.sdkuilib.internal.tasks.ProgressTask; import com.android.sdkuilib.repository.UpdaterWindow; @@ -95,6 +96,8 @@ public final class AvdSelector { private Image mOkImage; private Image mBrokenImage; + private SettingsController mController; + /** * The display mode of the AVD Selector. @@ -380,6 +383,14 @@ public final class AvdSelector { DisplayMode displayMode) { this(parent, osSdkPath, manager, new TargetBasedFilter(filter), displayMode); } + + /** + * Sets an optional SettingsController. + * @param controller the controller. + */ + public void setSettingsController(SettingsController controller) { + mController = controller; + } /** * Sets the table grid layout data. * @@ -898,52 +909,70 @@ public final class AvdSelector { return; } - String path = mOsSdkPath + - File.separator + - SdkConstants.OS_SDK_TOOLS_FOLDER + - SdkConstants.FN_EMULATOR; + AvdStartDialog dialog = new AvdStartDialog(mTable.getShell(), avdInfo, mOsSdkPath, + mController); + if (dialog.open() == Window.OK) { + String path = mOsSdkPath + + File.separator + + SdkConstants.OS_SDK_TOOLS_FOLDER + + SdkConstants.FN_EMULATOR; - final String avdName = avdInfo.getName(); + final String avdName = avdInfo.getName(); - // build the command line based on the available parameters. - ArrayList list = new ArrayList(); - list.add(path); - list.add("-avd"); //$NON-NLS-1$ - list.add(avdName); + // build the command line based on the available parameters. + ArrayList list = new ArrayList(); + list.add(path); + list.add("-avd"); //$NON-NLS-1$ + list.add(avdName); + if (dialog.getWipeData()) { + list.add("-wipe-data"); //$NON-NLS-1$ + } + float scale = dialog.getScale(); + if (scale != 0.f) { + // do the rounding ourselves. This is because %.1f will write .4899 as .4 + scale = Math.round(scale * 100); + scale /= 100.f; + list.add("-scale"); //$NON-NLS-1$ + list.add(String.format("%.2f", scale)); //$NON-NLS-1$ + } - // convert the list into an array for the call to exec. - final String[] command = list.toArray(new String[list.size()]); + // convert the list into an array for the call to exec. + final String[] command = list.toArray(new String[list.size()]); - // launch the emulator - new ProgressTask(mTable.getShell(), - "Starting Android Emulator", - new ITask() { - public void run(ITaskMonitor monitor) { - try { - monitor.setDescription("Starting emulator for AVD '%1$s'", avdName); - int n = 10; - monitor.setProgressMax(n); - Process process = Runtime.getRuntime().exec(command); - grabEmulatorOutput(process, monitor); + // launch the emulator + new ProgressTask(mTable.getShell(), + "Starting Android Emulator", + new ITask() { + public void run(ITaskMonitor monitor) { + try { + monitor.setDescription("Starting emulator for AVD '%1$s'", + avdName); + int n = 10; + monitor.setProgressMax(n); + Process process = Runtime.getRuntime().exec(command); + grabEmulatorOutput(process, monitor); - // This small wait prevents the dialog from closing too fast: - // When it works, the emulator returns immediately, even if no UI - // is shown yet. And when it fails (because the AVD is locked/running) - // if we don't have a wait we don't capture the error for some reason. - for (int i = 0; i < n; i++) { - try { - Thread.sleep(100); - monitor.incProgress(1); - } catch (InterruptedException e) { - // ignore + // This small wait prevents the dialog from closing too fast: + // When it works, the emulator returns immediately, even if + // no UI is shown yet. And when it fails (because the AVD is + // locked/running) + // if we don't have a wait we don't capture the error for + // some reason. + for (int i = 0; i < n; i++) { + try { + Thread.sleep(100); + monitor.incProgress(1); + } catch (InterruptedException e) { + // ignore + } } + } catch (IOException e) { + monitor.setResult("Failed to start emulator: %1$s", + e.getMessage()); } - } catch (IOException e) { - monitor.setResult("Failed to start emulator: %1$s", e.getMessage()); } - } - }); - + }); + } } /** diff --git a/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java new file mode 100644 index 000000000..227f23e4a --- /dev/null +++ b/tools/sdkmanager/libs/sdkuilib/src/com/android/sdkuilib/internal/widgets/AvdStartDialog.java @@ -0,0 +1,480 @@ +/* + * 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 com.android.sdklib.internal.avd.AvdManager; +import com.android.sdklib.internal.avd.AvdManager.AvdInfo; +import com.android.sdkuilib.internal.repository.SettingsController; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.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.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +import java.awt.Toolkit; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Dialog dealing with emulator launch options. The following options are supported: + *
    + *
  • -wipe-data
  • + *
  • -scale
  • + *
+ * + * Values are stored (in the class as static field) to be reused while the app is still running. + * The Monitor dpi is stored in the settings if availabe. + */ +final class AvdStartDialog extends Dialog { + // static field to reuse values during the same session. + private static boolean sWipeData = false; + private static int sMonitorDpi = 72; // used if there's no setting controller. + private static final Map sSkinScaling = new HashMap(); + + private static final Pattern sScreenSizePattern = Pattern.compile("\\d*(\\.\\d?)?"); + + private final AvdInfo mAvd; + private final String mSdkLocation; + private final SettingsController mSettingsController; + + private Text mScreenSize; + private Text mMonitorDpi; + private Button mScaleButton; + + private float mScale = 0.f; + private boolean mWipeData = false; + private int mDensity = 160; // medium density + private int mSize1 = -1; + private int mSize2 = -1; + private String mSkinDisplay; + private boolean mEnableScaling = true; + + protected AvdStartDialog(Shell parentShell, AvdInfo avd, String sdkLocation, + SettingsController settingsController) { + super(parentShell); + mAvd = avd; + mSdkLocation = sdkLocation; + mSettingsController = settingsController; + if (mAvd == null) { + throw new IllegalArgumentException("avd cannot be null"); + } + if (mSdkLocation == null) { + throw new IllegalArgumentException("sdkLocation cannot be null"); + } + + computeSkinData(); + } + + public boolean getWipeData() { + return mWipeData; + } + + /** + * Returns the scaling factor, or 0.f if none are set. + */ + public float getScale() { + return mScale; + } + + @Override + protected Control createDialogArea(Composite parent) { + GridData gd; + + // create a composite with standard margins and spacing + Composite composite = new Composite(parent, SWT.NONE); + GridLayout 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.setLayout(layout); + composite.setLayoutData(new GridData(GridData.FILL_BOTH)); + + Label l = new Label(composite, SWT.NONE); + l.setText("Skin:"); + + l = new Label(composite, SWT.NONE); + l.setText(mSkinDisplay); + l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + l = new Label(composite, SWT.NONE); + l.setText("Density:"); + + l = new Label(composite, SWT.NONE); + l.setText(getDensityText()); + l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + mScaleButton = new Button(composite, SWT.CHECK); + mScaleButton.setText("Scale display to real size"); + mScaleButton.setEnabled(mEnableScaling); + boolean defaultState = mEnableScaling && sSkinScaling.get(mAvd.getName()) != null; + mScaleButton.setSelection(defaultState); + mScaleButton.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + gd.horizontalSpan = 2; + final Group scaleGroup = new Group(composite, SWT.NONE); + scaleGroup.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + gd.horizontalIndent = 30; + gd.horizontalSpan = 2; + scaleGroup.setLayout(new GridLayout(2, false)); + + l = new Label(scaleGroup, SWT.NONE); + l.setText("Screen Size (in):"); + mScreenSize = new Text(scaleGroup, SWT.BORDER); + mScreenSize.setText(getScreenSize()); + mScreenSize.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + mScreenSize.addVerifyListener(new VerifyListener() { + public void verifyText(VerifyEvent event) { + // combine the current content and the new text + String text = mScreenSize.getText(); + text = text.substring(0, event.start) + event.text + text.substring(event.end); + + // now make sure it's a match for the regex + event.doit = sScreenSizePattern.matcher(text).matches(); + } + }); + mScreenSize.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent event) { + onScaleChange(); + } + }); + + l = new Label(scaleGroup, SWT.NONE); + l.setText("Monitor dpi:"); + mMonitorDpi = new Text(scaleGroup, SWT.BORDER); + mMonitorDpi.setText(Integer.toString(getMonitorDpi())); + mMonitorDpi.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + mMonitorDpi.addVerifyListener(new VerifyListener() { + public void verifyText(VerifyEvent event) { + // check for digit only. + for (int i = 0 ; i < event.text.length(); i++) { + char letter = event.text.charAt(i); + if (letter < '0' || letter > '9') { + event.doit = false; + return; + } + } + } + }); + mMonitorDpi.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent event) { + onScaleChange(); + } + }); + + scaleGroup.setEnabled(defaultState); + + mScaleButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent event) { + boolean enabled = mScaleButton.getSelection(); + scaleGroup.setEnabled(enabled); + if (enabled) { + onScaleChange(); + } else { + mScale = 0.f; + } + } + }); + + final Button wipeButton = new Button(composite, SWT.CHECK); + wipeButton.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + gd.horizontalSpan = 2; + wipeButton.setText("Wipe user data"); + wipeButton.setSelection(mWipeData = sWipeData); + wipeButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent arg0) { + mWipeData = wipeButton.getSelection(); + } + }); + + l = new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL); + l.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL)); + gd.horizontalSpan = 2; + + applyDialogFont(composite); + + // if the scaling is enabled by default, we must initialize the value of mScale + if (defaultState) { + onScaleChange(); + } + + return composite; + } + + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + newShell.setText("Launch Options"); + } + + @Override + protected Button createButton(Composite parent, int id, String label, boolean defaultButton) { + if (id == IDialogConstants.OK_ID) { + label = "Launch"; + } + + return super.createButton(parent, id, label, defaultButton); + } + + @Override + protected void okPressed() { + // override ok to store some info + // first the monitor dpi + String dpi = mMonitorDpi.getText(); + if (dpi.length() > 0) { + sMonitorDpi = Integer.parseInt(dpi); + + // if there is a setting controller, save it + if (mSettingsController != null) { + mSettingsController.setMonitorDensity(sMonitorDpi); + mSettingsController.saveSettings(); + } + } + + // now the scale factor + String key = mAvd.getName(); + sSkinScaling.remove(key); + if (mScaleButton.getSelection()) { + String size = mScreenSize.getText(); + if (size.length() > 0) { + sSkinScaling.put(key, size); + } + } + + // and then the wipe-data checkbox + sWipeData = mWipeData; + + // finally continue with the ok action + super.okPressed(); + } + + private void computeSkinData() { + Map prop = mAvd.getProperties(); + String dpi = prop.get("hw.lcd.density"); + if (dpi != null && dpi.length() > 0) { + mDensity = Integer.parseInt(dpi); + } + + findSkinResolution(); + } + + private void onScaleChange() { + String sizeStr = mScreenSize.getText(); + if (sizeStr.length() == 0) { + mScale = 0.f; + return; + } + + String dpiStr = mMonitorDpi.getText(); + if (dpiStr.length() == 0) { + mScale = 0.f; + return; + } + + int dpi = Integer.parseInt(dpiStr); + float size = Float.parseFloat(sizeStr); + /* + * We are trying to emulate the following device: + * resolution: 'mSize1'x'mSize2' + * density: 'mDensity' + * screen diagonal: 'size' + * ontop a monitor running at 'dpi' + */ + // We start by computing the screen diagonal in pixels, if the density was really mDensity + float diagonalPx = (float)Math.sqrt(mSize1*mSize1+mSize2*mSize2); + // Now we would convert this in actual inches: + // diagonalIn = diagonal / mDensity + // the scale factor is a mix of adapting to the new density and to the new size. + // (size/diagonalIn) * (dpi/mDensity) + // this can be simplified to: + mScale = (size * dpi) / diagonalPx; + } + + /** + * Returns the monitor dpi to start with. + * This can be coming from the settings, the session-based storage, or the from whatever Java + * can tell us. + */ + private int getMonitorDpi() { + if (mSettingsController != null) { + sMonitorDpi = mSettingsController.getMonitorDensity(); + } + + if (sMonitorDpi == -1) { // first time? try to get a value + sMonitorDpi = Toolkit.getDefaultToolkit().getScreenResolution(); + } + + return sMonitorDpi; + } + + /** + * Returns the screen size to start with. + *

If an emulator with the same skin was already launched, scaled, the size used is reused. + *

Otherwise the default is returned (3) + */ + private String getScreenSize() { + String size = sSkinScaling.get(mAvd.getName()); + if (size != null) { + return size; + } + + return "3"; + } + + /** + * Returns a display string for the density. + */ + private String getDensityText() { + switch (mDensity) { + case 120: + return "Low (120)"; + case 160: + return "Medium (160)"; + case 240: + return "High (240)"; + } + + return Integer.toString(mDensity); + } + + /** + * Finds the skin resolution and sets it in {@link #mSize1} and {@link #mSize2}. + */ + private void findSkinResolution() { + Map prop = mAvd.getProperties(); + String skinName = prop.get(AvdManager.AVD_INI_SKIN_NAME); + + Matcher m = AvdManager.NUMERIC_SKIN_SIZE.matcher(skinName); + if (m.matches()) { + mSize1 = Integer.parseInt(m.group(1)); + mSize2 = Integer.parseInt(m.group(2)); + mSkinDisplay = skinName; + mEnableScaling = true; + } else { + // The resolution is inside the layout file of the skin. + mEnableScaling = false; // default to false for now. + + // path to the skin layout file. + File skinFolder = new File(mSdkLocation, prop.get(AvdManager.AVD_INI_SKIN_PATH)); + if (skinFolder.isDirectory()) { + File layoutFile = new File(skinFolder, "layout"); + if (layoutFile.isFile()) { + if (parseLayoutFile(layoutFile)) { + mSkinDisplay = String.format("%1$s (%2$dx%3$d)", skinName, mSize1, mSize2); + mEnableScaling = true; + } else { + mSkinDisplay = skinName; + } + } + } + } + } + + /** + * Parses a layout file. + *

+ * the format is relatively easy. It's a collection of items defined as + * ≶name> { + * ≶content> + * } + * + * content is either 1+ items or 1+ properties + * properties are defined as + * ≶name>≶whitespace>≶value> + * + * We're going to look for an item called display, with 2 properties height and width. + * This is very basic parser. + * + * @param layoutFile the file to parse + * @return true if both sizes where found. + */ + private boolean parseLayoutFile(File layoutFile) { + try { + BufferedReader input = new BufferedReader(new FileReader(layoutFile)); + String line; + + while ((line = input.readLine()) != null) { + // trim to remove whitespace + line = line.trim(); + int len = line.length(); + if (len == 0) continue; + + // check if this is a new item + if (line.charAt(len-1) == '{') { + // this is the start of a node + String[] tokens = line.split(" "); + if ("display".equals(tokens[0])) { + // this is the one we're looking for! + while ((mSize1 == -1 || mSize2 == -1) && + (line = input.readLine()) != null) { + // trim to remove whitespace + line = line.trim(); + len = line.length(); + if (len == 0) continue; + + if ("}".equals(line)) { // looks like we're done with the item. + break; + } + + tokens = line.split(" "); + if (tokens.length >= 2) { + // there can be multiple space between the name and value + // in which case we'll get an extra empty token in the middle. + if ("width".equals(tokens[0])) { + mSize1 = Integer.parseInt(tokens[tokens.length-1]); + } else if ("height".equals(tokens[0])) { + mSize2 = Integer.parseInt(tokens[tokens.length-1]); + } + } + } + + return mSize1 != -1 && mSize2 != -1; + } + } + + } + // if it reaches here, display was not found. + // false is returned below. + } catch (IOException e) { + // ignore. + } + + return false; + } +}