/*
* 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.skin.android;
import static com.motorola.studio.android.common.log.StudioLogger.error;
import static com.motorola.studio.android.common.log.StudioLogger.info;
import static com.motorola.studio.android.common.log.StudioLogger.warn;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import com.motorola.studio.android.common.utilities.EclipseUtils;
import com.motorola.studio.android.emulator.core.exception.SkinException;
import com.motorola.studio.android.emulator.core.skin.AndroidSkinBean;
import com.motorola.studio.android.emulator.core.skin.IAndroidKey;
import com.motorola.studio.android.emulator.core.skin.IAndroidSkin;
import com.motorola.studio.android.emulator.core.skin.ISkinKeyXmlTags;
import com.motorola.studio.android.emulator.i18n.EmulatorNLS;
import com.motorola.studio.android.emulator.skin.android.parser.LayoutFileModel;
import com.motorola.studio.android.emulator.skin.android.parser.LayoutFileParser;
public class AndroidSkin implements IAndroidSkin
{
/**
* The released images of the skin, as read from the skin files
*/
private final Map<String, ImageData> releasedImagesPool = new HashMap<String, ImageData>();
/**
* The pressed images of the skin, as read from the skin files
*/
private final Map<String, ImageData> pressedImagesPool = new HashMap<String, ImageData>();
/**
* The enter images of the skin, as read from the skin files
*/
private final Map<String, ImageData> enterImagesPool = new HashMap<String, ImageData>();
/**
* The keys of the skin, as read from the skin files
*/
private final Map<String, Collection<IAndroidKey>> androidKeysPool =
new HashMap<String, Collection<IAndroidKey>>();
/**
* The folder where to look for skin files
*/
private File skinFilesPath;
/**
* A model containing the parsed layout file
*/
private LayoutFileModel layoutFile;
private Properties keycodes;
/**
* Retrieves data being kept in a pool
*
* @param layoutName The name of the layout which object has been requested
* @param pool The pool where to look for data
*
* @return An object from the pool that matches the request
*
* @throws SkinException If either the layout file was not loaded, or if the images/keys cannot
* be generated
*/
private ImageData getImageFromPool(String layoutName, Map<String, ImageData> pool)
throws SkinException
{
if (layoutFile == null)
{
error("User has tried to request skin data without setting a valid skin files path");
throw new SkinException(EmulatorNLS.ERR_AndroidSkin_NoLayoutLoaded);
}
// Tries to get data from the pool. If it is not available for the
// current layout yet, load all resources related to the layout to the pools.
ImageData id = pool.get(layoutName);
if (id == null)
{
addImagesToPools(layoutName);
id = pool.get(layoutName);
}
return id;
}
/*
* (non-Javadoc)
* @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getKeyDataCollection(java.lang.String)
*/
public Collection<IAndroidKey> getKeyDataCollection(String layoutName)
{
Collection<IAndroidKey> androidKeys;
try
{
if (layoutFile == null)
{
error("User has tried to request skin data without setting a valid skin files path");
throw new SkinException(EmulatorNLS.ERR_AndroidSkin_NoLayoutLoaded);
}
// Tries to get data from the pool. If it is not available for the
// current layout yet, load all resources related to the layout to the pools.
androidKeys = androidKeysPool.get(layoutName);
if (androidKeys == null)
{
System.gc();
androidKeys =
AndroidSkinTranslator.generateAndroidKeys(layoutFile, layoutName,
skinFilesPath);
System.gc();
androidKeysPool.put(layoutName, androidKeys);
}
}
catch (SkinException e)
{
androidKeys = new HashSet<IAndroidKey>();
error("The key data could not be retrieved from skin files. Cause: " + e.getMessage());
EclipseUtils.showErrorDialog(e);
}
return androidKeys;
}
public Properties getKeyCodes()
{
if ((keycodes == null) || ((keycodes != null) && keycodes.isEmpty()))
{
try
{
keycodes = AndroidSkinTranslator.getKeycodes(layoutFile, skinFilesPath);
}
catch (SkinException e)
{
keycodes = new Properties();
error("The key data could not be retrieved from skin files. Cause: "
+ e.getMessage());
}
}
return keycodes;
}
/*
* (non-Javadoc)
* @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getPressedImageData(java.lang.String)
*/
public ImageData getPressedImageData(String layoutName) throws SkinException
{
return getImageFromPool(layoutName, pressedImagesPool);
}
/*
* (non-Javadoc)
* @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getEnterImageData(java.lang.String)
*/
public ImageData getEnterImageData(String layoutName) throws SkinException
{
return getImageFromPool(layoutName, enterImagesPool);
}
/*
* (non-Javadoc)
* @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getReleasedImageData(java.lang.String)
*/
public ImageData getReleasedImageData(String layoutName) throws SkinException
{
return getImageFromPool(layoutName, releasedImagesPool);
}
/*
* (non-Javadoc)
* @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getSkinBean(java.lang.String)
*/
public AndroidSkinBean getSkinBean(String layoutName) throws SkinException
{
if (layoutFile == null)
{
error("User has tried to request additional skin data without setting a valid skin files path");
throw new SkinException(EmulatorNLS.ERR_AndroidSkin_NoLayoutLoaded);
}
AndroidSkinBean bean = new AndroidSkinBean();
// Fills the skin bean with information related to the part chosen for display. This bean must have
// at least display positioning information and scale.
String partName = layoutFile.getMainPartName(layoutName);
Point dPosition =
AndroidSkinTranslator.translateDisplayPosition(layoutFile, layoutName, partName,
skinFilesPath);
int dWidth = layoutFile.getDisplayWidth(partName);
int dHeight = layoutFile.getDisplayHeight(partName);
int dw, dh;
if (layoutFile.isSwapWidthHeightNeededAtLayout(layoutName))
{
dw = dHeight;
dh = dWidth;
}
else
{
dw = dWidth;
dh = dHeight;
}
bean.addSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_X, dPosition.x);
bean.addSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_Y, dPosition.y);
bean.addSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_WIDTH, dw);
bean.addSkinPropertyValue(ISkinKeyXmlTags.SKIN_INTERNAL_VIEW_HEIGHT, dh);
bean.addSkinPropertyValue(ISkinKeyXmlTags.SKIN_EMBEDDED_VIEW_SCALE, 10);
return bean;
}
/*
* (non-Javadoc)
* @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#isFlipSupported()
*/
public boolean isFlipSupported()
{
return false;
}
/*
* (non-Javadoc)
* @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#setSkinFilesPath(java.lang.String)
*/
public void setSkinFilesPath(String skinFilesPath) throws SkinException
{
this.skinFilesPath = new File(skinFilesPath);
if (this.skinFilesPath.isDirectory())
{
layoutFile = LayoutFileParser.readLayout(this.skinFilesPath);
}
else
{
error("Provided skin files path is not a directory. Setting the skin files path operation has failed.");
throw new SkinException(EmulatorNLS.ERR_AndroidSkin_ProvidedSkinPathIsNotADirectory);
}
}
/*
* (non-Javadoc)
* @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#isRotatedLayout(java.lang.String)
*/
public boolean isSwapWidthHeightNeededAtLayout(String layoutName)
{
return layoutFile.isSwapWidthHeightNeededAtLayout(layoutName);
}
/*
* (non-Javadoc)
* @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getAvailableLayouts()
*/
public Collection<String> getAvailableLayouts()
{
return layoutFile.getLayoutNames();
}
/*
* (non-Javadoc)
* @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getLayoutScreenCommand(java.lang.String)
*/
public String getLayoutScreenCommand(String layoutName)
{
return layoutFile.getLayoutSwitchCommand(layoutName);
}
/*
* (non-Javadoc)
* @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#nextLayout(java.lang.String)
*/
public String getNextLayout(String referenceLayout)
{
info("Calculating the next layout");
String next = null;
if (referenceLayout != null)
{
List<String> layoutsList = new ArrayList<String>(getAvailableLayouts());
// Switches to the next layout if the skin supports it. The if/else clause
// implements a circular list
if (layoutsList.size() > 1)
{
int currentLayoutNum = layoutsList.indexOf(referenceLayout);
if (currentLayoutNum != layoutsList.size() - 1)
{
next = layoutsList.get(++currentLayoutNum);
}
else
{
next = layoutsList.get(0);
}
info("Next layout: " + next);
}
else
{
warn("The skin doesn't have multiple layouts. The operation was not performed");
}
}
else
{
warn("The skin doesn't have multiple layouts. The operation was not performed");
}
return next;
}
/*
* (non-Javadoc)
* @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#previousLayout(java.lang.String)
*/
public String getPreviousLayout(String referenceLayout)
{
info("Calculating the previous layout");
String previous = null;
if (referenceLayout != null)
{
List<String> layoutsList = new ArrayList<String>(getAvailableLayouts());
// Switches to the previous layout if the skin supports it. The if/else clause
// implements a circular list
if (layoutsList.size() > 1)
{
int currentLayoutNum = layoutsList.indexOf(referenceLayout);
if (currentLayoutNum != 0)
{
previous = layoutsList.get(--currentLayoutNum);
}
else
{
previous = layoutsList.get(layoutsList.size() - 1);
}
info("Previous layout: " + previous);
}
else
{
warn("The skin doesn't have multiple layouts. The operation was not performed");
}
}
else
{
warn("The skin doesn't have multiple layouts. The operation was not performed");
}
return previous;
}
/*
* (non-Javadoc)
* @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getBackgroundColor(java.lang.String)
*/
public RGB getBackgroundColor(String layoutName)
{
return layoutFile.getLayoutColor(layoutName, skinFilesPath);
}
/*
* (non-Javadoc)
* @see com.motorola.studio.android.emulator.core.skin.IAndroidSkin#getDpadRotation(java.lang.String)
*/
public int getDpadRotation(String layoutName)
{
return layoutFile.getDpadRotation(layoutName);
}
/**
* Generates images/keys for the current layout (or main part, if a layout is not available)
* and store them at the appropriate pools
*
* @param key The key to be used to store data at the pools
*
* @throws SkinException If the layout file cannot be loaded
*/
private void addImagesToPools(String key) throws SkinException
{
// Validates the provided string before proceeding
if ((key == null) || (!layoutFile.getLayoutNames().contains(key)))
{
throw new SkinException(EmulatorNLS.ERR_AndroidSkin_InvalidLayoutProvided);
}
System.gc();
ImageData[] images =
AndroidSkinTranslator.generateLayoutImages(layoutFile, key, skinFilesPath);
info("Adding released/pressed/hover images to the pool");
releasedImagesPool.put(key, images[0]);
pressedImagesPool.put(key, images[1]);
enterImagesPool.put(key, images[2]);
System.gc();
}
}