/* * Copyright (C) 2014 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.tools.idea.avdmanager; import com.android.sdklib.devices.Storage; import com.android.tools.idea.wizard.ScopedStateStore; import com.google.common.base.CharMatcher; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.intellij.ui.JBColor; import com.intellij.util.ui.GraphicsUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.text.DecimalFormat; import java.util.Map; import static com.android.tools.idea.avdmanager.AvdWizardConstants.*; import static com.android.tools.idea.wizard.ScopedStateStore.Key; /** * A help panel that displays help text and error messaging for AVD options. */ public class AvdConfigurationOptionHelpPanel extends JPanel { private static final String NO_OPTION_SELECTED = "Nothing Selected"; private static final int PADDING = 20; private String myErrorMessage; private String myTitle; private String myDescriptionBody; public void setDescriptionText(@Nullable String descriptionText) { if (descriptionText == null || descriptionText.isEmpty()) { myTitle = null; myDescriptionBody = null; return; } Iterable<String> iterable = Splitter.on('|').split(descriptionText); myTitle = Iterables.getFirst(iterable, null); myDescriptionBody = Iterables.getLast(iterable, null); repaint(); } @Override protected void paintComponent(Graphics g) { GraphicsUtil.setupAntialiasing(g); GraphicsUtil.setupAAPainting(g); super.paintComponent(g); Graphics2D g2d = (Graphics2D)g; g2d.setColor(JBColor.background()); g2d.fillRect(0, 0, getWidth(), getHeight()); g2d.setColor(JBColor.foreground()); g2d.setFont(STANDARD_FONT); if (myTitle == null) { FontMetrics metrics = g2d.getFontMetrics(); g2d.drawString(NO_OPTION_SELECTED, (getWidth() - metrics.stringWidth(NO_OPTION_SELECTED)) / 2, (getHeight() - metrics.getHeight()) / 2 ); return; } // Paint the name g2d.setFont(TITLE_FONT); FontMetrics metrics = g.getFontMetrics(TITLE_FONT); g2d.drawString(myTitle, PADDING, PADDING + metrics.getHeight() / 2); g2d.drawLine(0, 50, getWidth(), 50); // Paint the details. int stringHeight = g2d.getFontMetrics(TITLE_FONT).getHeight(); int infoSegmentX = PADDING; int infoSegmentY = PADDING + stringHeight * 2; g2d.setFont(STANDARD_FONT); int maxWidth = getWidth() - 2 * PADDING; // If there's an error message, paint it if (myErrorMessage != null) { g2d.setColor(JBColor.RED); infoSegmentY += drawMultilineString(g2d, myErrorMessage, maxWidth, infoSegmentX, infoSegmentY); } infoSegmentY += stringHeight; // Paint our description if (myDescriptionBody != null) { g2d.setColor(JBColor.foreground()); for (String line : Splitter.on(CharMatcher.anyOf("\n\r")).omitEmptyStrings().split(myDescriptionBody)) { infoSegmentY += drawMultilineString(g2d, line, maxWidth, infoSegmentX, infoSegmentY); } } } /** * Returns the height of the text drawn. */ private static int drawMultilineString(@NotNull Graphics2D g2d, @NotNull String fullString, int maxWidth, int startX, int startY) { int currentY = startY; FontMetrics metrics = g2d.getFontMetrics(); int stringHeight = metrics.getHeight(); Iterable<String> parts = Splitter.on(CharMatcher.WHITESPACE).omitEmptyStrings().split(fullString); String currentLine = ""; for (String part : parts) { if (metrics.stringWidth(currentLine + part) > maxWidth) { currentY += stringHeight; g2d.drawString(currentLine, startX, currentY); currentLine = ""; } currentLine += part + " "; } // Flush the remaining buffer if (!currentLine.isEmpty()) { currentY += stringHeight; g2d.drawString(currentLine, startX, currentY); } return currentY - startY; } public void setErrorMessage(@NotNull String message) { myErrorMessage = message; repaint(); } private static Map<Key<?>, String> TITLES = ImmutableMap.<Key<?>, String>builder(). put(RAM_STORAGE_KEY, "Device RAM"). put(VM_HEAP_STORAGE_KEY, "Virtual Machine Heap"). put(INTERNAL_STORAGE_KEY, "Internal Flash"). put(SD_CARD_STORAGE_KEY, "New SD Card Size"). put(EXISTING_SD_LOCATION, "Location of SD card image"). put(SCALE_SELECTION_KEY, "Start-Up Size"). put(DEFAULT_ORIENTATION_KEY, "Default Orientation"). put(NETWORK_SPEED_KEY, "Network Speed"). put(NETWORK_LATENCY_KEY, "Network Latency"). put(FRONT_CAMERA_KEY, "Front Camera"). put(BACK_CAMERA_KEY, "Back Camera"). put(USE_HOST_GPU_KEY, "Use Host GPU"). put(USE_SNAPSHOT_KEY, "Enable Snapshot"). put(CUSTOM_SKIN_FILE_KEY, "Custom Hardware Skin"). build(); private static Map<Key<?>, String> DESCRIPTIONS = ImmutableMap.<Key<?>, String>builder(). put(RAM_STORAGE_KEY, "The amount of physical RAM on the device.\n" + "1 MB = 1024 KB\n" + "1 GB = 1025 MB"). put(VM_HEAP_STORAGE_KEY, "The amount of RAM available to Java virtual machine (VM) to allocate to running apps on the device. " + "A larger VM heap allows application to run longer between garbage collection event."). put(INTERNAL_STORAGE_KEY, "The amount of non-removable space available to store data on the device."). put(SD_CARD_STORAGE_KEY, "The amount of removable space available to store data on the device. "). put(EXISTING_SD_LOCATION, "Choose a file path to an existing SD Card image. Using an external SD Card is useful when sharing " + "SD Card data (pictures, media, files, etc.) between Android Virtual Devices. "). put(SCALE_SELECTION_KEY, "Enables you to test your application on a screen that uses a resolution or density not supported by the " + "built-in AVD skins, you can create an AVD that uses a custom resolution by selecting one of the scale values."). put(DEFAULT_ORIENTATION_KEY, "Sets the initial orientation of the device. During AVD emulation you can also rotate the device screen. "). put(NETWORK_SPEED_KEY, "Sets the initial state of the simulated network transfer rate used by AVD. " + "The network speed can also be adjusted in the emulator."). put(NETWORK_LATENCY_KEY, "Sets the initial state of the simulated network transfer latency used by AVD. " + " Latency is the delay in processing data across the network." + " The latency speed can also be adjusted in the emulator."). put(FRONT_CAMERA_KEY, "None - no camera installed for AVD\n" + "Emulated - use a simulated camera\n" + "Device - use host computer webcam or built-in camera"). put(BACK_CAMERA_KEY, "None - no camera installed for AVD\n" + "Emulated - use a simulated camera\n" + "Device - use host computer webcam or built-in camera"). put(USE_HOST_GPU_KEY, "This enables the emulator graphics to run faster by using your computer's graphics card for " + "OpenGL ES graphics rendering. (Recommended for better emulator experience)"). put(USE_SNAPSHOT_KEY, "Helps improve emulator re-start performance. Start the AVD from the AVD manager and check" + " Launch from snapshot and Save to snapshot. This way, when you close the emulator, a snapshot" + " of the AVD state is saved and used to quickly re-launch the AVD next time. " + " Note this will make the emulator slow to close. "). put(CUSTOM_SKIN_FILE_KEY, "A collection of images and configuration data that indicates how to populate the window. Each skin can have " + "several \"layouts\" (e.g. \"landscape\" and \"portrait\") corresponding to different orientation " + "/ physical configurations of the emulated device.\n"). build(); /** * Get the help text for the given key in the form "Title|Body" */ public String getDescription(Key<?> key) { return TITLES.get(key) + "|" + DESCRIPTIONS.get(key); } }