// // Copyright 2005 The Android Open Source Project // // Simulated device data. // // For compilers that support precompilation, include "wx/wx.h". #include "wx/wxprec.h" // Otherwise, include all standard headers #ifndef WX_PRECOMP # include "wx/wx.h" #endif #include "wx/image.h" // needed for Windows build #include "PhoneData.h" #include "PhoneButton.h" #include "PhoneCollection.h" #include "MyApp.h" #include "utils.h" #include <utils/AssetManager.h> #include <utils/String8.h> #include "tinyxml.h" #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <assert.h> using namespace android; /* image relative path hack */ static const char* kRelPathMagic = "::/"; /* * =========================================================================== * PhoneKeyboard * =========================================================================== */ /* * Load a <keyboard> chunk. */ bool PhoneKeyboard::ProcessAndValidate(TiXmlNode* pNode) { //TiXmlNode* pChild; TiXmlElement* pElem; int qwerty = 0; assert(pNode->Type() == TiXmlNode::ELEMENT); pElem = pNode->ToElement(); pElem->Attribute("qwerty", &qwerty); const char *kmap = pElem->Attribute("keycharmap"); if (qwerty == 1) { printf("############## PhoneKeyboard::ProcessAndValidate: qwerty = true!\n"); mQwerty = true; } if (kmap != NULL) { printf("############## PhoneKeyboard::ProcessAndValidate: keycharmap = %s\n", kmap); mKeyMap = strdup(kmap); } return true; } /* * =========================================================================== * PhoneDisplay * =========================================================================== */ /* * Load a <display> chunk. */ bool PhoneDisplay::ProcessAndValidate(TiXmlNode* pNode) { //TiXmlNode* pChild; TiXmlElement* pElem; const char* name; const char* format; assert(pNode->Type() == TiXmlNode::ELEMENT); /* * Process attributes. Right now they're all mandatory, but some of * them could be defaulted (e.g. "rotate"). * * [We should do some range-checking here.] */ pElem = pNode->ToElement(); name = pElem->Attribute("name"); if (name == NULL) goto missing; if (pElem->Attribute("width", &mWidth) == NULL) goto missing; if (pElem->Attribute("height", &mHeight) == NULL) goto missing; if (pElem->Attribute("refresh", &mRefresh) == NULL) goto missing; format = pElem->Attribute("format"); if (format == NULL) goto missing; delete[] mName; mName = strdupNew(name); if (strcasecmp(format, "rgb565") == 0) { mFormat = android::PIXEL_FORMAT_RGB_565; } else { fprintf(stderr, "SimCFG: unexpected value for display format\n"); return false; } return true; missing: fprintf(stderr, "SimCFG: <display> requires name/width/height/format/refresh\n"); return false; } /* * Returns "true" if the two displays are compatible, "false" if not. * * Compatibility means they have the same resolution, format, refresh * rate, and so on. Anything transmitted to the runtime as part of the * initial configuration setup should be tested. */ /*static*/ bool PhoneDisplay::IsCompatible(PhoneDisplay* pDisplay1, PhoneDisplay* pDisplay2) { return (pDisplay1->mWidth == pDisplay2->mWidth && pDisplay1->mHeight == pDisplay2->mHeight && pDisplay1->mFormat == pDisplay2->mFormat && pDisplay1->mRefresh == pDisplay2->mRefresh); } /* * =========================================================================== * PhoneView * =========================================================================== */ /* * Load a <view> chunk. */ bool PhoneView::ProcessAndValidate(TiXmlNode* pNode, const char* directory) { TiXmlNode* pChild; TiXmlElement* pElem; int rotate; const char* displayName; assert(pNode->Type() == TiXmlNode::ELEMENT); /* * Process attributes. Right now they're all mandatory, but some of * them could be defaulted (e.g. "rotate"). * * [We should do some range-checking here.] */ pElem = pNode->ToElement(); displayName = pElem->Attribute("display"); if (displayName == NULL) goto missing; if (pElem->Attribute("x", &mXOffset) == NULL) goto missing; if (pElem->Attribute("y", &mYOffset) == NULL) goto missing; if (pElem->Attribute("rotate", &rotate) == NULL) goto missing; switch (rotate) { case 0: mRotation = kRot0; break; case 90: mRotation = kRot90; break; case 180: mRotation = kRot180; break; case 270: mRotation = kRot270; break; default: fprintf(stderr, "SimCFG: unexpected value for rotation\n"); mRotation = kRotUnknown; return false; } delete[] mDisplayName; mDisplayName = android::strdupNew(displayName); /* * Process elements. */ for (pChild = pNode->FirstChild(); pChild != NULL; pChild = pChild->NextSibling()) { if (pChild->Type() == TiXmlNode::COMMENT) continue; if (pChild->Type() == TiXmlNode::ELEMENT) { if (strcasecmp(pChild->Value(), "image") == 0) { if (!ProcessImage(pChild, directory)) return false; } else if (strcasecmp(pChild->Value(), "button") == 0) { if (!ProcessButton(pChild, directory)) return false; } else { fprintf(stderr, "SimCFG: Warning: unexpected elements in <display>\n"); } } else { fprintf(stderr, "SimCFG: Warning: unexpected stuff in <display>\n"); } } return true; missing: fprintf(stderr, "SimCFG: <view> requires display/x/y/rotate\n"); return false; } /* * Handle <image src="zzz" x="123" y="123"/>. */ bool PhoneView::ProcessImage(TiXmlNode* pNode, const char* directory) { TiXmlNode* pChild; TiXmlElement* pElem; int x, y; const char* src; LoadableImage tmpLimg; android::String8 fileName; pChild = pNode->FirstChild(); if (pChild != NULL) { fprintf(stderr, "SimCFG: <image> is funky\n"); return false; } /* * All attributes are mandatory. */ pElem = pNode->ToElement(); src = pElem->Attribute("src"); if (src == NULL) goto missing; if (pElem->Attribute("x", &x) == NULL) goto missing; if (pElem->Attribute("y", &y) == NULL) goto missing; if (strncmp(src, kRelPathMagic, strlen(kRelPathMagic)) == 0) { fileName = src + strlen(kRelPathMagic); } else { fileName = directory; fileName += "/"; fileName += src; } tmpLimg.Create(fileName, x, y); mImageList.push_back(tmpLimg); return true; missing: fprintf(stderr, "SimCFG: <image> requires src/x/y\n"); return false; } /* * Handle <button keyCode="zzz" src="zzz" x="123" y="123"/> and * <button keyCode="zzz"/>. */ bool PhoneView::ProcessButton(TiXmlNode* pNode, const char* directory) { TiXmlNode* pChild; TiXmlElement* pElem; int x, y; const char* keyCode; const char* src; PhoneButton tmpButton; android::String8 fileName; pChild = pNode->FirstChild(); if (pChild != NULL) { fprintf(stderr, "SimCFG: button is funky\n"); return false; } /* * Only keyCode is mandatory. If they specify "src", then "x" and "y" * are also required. */ pElem = pNode->ToElement(); keyCode = pElem->Attribute("keyCode"); if (keyCode == NULL) goto missing; src = pElem->Attribute("src"); if (src != NULL) { if (pElem->Attribute("x", &x) == NULL) goto missing; if (pElem->Attribute("y", &y) == NULL) goto missing; } if (src == NULL) tmpButton.Create(keyCode); else { if (strncmp(src, kRelPathMagic, strlen(kRelPathMagic)) == 0) { fileName = src + strlen(kRelPathMagic); } else { fileName = directory; fileName += "/"; fileName += src; } tmpButton.Create(keyCode, fileName, x, y); } mButtonList.push_back(tmpButton); return true; missing: fprintf(stderr, "SimCFG: <button> requires keycode and may have src/x/y\n"); return false; } /* * Load all resources associated with the display. */ bool PhoneView::LoadResources(void) { typedef List<LoadableImage>::iterator LIter; typedef List<PhoneButton>::iterator BIter; for (LIter ii = mImageList.begin(); ii != mImageList.end(); ++ii) (*ii).LoadResources(); for (BIter ii = mButtonList.begin(); ii != mButtonList.end(); ++ii) (*ii).LoadResources(); return true; } /* * Unload all resources associated with the display. */ bool PhoneView::UnloadResources(void) { typedef List<LoadableImage>::iterator LIter; typedef List<PhoneButton>::iterator BIter; for (LIter ii = mImageList.begin(); ii != mImageList.end(); ++ii) (*ii).UnloadResources(); for (BIter ii = mButtonList.begin(); ii != mButtonList.end(); ++ii) (*ii).UnloadResources(); return true; } /* * Get the #of images. */ int PhoneView::GetBkgImageCount(void) const { return mImageList.size(); } /* * Return the Nth entry. */ const LoadableImage* PhoneView::GetBkgImage(int idx) const { typedef List<LoadableImage>::const_iterator Iter; for (Iter ii = mImageList.begin(); ii != mImageList.end(); ++ii) { if (!idx) return &(*ii); --idx; } return NULL; } /* * Find the first button that covers the specified coordinates. * * The coordinates must be relative to the upper left corner of the * phone image. */ PhoneButton* PhoneView::FindButtonHit(int x, int y) { typedef List<PhoneButton>::iterator Iter; for (Iter ii = mButtonList.begin(); ii != mButtonList.end(); ++ii) { if ((*ii).CheckCollision(x, y)) return &(*ii); } return NULL; } /* * Find the first button with a matching key code. */ PhoneButton* PhoneView::FindButtonByKey(int32_t keyCode) { typedef List<PhoneButton>::iterator Iter; for (Iter ii = mButtonList.begin(); ii != mButtonList.end(); ++ii) { if ((*ii).GetKeyCode() == keyCode) return &(*ii); } return NULL; } /* * =========================================================================== * PhoneMode * =========================================================================== */ /* * Process a <mode name="zzz"> chunk. */ bool PhoneMode::ProcessAndValidate(TiXmlNode* pNode, const char* directory) { TiXmlNode* pChild; const char* name; assert(pNode->Type() == TiXmlNode::ELEMENT); name = pNode->ToElement()->Attribute("name"); if (name == NULL) { fprintf(stderr, "SimCFG: <mode> requires name attrib\n"); return false; } SetName(name); for (pChild = pNode->FirstChild(); pChild != NULL; pChild = pChild->NextSibling()) { if (pChild->Type() == TiXmlNode::COMMENT) continue; if (pChild->Type() == TiXmlNode::ELEMENT && strcasecmp(pChild->Value(), "view") == 0) { PhoneView tmpDisplay; bool result; result = tmpDisplay.ProcessAndValidate(pChild, directory); if (!result) return false; mViewList.push_back(tmpDisplay); } else { fprintf(stderr, "SimCFG: Warning: unexpected stuff in <mode>\n"); } } if (mViewList.size() == 0) { fprintf(stderr, "SimCFG: no <view> entries found\n"); return false; } return true; } /* * Load all resources associated with the phone. */ bool PhoneMode::LoadResources(void) { typedef List<PhoneView>::iterator Iter; for (Iter ii = mViewList.begin(); ii != mViewList.end(); ++ii) (*ii).LoadResources(); return true; } /* * Unload all resources associated with the phone. */ bool PhoneMode::UnloadResources(void) { typedef List<PhoneView>::iterator Iter; for (Iter ii = mViewList.begin(); ii != mViewList.end(); ++ii) (*ii).UnloadResources(); return true; } /* * Return the Nth entry. [make this a Vector?] */ PhoneView* PhoneMode::GetPhoneView(int viewNum) { typedef List<PhoneView>::iterator Iter; for (Iter ii = mViewList.begin(); ii != mViewList.end(); ++ii) { if (viewNum == 0) return &(*ii); --viewNum; } return NULL; } /* * =========================================================================== * PhoneData * =========================================================================== */ /* * Look for a "layout.xml" in the specified directory. If found, parse * the contents out. * * Returns "true" on success, "false" on failure. */ bool PhoneData::Create(const char* directory) { android::String8 fileName; SetDirectory(directory); fileName = directory; fileName += "/"; fileName += PhoneCollection::kLayoutFile; #ifdef BEFORE_ASSET TiXmlDocument doc(fileName); if (!doc.LoadFile()) #else android::AssetManager* pAssetMgr = ((MyApp*)wxTheApp)->GetAssetManager(); TiXmlDocument doc; android::Asset* pAsset; bool result; pAsset = pAssetMgr->open(fileName, Asset::ACCESS_STREAMING); if (pAsset == NULL) { fprintf(stderr, "Unable to open asset '%s'\n", (const char*) fileName); return false; } else { //printf("--- opened asset '%s'\n", // (const char*) pAsset->getAssetSource()); } /* TinyXml insists that the buffer be NULL-terminated... ugh */ char* buf = new char[pAsset->getLength() +1]; pAsset->read(buf, pAsset->getLength()); buf[pAsset->getLength()] = '\0'; delete pAsset; result = doc.Parse(buf); delete[] buf; if (!result) #endif { fprintf(stderr, "SimCFG: ERROR: failed parsing '%s'\n", (const char*) fileName); if (doc.ErrorRow() != 0) fprintf(stderr, " XML: %s (row=%d col=%d)\n", doc.ErrorDesc(), doc.ErrorRow(), doc.ErrorCol()); else fprintf(stderr, " XML: %s\n", doc.ErrorDesc()); return false; } if (!ProcessAndValidate(&doc)) { fprintf(stderr, "SimCFG: ERROR: failed analyzing '%s'\n", (const char*) fileName); return false; } printf("SimCFG: loaded data from '%s'\n", (const char*) fileName); return true; } /* * TinyXml has loaded and parsed the XML document for us. We need to * run through the DOM tree, pull out the interesting bits, and make * sure the stuff we need is present. * * Returns "true" on success, "false" on failure. */ bool PhoneData::ProcessAndValidate(TiXmlDocument* pDoc) { bool deviceFound = false; TiXmlNode* pChild; assert(pDoc->Type() == TiXmlNode::DOCUMENT); for (pChild = pDoc->FirstChild(); pChild != NULL; pChild = pChild->NextSibling()) { /* * Find the <device> entry. There should be exactly one. */ if (pChild->Type() == TiXmlNode::ELEMENT) { if (strcasecmp(pChild->Value(), "device") != 0) { fprintf(stderr, "SimCFG: Warning: unexpected element '%s' at top level\n", pChild->Value()); continue; } if (deviceFound) { fprintf(stderr, "SimCFG: one <device> per customer\n"); return false; } bool result = ProcessDevice(pChild); if (!result) return false; deviceFound = true; } } if (!deviceFound) { fprintf(stderr, "SimCFG: no <device> section found\n"); return false; } return true; } /* * Process a <device name="zzz"> chunk. */ bool PhoneData::ProcessDevice(TiXmlNode* pNode) { TiXmlNode* pChild; const char* name; assert(pNode->Type() == TiXmlNode::ELEMENT); name = pNode->ToElement()->Attribute("name"); if (name == NULL) { fprintf(stderr, "SimCFG: <device> requires name attrib\n"); return false; } SetName(name); /* * Walk through the children and find interesting stuff. * * Might be more correct to process all <display> entries and * then process all <view> entries, since <view> has "pointers" * to <display>. We're deferring the lookup until later, though, * so for now it doesn't really matter. */ for (pChild = pNode->FirstChild(); pChild != NULL; pChild = pChild->NextSibling()) { bool result; if (pChild->Type() == TiXmlNode::COMMENT) continue; if (pChild->Type() == TiXmlNode::ELEMENT && strcasecmp(pChild->Value(), "title") == 0) { result = ProcessTitle(pChild); if (!result) return false; } else if (pChild->Type() == TiXmlNode::ELEMENT && strcasecmp(pChild->Value(), "display") == 0) { PhoneDisplay tmpDisplay; result = tmpDisplay.ProcessAndValidate(pChild); if (!result) return false; mDisplayList.push_back(tmpDisplay); } else if (pChild->Type() == TiXmlNode::ELEMENT && strcasecmp(pChild->Value(), "keyboard") == 0) { PhoneKeyboard tmpKeyboard; result = tmpKeyboard.ProcessAndValidate(pChild); if (!result) return false; mKeyboardList.push_back(tmpKeyboard); } else if (pChild->Type() == TiXmlNode::ELEMENT && strcasecmp(pChild->Value(), "mode") == 0) { PhoneMode tmpMode; result = tmpMode.ProcessAndValidate(pChild, mDirectory); if (!result) return false; mModeList.push_back(tmpMode); } else { fprintf(stderr, "SimCFG: Warning: unexpected stuff in <device>\n"); } } if (mDisplayList.size() == 0) { fprintf(stderr, "SimCFG: no <display> entries found\n"); return false; } if (mModeList.size() == 0) { fprintf(stderr, "SimCFG: no <mode> entries found\n"); return false; } return true; } /* * Handle <title>. */ bool PhoneData::ProcessTitle(TiXmlNode* pNode) { TiXmlNode* pChild; pChild = pNode->FirstChild(); if (pChild->Type() != TiXmlNode::TEXT) { fprintf(stderr, "SimCFG: title is funky\n"); return false; } SetTitle(pChild->Value()); return true; } /* * Load all resources associated with the phone. */ bool PhoneData::LoadResources(void) { typedef List<PhoneMode>::iterator Iter; for (Iter ii = mModeList.begin(); ii != mModeList.end(); ++ii) (*ii).LoadResources(); return true; } /* * Unload all resources associated with the phone. */ bool PhoneData::UnloadResources(void) { typedef List<PhoneMode>::iterator Iter; for (Iter ii = mModeList.begin(); ii != mModeList.end(); ++ii) (*ii).UnloadResources(); return true; } /* * Return the PhoneMode entry with the matching name. * * Returns NULL if no match was found. */ PhoneMode* PhoneData::GetPhoneMode(const char* modeName) { typedef List<PhoneMode>::iterator Iter; for (Iter ii = mModeList.begin(); ii != mModeList.end(); ++ii) { if (strcmp((*ii).GetName(), modeName) == 0) return &(*ii); } return NULL; } /* * Return the Nth phone mode entry. */ PhoneMode* PhoneData::GetPhoneMode(int idx) { typedef List<PhoneMode>::iterator Iter; for (Iter ii = mModeList.begin(); ii != mModeList.end(); ++ii) { if (!idx) return &(*ii); --idx; } return NULL; } /* * Return the PhoneDisplay entry with the matching name. * * Returns NULL if no match was found. */ PhoneDisplay* PhoneData::GetPhoneDisplay(const char* dispName) { typedef List<PhoneDisplay>::iterator Iter; for (Iter ii = mDisplayList.begin(); ii != mDisplayList.end(); ++ii) { if (strcmp((*ii).GetName(), dispName) == 0) return &(*ii); } return NULL; } /* * Return the Nth phone mode entry. */ PhoneDisplay* PhoneData::GetPhoneDisplay(int idx) { typedef List<PhoneDisplay>::iterator Iter; for (Iter ii = mDisplayList.begin(); ii != mDisplayList.end(); ++ii) { if (!idx) return &(*ii); --idx; } return NULL; } /* * Find the PhoneDisplay entry with the matching name, and return its index. * * Returns -1 if the entry wasn't found. */ int PhoneData::GetPhoneDisplayIndex(const char* dispName) { typedef List<PhoneDisplay>::iterator Iter; int idx = 0; for (Iter ii = mDisplayList.begin(); ii != mDisplayList.end(); ++ii) { if (strcmp((*ii).GetName(), dispName) == 0) return idx; idx++; } return -1; } /* * Return the Nth phone keyboard entry. */ PhoneKeyboard* PhoneData::GetPhoneKeyboard(int idx) { typedef List<PhoneKeyboard>::iterator Iter; for (Iter ii = mKeyboardList.begin(); ii != mKeyboardList.end(); ++ii) { if (!idx) return &(*ii); --idx; } return NULL; }