/* * Copyright (C) 2012 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.motorola.studio.android.emulator.ui.controls.skin; import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.SWTRemoteDisplay; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Layout; import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance; import com.motorola.studio.android.emulator.core.skin.AndroidSkinBean; import com.motorola.studio.android.emulator.core.skin.ISkinKeyXmlTags; import com.motorola.studio.android.emulator.ui.IAndroidUIConstants; import com.motorola.studio.android.emulator.ui.controls.RemoteCLIDisplay; import com.motorola.studio.android.emulator.ui.controls.UIHelper; /** * DESCRIPTION: * This class implements the layout used to draw skins * It is a very specific implementation that applies only to Android Emulator * The position and size of the widgets are read from the skin bean provided * during layout construction * * RESPONSIBILITY: * - Draw the widgets that composes the skin in the correct position and * with the correct size * * COLABORATORS: * None. * * USAGE: * Use of this class is restricted to the classes in this package */ class AndroidSkinLayout extends Layout { /** * Object that holds information from the skin.xml file * of the skin being currently used */ private final AndroidSkinBean skin; /** * Reference to the CLI display that will be placed by this layout */ private RemoteCLIDisplay cliDisplayChild = null; /** * Reference to the main display that will be placed by this layout */ private SWTRemoteDisplay mainDisplayChild = null; /** * Flag that indicates if the skin contains an open external display * placeholder */ private final boolean openExternalDisplayAvailable; /** * Flag that indicates if the skin contains an external display * placeholder */ private final boolean externalDisplayAvailable; /** * Flag that indicates that the flip is supported. This flag has nothing to do with slide */ private final boolean isFlipSupported; /** * Creates a new AndroidSkinLayout object. * As the image dimensions are not provided with this constructor, it is not possible * to have automatic zoom factor calculation. Using this constructor will set the * initial zoom policy to "100%" * * @param skin An skin bean containing the parameters used to place the * widgets * @param isFlipSupported Flag that indicates if flip (not slide) is supported */ AndroidSkinLayout(AndroidSkinBean skin, boolean isFlipSupported) { this.skin = skin; this.isFlipSupported = isFlipSupported; openExternalDisplayAvailable = skin.isOpenExternalDisplayAvailable(); externalDisplayAvailable = skin.isExternalDisplayAvailable(); } /** * @see org.eclipse.swt.widgets.Layout#computeSize(Composite, int, int, boolean) */ @Override protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { if (!(composite instanceof SkinComposite)) { throw new IllegalArgumentException(); } Point size; // Retrieve needed data from composite IAndroidEmulatorInstance instance = UIHelper.getInstanceAssociatedToControl(composite); boolean flipSlideClosed = false; if (instance != null) { //flipSlideClosed = instance.isFlipSlideClosed(); flipSlideClosed = false; } double zoomFactor = ((SkinComposite) composite).getZoomFactor(); if (externalDisplayAvailable) { if (openExternalDisplayAvailable) { if (!flipSlideClosed) { // Scenario 1: // A) Available displays: INTERNAL, OPEN EXTERNAL, EXTERNAL // B) Flip is open // C) Displays being showed: INTERNAL, OPEN EXTERNAL size = allAvailable(zoomFactor); } else { // Scenario 2: // A) Available displays: INTERNAL, OPEN EXTERNAL, EXTERNAL // B) Flip is closed // C) Display being showed: EXTERNAL size = flipClosed(zoomFactor); } } else { if (!flipSlideClosed) { // Scenario 3: // A) Available displays: INTERNAL, EXTERNAL // B) Flip is opened // C) Display being showed: INTERNAL size = openExternalUnavailable(zoomFactor); } else { // Scenario 4: // A) Available displays: INTERNAL, EXTERNAL // B) Flip is closed // C) Display being showed: EXTERNAL size = openExternalUnavailableAndFlipClosed(zoomFactor); } } } else { // Scenario 5: // A) Available display: INTERNAL // B) Flip is opened or closed // C) Display being showed: INTERNAL size = onlyInternal(zoomFactor); } return size; } /** * This method is called by computeSize when all displays are available and the flip is opened * * @param zoomFactor The zoom factor used to calculate the size * * @return The size */ private Point allAvailable(double zoomFactor) { Point size; int x1 = skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_X); int y1 = Math.min(skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_Y), skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_Y)); int x2 = skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_X) + skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_WIDTH); int y2 = Math.max( skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_Y) + skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_HEIGHT), skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_Y) + skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_HEIGHT)); size = new Point((int) ((x2 - x1) * zoomFactor), (int) ((y2 - y1) * zoomFactor)); return size; } /** * This method is called by computeSize when all displays are available and the flip is closed * * @param zoomFactor The zoom factor used to calculate the size * * @return The size */ private Point flipClosed(double zoomFactor) { Point size = new Point( (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_WIDTH) * zoomFactor), (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_HEIGHT) * zoomFactor)); return size; } /** * This method is called by computeSize when all displays but open external display are available * and the flip is opened * * @param zoomFactor The zoom factor used to calculate the size * * @return The size */ private Point openExternalUnavailable(double zoomFactor) { Point size = new Point( (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_WIDTH) * zoomFactor), (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_HEIGHT) * zoomFactor)); return size; } /** * This method is called by computeSize when all displays but open external display are available * and the flip is closed * * @param zoomFactor The zoom factor used to calculate the size * * @return The size */ private Point openExternalUnavailableAndFlipClosed(double zoomFactor) { Point size = new Point( (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_WIDTH) * zoomFactor), (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_HEIGHT) * zoomFactor)); return size; } /** * This method is called by computeSize when only the internal display is available * * @param zoomFactor The zoom factor used to calculate the size * * @return The size */ private Point onlyInternal(double zoomFactor) { Point size = new Point( (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_WIDTH) * zoomFactor), (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_HEIGHT) * zoomFactor)); return size; } /** * @see org.eclipse.swt.widgets.Layout#layout(Composite, boolean) */ @Override protected void layout(Composite composite, boolean flushCache) { if (!(composite instanceof SkinComposite)) { // If this composite is not a SkinComposite, no layout should be done return; } boolean canProceed = true; if (flushCache || (mainDisplayChild == null) || (cliDisplayChild == null)) { Control[] children = composite.getChildren(); canProceed = checkChidren(children); } if (canProceed) { // Retrieve needed data from composite SkinComposite skinComposite = (SkinComposite) composite; IAndroidEmulatorInstance instance = UIHelper.getInstanceAssociatedToControl(composite); boolean flipSlideClosed = false; if (instance != null) { //flipSlideClosed = instance.isFlipSlideClosed(); flipSlideClosed = false; } skinComposite.recalculateZoomFactor(); skinComposite.updateDisplayRectangle(); double zoomFactor = skinComposite.getZoomFactor(); Rectangle displayRectangle = skinComposite.getDisplayRectangle(); // Handling main display case if (mainDisplayChild != null) { layoutMainDisplay(zoomFactor, displayRectangle); if ((isFlipSupported) && (flipSlideClosed)) { // On phones that support flip, the main display is hidden // when the flip is closed mainDisplayChild.setVisible(false); } else { mainDisplayChild.setVisible(true); } } // Handling CLI display case if (cliDisplayChild != null) { layoutCliDisplay(zoomFactor, displayRectangle, flipSlideClosed); if (((externalDisplayAvailable) && (!flipSlideClosed)) || ((openExternalDisplayAvailable) && (flipSlideClosed))) { // The CLI display is shown in 2 situations: // 1. When the flip is closed and there is information about // external display // 2. When the flip is opened and there is information about // open external display cliDisplayChild.setVisible(true); } else { cliDisplayChild.setVisible(false); } } } } /** * Checks if the composite children are as expected by the layout * It is needed to check if the composite's children are, at most: * 1. One main display * 2. One CLI display * * @param children Array of composite children to check * * @return true if children are as expected; false otherwise */ private boolean checkChidren(Control[] children) { RemoteCLIDisplay cliDisplayInstance = null; SWTRemoteDisplay mainDisplayInstance = null; boolean childrenOk = true; // We need to check if the composite's children are, at most: // // 1. One main display // 2. One CLI display for (Control child : children) { if (child instanceof SWTRemoteDisplay) { if (mainDisplayInstance == null) { mainDisplayInstance = (SWTRemoteDisplay) child; mainDisplayChild = mainDisplayInstance; } else { childrenOk = false; break; } } else if (child instanceof RemoteCLIDisplay) { if (cliDisplayInstance == null) { cliDisplayInstance = (RemoteCLIDisplay) child; cliDisplayChild = cliDisplayInstance; } else { childrenOk = false; break; } } else { childrenOk = false; break; } } return childrenOk; } /** * Performs the layout for main display * * @param zoomFactor The zoom factor to use when dimensioning the main display. * @param displayRectangle The area of skin that is being shown at the view. It can * be only a part of the skin if the view client area * is smaller than the skin size. It is used to calculate translation. */ private void layoutMainDisplay(double zoomFactor, Rectangle displayRectangle) { // Computes main display position int x = (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_X) * zoomFactor) - displayRectangle.x; int y = (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_Y) * zoomFactor) - displayRectangle.y; int width = (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_WIDTH) * zoomFactor * skin.getEmbeddedViewScale()); int height = (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_HEIGHT) * zoomFactor * skin.getEmbeddedViewScale()); // Sets main display size and position mainDisplayChild.setZoomFactor(zoomFactor * skin.getEmbeddedViewScale()); mainDisplayChild.setBounds(x, y, width, height); } /** * Performs the layout for CLI display * * @param zoomFactor The zoom factor to use when dimensioning the main display * @param displayRectangle The area of skin that is being shown at the view. It can * be only a part of the skin if the view client area * is smaller than the skin size. It is used to calculate translation. * @param isFlipSlideClosed True if the flip or slide is currently closed. False otherwise. */ private void layoutCliDisplay(double zoomFactor, Rectangle displayRectangle, boolean flipSlideClosed) { // Computes CLI display position int x = 0; int y = 0; int width = 0; int height = 0; if (!flipSlideClosed && openExternalDisplayAvailable) { x = (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_X) * zoomFactor) - displayRectangle.x; y = (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_Y) * zoomFactor) - displayRectangle.y; width = (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_WIDTH) * zoomFactor * IAndroidUIConstants.DISPLAY_TO_SKIN_RATIO); height = (int) (skin .getSkinPropertyValue(ISkinKeyXmlTags.SKIN_OPEN_EXTERNAL_VIEW_HEIGHT) * zoomFactor * IAndroidUIConstants.DISPLAY_TO_SKIN_RATIO); } else if (flipSlideClosed && externalDisplayAvailable) { x = (int) ((skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_X) * zoomFactor) - displayRectangle.x); y = (int) ((skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_Y) * zoomFactor) - displayRectangle.y); width = (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_WIDTH) * zoomFactor * IAndroidUIConstants.DISPLAY_TO_SKIN_RATIO); height = (int) (skin.getSkinPropertyValue(ISkinKeyXmlTags.SKIN_EXTERNAL_VIEW_HEIGHT) * zoomFactor * IAndroidUIConstants.DISPLAY_TO_SKIN_RATIO); } // Sets cli display size cliDisplayChild.setZoomFactor(zoomFactor * IAndroidUIConstants.DISPLAY_TO_SKIN_RATIO); cliDisplayChild.setBounds(x, y, width, height); } void dispose() { cliDisplayChild = null; mainDisplayChild = null; } }