[
  {
    "path": ".gitignore",
    "content": "# Built application files\n*.apk\n*.ap_\n.DS_Store\n\n# files for the dex VM\n*.dex\n\n# Java class files\n*.class\n\n# generated files\nbin/\ngen/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Eclipse project files\n.classpath\n.project\n\n# Proguard folder generated by Eclipse\nproguard/\n\n# Intellij project files\n*.iml\n*.ipr\n*.iws\n.idea/\n\n# Gradle files\n.gradle/\nbuild/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Proguard folder generated by Eclipse\nproguard/\n"
  },
  {
    "path": "LICENSE",
    "content": "Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n\"License\" shall mean the terms and conditions for use, reproduction, and\ndistribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright\nowner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all other entities\nthat control, are controlled by, or are under common control with that entity.\nFor the purposes of this definition, \"control\" means (i) the power, direct or\nindirect, to cause the direction or management of such entity, whether by\ncontract or otherwise, or (ii) ownership of fifty percent (50%) or more of the\noutstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising\npermissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications, including\nbut not limited to software source code, documentation source, and configuration\nfiles.\n\n\"Object\" form shall mean any form resulting from mechanical transformation or\ntranslation of a Source form, including but not limited to compiled object code,\ngenerated documentation, and conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made\navailable under the License, as indicated by a copyright notice that is included\nin or attached to the work (an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that\nis based on (or derived from) the Work and for which the editorial revisions,\nannotations, elaborations, or other modifications represent, as a whole, an\noriginal work of authorship. For the purposes of this License, Derivative Works\nshall not include works that remain separable from, or merely link (or bind by\nname) to the interfaces of, the Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including the original version\nof the Work and any modifications or additions to that Work or Derivative Works\nthereof, that is intentionally submitted to Licensor for inclusion in the Work\nby the copyright owner or by an individual or Legal Entity authorized to submit\non behalf of the copyright owner. For the purposes of this definition,\n\"submitted\" means any form of electronic, verbal, or written communication sent\nto the Licensor or its representatives, including but not limited to\ncommunication on electronic mailing lists, source code control systems, and\nissue tracking systems that are managed by, or on behalf of, the Licensor for\nthe purpose of discussing and improving the Work, but excluding communication\nthat is conspicuously marked or otherwise designated in writing by the copyright\nowner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf\nof whom a Contribution has been received by Licensor and subsequently\nincorporated within the Work.\n\n2. Grant of Copyright License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable copyright license to reproduce, prepare Derivative Works of,\npublicly display, publicly perform, sublicense, and distribute the Work and such\nDerivative Works in Source or Object form.\n\n3. Grant of Patent License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable (except as stated in this section) patent license to make, have\nmade, use, offer to sell, sell, import, and otherwise transfer the Work, where\nsuch license applies only to those patent claims licensable by such Contributor\nthat are necessarily infringed by their Contribution(s) alone or by combination\nof their Contribution(s) with the Work to which such Contribution(s) was\nsubmitted. If You institute patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that the Work or a\nContribution incorporated within the Work constitutes direct or contributory\npatent infringement, then any patent licenses granted to You under this License\nfor that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution.\n\nYou may reproduce and distribute copies of the Work or Derivative Works thereof\nin any medium, with or without modifications, and in Source or Object form,\nprovided that You meet the following conditions:\n\nYou must give any other recipients of the Work or Derivative Works a copy of\nthis License; and\nYou must cause any modified files to carry prominent notices stating that You\nchanged the files; and\nYou must retain, in the Source form of any Derivative Works that You distribute,\nall copyright, patent, trademark, and attribution notices from the Source form\nof the Work, excluding those notices that do not pertain to any part of the\nDerivative Works; and\nIf the Work includes a \"NOTICE\" text file as part of its distribution, then any\nDerivative Works that You distribute must include a readable copy of the\nattribution notices contained within such NOTICE file, excluding those notices\nthat do not pertain to any part of the Derivative Works, in at least one of the\nfollowing places: within a NOTICE text file distributed as part of the\nDerivative Works; within the Source form or documentation, if provided along\nwith the Derivative Works; or, within a display generated by the Derivative\nWorks, if and wherever such third-party notices normally appear. The contents of\nthe NOTICE file are for informational purposes only and do not modify the\nLicense. You may add Your own attribution notices within Derivative Works that\nYou distribute, alongside or as an addendum to the NOTICE text from the Work,\nprovided that such additional attribution notices cannot be construed as\nmodifying the License.\nYou may add Your own copyright statement to Your modifications and may provide\nadditional or different license terms and conditions for use, reproduction, or\ndistribution of Your modifications, or for any such Derivative Works as a whole,\nprovided Your use, reproduction, and distribution of the Work otherwise complies\nwith the conditions stated in this License.\n\n5. Submission of Contributions.\n\nUnless You explicitly state otherwise, any Contribution intentionally submitted\nfor inclusion in the Work by You to the Licensor shall be under the terms and\nconditions of this License, without any additional terms or conditions.\nNotwithstanding the above, nothing herein shall supersede or modify the terms of\nany separate license agreement you may have executed with Licensor regarding\nsuch Contributions.\n\n6. Trademarks.\n\nThis License does not grant permission to use the trade names, trademarks,\nservice marks, or product names of the Licensor, except as required for\nreasonable and customary use in describing the origin of the Work and\nreproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty.\n\nUnless required by applicable law or agreed to in writing, Licensor provides the\nWork (and each Contributor provides its Contributions) on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,\nincluding, without limitation, any warranties or conditions of TITLE,\nNON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are\nsolely responsible for determining the appropriateness of using or\nredistributing the Work and assume any risks associated with Your exercise of\npermissions under this License.\n\n8. Limitation of Liability.\n\nIn no event and under no legal theory, whether in tort (including negligence),\ncontract, or otherwise, unless required by applicable law (such as deliberate\nand grossly negligent acts) or agreed to in writing, shall any Contributor be\nliable to You for damages, including any direct, indirect, special, incidental,\nor consequential damages of any character arising as a result of this License or\nout of the use or inability to use the Work (including but not limited to\ndamages for loss of goodwill, work stoppage, computer failure or malfunction, or\nany and all other commercial damages or losses), even if such Contributor has\nbeen advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability.\n\nWhile redistributing the Work or Derivative Works thereof, You may choose to\noffer, and charge a fee for, acceptance of support, warranty, indemnity, or\nother liability obligations and/or rights consistent with this License. However,\nin accepting such obligations, You may act only on Your own behalf and on Your\nsole responsibility, not on behalf of any other Contributor, and only if You\nagree to indemnify, defend, and hold each Contributor harmless for any liability\nincurred by, or claims asserted against, such Contributor by reason of your\naccepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\n\nTo apply the Apache License to your work, attach the following boilerplate\nnotice, with the fields enclosed by brackets \"{}\" replaced with your own\nidentifying information. (Don't include the brackets!) The text should be\nenclosed in the appropriate comment syntax for the file format. We also\nrecommend that a file or class name and description of purpose be included on\nthe same \"printed page\" as the copyright notice for easier identification within\nthird-party archives.\n\n   Copyright 2014 way\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License."
  },
  {
    "path": "README.md",
    "content": "威震天气\n======\n\n本应用是以变形金刚作为主题元素的实用型Android天气软件，界面高仿雅虎天气，数据来自中国天气网！\n\n##下载apk\n\n[腾讯应用市场](http://sj.qq.com/myapp/detail.htm?apkName=com.way.yahoo) \n\n[百度应用市场](http://shouji.baidu.com/soft/item?docid=6930651&from=as&f=search_app_%E5%A8%81%E9%9C%87%E5%A4%A9%E6%B0%94%40list_1_title%401%40header_all_input)\n\t \n\n# **威震天气 Android 客户端项目简析** #\n\n*注：本文假设你已经有Android开发环境*\n\n①.启动Eclipse，将名为WayHoo导入作为主工程，并确保你当前的Android SDK是最新版。<br>\n②.将libs目录下相应的库导入Eclipse，并作为主工程的库工程，以免缺少一些必要的资源或类。<br>\n③.如果编译出错，请修改项目根目录下的 project.properties 文件。<br>\n④.请使用Android 4.0 以上版本的SDK，JDK1.6编译：\n\n> target=android-19\n\n**本项目采用 GPL 授权协议，欢迎大家在这个基础上进行改进，并与大家分享。**\n\n## 联系我\n\nway:\n  * [邮箱](mailto:way.ping.li@gmail.com \"给我发邮件\")\n  * [博客](http://blog.csdn.net/way_ping_li \"CSDN博客\")\n\n\n## 测试截图\n\n###高斯模糊效果图\n![Screenshot 0](https://raw.githubusercontent.com/way1989/WayHoo/master/screenshots/0.png \"Screenshot 0\")\n\n![Screenshot 1](http://git.oschina.net/way/WayHoo/raw/master/screenshots/1.png \"Screenshot 1\")\n\n![Screenshot 2](http://git.oschina.net/way/WayHoo/raw/master/screenshots/2.png \"Screenshot 2\")\n\n![Screenshot 3](http://git.oschina.net/way/WayHoo/raw/master/screenshots/3.png \"Screenshot 3\")\n\n![Screenshot 4](http://git.oschina.net/way/WayHoo/raw/master/screenshots/4.png \"Screenshot 4\")\n\n![Screenshot 5](http://git.oschina.net/way/WayHoo/raw/master/screenshots/5.png \"Screenshot 5\")\n\n![Screenshot 6](http://git.oschina.net/way/WayHoo/raw/master/screenshots/6.png \"Screenshot 6\")"
  },
  {
    "path": "Upgrade-lib/.settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6\norg.eclipse.jdt.core.compiler.compliance=1.6\norg.eclipse.jdt.core.compiler.source=1.6\n"
  },
  {
    "path": "Upgrade-lib/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    package=\"com.way.upgrade\"\r\n    android:versionCode=\"1\"\r\n    android:versionName=\"1.0.0\" >\r\n\n    <uses-sdk\n        android:minSdkVersion=\"14\"\n        android:targetSdkVersion=\"21\" />\r\n</manifest>\r\n"
  },
  {
    "path": "Upgrade-lib/proguard-project.txt",
    "content": "# To enable ProGuard in your project, edit project.properties\n# to define the proguard.config property as described in that file.\n#\n# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in ${sdk.dir}/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the ProGuard\n# include property in project.properties.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "Upgrade-lib/project.properties",
    "content": "# This file is automatically generated by Android Tools.\n# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n#\n# This file must be checked in Version Control Systems.\n#\n# To customize properties used by the Ant build system edit\n# \"ant.properties\", and override values to adapt the script to your\n# project structure.\n#\n# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):\n#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt\n\n# Project target.\ntarget=android-22\nandroid.library=true\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/MainActivity.java",
    "content": "package com.way.upgrade;\r\n\r\nimport android.app.Activity;\r\nimport android.os.Bundle;\r\n\r\nimport com.way.upgrade.core.UpgradeManager;\r\n\r\npublic class MainActivity extends Activity {\r\n\r\n\t@Override\r\n\tprotected void onCreate(Bundle savedInstanceState) {\r\n\t\tsuper.onCreate(savedInstanceState);\r\n\r\n\t\tfinal UpgradeManager upgradeMangeer = UpgradeManager.newInstance(this);\r\n\r\n\t\tupgradeMangeer.askForNewVersion();\r\n\t}\r\n}\r\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/bean/UpgradeInfo.java",
    "content": "package com.way.upgrade.bean;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\n/**\n * 版本更新信息\n * \n * @author way\n * @since 2014/4/28\n */\npublic class UpgradeInfo implements Parcelable{\n\tprivate int id;\n\t\n\t/**唯一标识apk*/\n\tprivate String key;\n\n\t/** 版本信息 */\n\tprivate String version;\n\t\n\t/** 版本名称 */\n\tprivate String versionName;\n\t\n\t/** 是否有更新 */\n\tprivate String result;\n\n\t/** 新版本地址 */\n\tprivate String url;\n\n\t/** 版本描述 */\n\tprivate String description;\n\t\n\t/** 已下载的大小  */\n\tprivate long downloadSize;\n\t\n\tprivate String mustUpdate;\n\tprivate String changeLog;\n\t\n\tprivate String errorCode;\n\t\n\t\n\tpublic static final Parcelable.Creator<UpgradeInfo> CREATOR = new Creator<UpgradeInfo>() {\n\n\t\t@Override\n\t\tpublic UpgradeInfo[] newArray(int size) {\n\t\t\treturn new UpgradeInfo[size];\n\t\t}\n\n\t\t@Override\n\t\tpublic UpgradeInfo createFromParcel(Parcel source) {\n\t\t\treturn new UpgradeInfo(source);\n\t\t}\n\t};\n\n\tpublic UpgradeInfo() {\n\n\t}\n\t\n\tpublic UpgradeInfo(Parcel in) {\n\t\tid = in.readInt();\n\t\tkey = in.readString();\n\t\tversion = in.readString();\n\t\tversionName = in.readString();\n\t\tresult = in.readString();\n\t\turl = in.readString();\n\t\tdescription = in.readString();\n\t\tdownloadSize = in.readLong();\n\t\tresult = in.readString();\n\t\tmustUpdate = in.readString();\n\t\tchangeLog = in.readString();\n\t\terrorCode = in.readString();\n\t}\n\n\t@Override\n\tpublic int describeContents() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void writeToParcel(Parcel dest, int flags) {\n\t\tdest.writeInt(id);\n\t\tdest.writeString(key);\n\t\tdest.writeString(version);\n\t\tdest.writeString(versionName);\n\t\tdest.writeString(result);\n\t\tdest.writeString(url);\n\t\tdest.writeString(description);\n\t\tdest.writeLong(downloadSize);\n\t\tdest.writeString(result);\n\t\tdest.writeString(mustUpdate);\n\t\tdest.writeString(changeLog);\n\t\tdest.writeString(errorCode);\n\t}\n\n\tpublic int getId() {\n\t\treturn id;\n\t}\n\n\tpublic void setId(int id) {\n\t\tthis.id = id;\n\t}\n\n\tpublic String getKey() {\n\t\treturn key;\n\t}\n\n\tpublic void setKey(String key) {\n\t\tthis.key = key;\n\t}\n\n\tpublic String getVersion() {\n\t\treturn version;\n\t}\n\n\tpublic void setVersion(String version) {\n\t\tthis.version = version;\n\t}\n\n\tpublic String getUrl() {\n\t\treturn url;\n\t}\n\n\tpublic void setUrl(String url) {\n\t\tthis.url = url;\n\t}\n\n\tpublic String getDescription() {\n\t\treturn description;\n\t}\n\n\tpublic void setDescription(String description) {\n\t\tthis.description = description;\n\t}\n\n\tpublic long getDownloadSize() {\n\t\treturn downloadSize;\n\t}\n\n\tpublic void setDownloadSize(long downloadSize) {\n\t\tthis.downloadSize = downloadSize;\n\t}\n\n\tpublic String getVersionName() {\n\t\treturn versionName;\n\t}\n\n\tpublic void setVersionName(String versionName) {\n\t\tthis.versionName = versionName;\n\t}\n\n\tpublic String isResult() {\n\t\treturn result;\n\t}\n\n\tpublic void setResult(String result) {\n\t\tthis.result = result;\n\t}\n\n\tpublic String getMustUpdate() {\n\t\treturn mustUpdate;\n\t}\n\n\tpublic void setMustUpdate(String mustUpdate) {\n\t\tthis.mustUpdate = mustUpdate;\n\t}\n\t\n\tpublic String getChangeLog() {\n\t\treturn changeLog;\n\t}\n\n\tpublic void setChangeLog(String changeLog) {\n\t\tthis.changeLog = changeLog;\n\t}\n\n\tpublic String getResult() {\n\t\treturn result;\n\t}\n\n\n\tpublic String getErrorCode() {\n\t\treturn errorCode;\n\t}\n\n\tpublic void setErrorCode(String errorCode) {\n\t\tthis.errorCode = errorCode;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"UpgradeInfo [id=\" + id + \", key=\" + key + \", version=\"\n\t\t\t\t+ version + \", versionName=\" + versionName + \", result=\"\n\t\t\t\t+ result + \", url=\" + url + \", description=\" + description\n\t\t\t\t+ \", downloadSize=\" + downloadSize + \", mustUpdate=\"\n\t\t\t\t+ mustUpdate + \", changeLog=\" + changeLog\n\t\t\t\t+ \", errorCode=\" + errorCode + \"]\";\n\t}\n\n\t\n}\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/core/CheckNewVersionListener.java",
    "content": "package com.way.upgrade.core;\r\n\r\n/**\r\n * \r\n * @author way\r\n * @sine 2014/10/29\r\n */\r\npublic interface CheckNewVersionListener {\r\n\tpublic void checkNewVersion(boolean result);\r\n}\r\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/core/UpgradeInterface.java",
    "content": "package com.way.upgrade.core;\r\n\r\nimport com.way.upgrade.bean.UpgradeInfo;\r\n\r\n/**\r\n * \r\n * @author way\r\n * @since 2014/4/28\r\n */\r\npublic interface UpgradeInterface {\r\n\t/**\r\n\t * 请求服务器是否有新版本,并弹出对话框\r\n\t * \r\n\t * @return\r\n\t */\r\n\tpublic void askForNewVersion();\r\n\r\n\t/**\r\n\t * 请求服务器是否有新版本\r\n\t * \r\n\t * @return\r\n\t */\r\n\tpublic void askForNewVersionFlag(\r\n\t\t\tCheckNewVersionListener checkversionListener);\r\n\r\n\t/**\r\n\t * 下载新版本\r\n\t * \r\n\t * @return\r\n\t */\r\n\tpublic void downloadNewVersion(UpgradeInfo upgradeInfo);\r\n}\r\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/core/UpgradeManager.java",
    "content": "package com.way.upgrade.core;\r\n\r\nimport java.util.HashMap;\r\nimport java.util.Locale;\r\nimport java.util.Map;\r\n\r\nimport android.app.AlertDialog;\r\nimport android.app.ProgressDialog;\r\nimport android.content.Context;\r\nimport android.content.DialogInterface;\r\nimport android.os.Handler;\r\nimport android.os.Message;\r\nimport android.util.Log;\r\nimport android.widget.Toast;\r\n\r\nimport com.way.upgrade.bean.UpgradeInfo;\r\nimport com.way.upgrade.job.CheckNewVersionJobWithoutClientUrl;\r\nimport com.way.upgrade.job.DownloadNewVersionJob;\r\nimport com.way.upgrade.locale.LocaleChina;\r\nimport com.way.upgrade.locale.LocaleChinaTW;\r\nimport com.way.upgrade.locale.LocaleChinese;\r\nimport com.way.upgrade.locale.LocaleEnglish;\r\nimport com.way.upgrade.locale.LocaleHandler;\r\nimport com.way.upgrade.locale.LocaleUS;\r\nimport com.way.upgrade.utils.Constants;\r\nimport com.way.upgrade.utils.ContextUtils;\r\nimport com.way.upgrade.utils.thread.Future;\r\nimport com.way.upgrade.utils.thread.FutureListener;\r\nimport com.way.upgrade.utils.thread.ThreadPool;\r\n\r\n/**\r\n * \r\n * @author way\r\n * @since 2014/4/28\r\n */\r\npublic class UpgradeManager extends LocaleHandler implements UpgradeInterface {\r\n\tprivate Context mContext;\r\n\tprivate static ThreadPool mThreadPool;\r\n\tprivate Handler mHandler;\r\n\tprivate AlertDialog alertDialog;\r\n\tprivate AlertDialog alertDialogForMustUpdate;\r\n\tprivate ProgressDialog mProgressDialog;\r\n\r\n\tprivate Map<String, LocaleHandler> handlers;\r\n\r\n\tprivate static Future<UpgradeInfo> mFuture;\r\n\r\n\tprivate CheckNewVersionListener mAskForNewVersionFlagListener;\r\n\r\n\tprivate FutureListener<UpgradeInfo> mAskForNewVersionListener = new FutureListener<UpgradeInfo>() {\r\n\t\tpublic void onFutureDone(Future<UpgradeInfo> future) {\r\n\t\t\tUpgradeInfo upgradeInfo = future.get();\r\n\t\t\tMessage msg = mHandler.obtainMessage();\r\n\t\t\tif (upgradeInfo != null) {\r\n\t\t\t\tif (upgradeInfo.getErrorCode() != null) {\r\n\t\t\t\t\tif (upgradeInfo.getErrorCode().equals(\r\n\t\t\t\t\t\t\tConstants.ERROR_CODE_NET)) {\r\n\t\t\t\t\t\tmsg.what = Constants.MSG_NET_ERROR;\r\n\t\t\t\t\t}\r\n\t\t\t\t} else if (Boolean.parseBoolean(upgradeInfo.getResult())) {\r\n\t\t\t\t\tmFuture = future;\r\n\t\t\t\t\tmsg.what = Constants.MSG_HAVA_NEW_VERSION;\r\n\t\t\t\t\tmsg.obj = upgradeInfo;\r\n\t\t\t\t} else if (!Boolean.parseBoolean(upgradeInfo.getResult())) {\r\n\t\t\t\t\tmsg.what = Constants.MSG_NO_NEW_VERSION;\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\tmsg.what = Constants.MSG_NO_NEW_VERSION;\r\n\t\t\t}\r\n\t\t\tmHandler.sendMessage(msg);\r\n\t\t};\r\n\t};\r\n\r\n\tpublic class AskForNewVersionFlag implements FutureListener<UpgradeInfo> {\r\n\t\t@Override\r\n\t\tpublic void onFutureDone(Future<UpgradeInfo> future) {\r\n\t\t\tUpgradeInfo upgradeInfo = future.get();\r\n\t\t\tboolean result = false;\r\n\t\t\tif (upgradeInfo != null) {\r\n\t\t\t\tresult = Boolean.parseBoolean(upgradeInfo.getResult());\r\n\t\t\t}\r\n\t\t\tMessage msg = mHandler.obtainMessage();\r\n\t\t\tmsg.what = Constants.MSG_VERSION_RESULT;\r\n\t\t\tmsg.obj = result;\r\n\t\t\tmHandler.sendMessage(msg);\r\n\t\t}\r\n\r\n\t}\r\n\r\n\tpublic UpgradeManager(Context context) {\r\n\t\tcreateHandlers();\r\n\t\tmContext = context;\r\n\t\tinit();\r\n\t}\r\n\r\n\tprivate void init() {\r\n\t\tif (mThreadPool == null) {\r\n\t\t\tmThreadPool = new ThreadPool();\r\n\t\t}\r\n\t\tmHandler = new Handler(mContext.getMainLooper()) {\r\n\t\t\t@Override\r\n\t\t\tpublic void handleMessage(Message msg) {\r\n\t\t\t\tswitch (msg.what) {\r\n\t\t\t\tcase Constants.MSG_HAVA_NEW_VERSION:\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tContextUtils.closeProgressDialog(mProgressDialog);\r\n\t\t\t\t\t\tUpgradeInfo description = (UpgradeInfo) msg.obj;\r\n\t\t\t\t\t\tshowDialog(description);\r\n\t\t\t\t\t} catch (Exception e1) {\r\n\t\t\t\t\t\te1.printStackTrace();\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase Constants.MSG_NO_NEW_VERSION:\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tContextUtils.closeProgressDialog(mProgressDialog);\r\n\t\t\t\t\t\tContextUtils.showToast(mContext, getToastMessage(),\r\n\t\t\t\t\t\t\t\tToast.LENGTH_SHORT);\r\n\t\t\t\t\t} catch (Exception e) {\r\n\t\t\t\t\t\te.printStackTrace();\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase Constants.MSG_START_DOWNLOAD:\r\n\t\t\t\t\tUpgradeInfo description1 = (UpgradeInfo) msg.obj;\r\n\t\t\t\t\tdownloadNewVersion(description1);\r\n\t\t\t\t\tbreak;\r\n\r\n\t\t\t\tcase Constants.MSG_VERSION_RESULT:\r\n\t\t\t\t\tBoolean result = (Boolean) msg.obj;\r\n\t\t\t\t\tmAskForNewVersionFlagListener.checkNewVersion(result);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase Constants.MSG_NET_ERROR:\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tContextUtils.closeProgressDialog(mProgressDialog);\r\n\t\t\t\t\t} catch (Exception e) {\r\n\t\t\t\t\t\te.printStackTrace();\r\n\t\t\t\t\t}\r\n\t\t\t\t\tContextUtils.showToast(mContext, getToastNetErrorMessage(),\r\n\t\t\t\t\t\t\tToast.LENGTH_SHORT);\r\n\t\t\t\tdefault:\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\talertDialog = ContextUtils.showAlertDialog(mContext, getDialogTitle(),\r\n\t\t\t\t\"\", new int[] { android.R.string.ok, android.R.string.cancel },\r\n\t\t\t\tnew DialogInterface.OnClickListener[] {\r\n\t\t\t\t\t\tnew DialogInterface.OnClickListener() {\r\n\r\n\t\t\t\t\t\t\t@Override\r\n\t\t\t\t\t\t\tpublic void onClick(DialogInterface arg0, int arg1) {\r\n\t\t\t\t\t\t\t\tif (mFuture != null) {\r\n\t\t\t\t\t\t\t\t\tMessage msg = mHandler.obtainMessage();\r\n\t\t\t\t\t\t\t\t\tmsg.what = Constants.MSG_START_DOWNLOAD;\r\n\t\t\t\t\t\t\t\t\tmsg.obj = mFuture.get();\r\n\t\t\t\t\t\t\t\t\tmHandler.sendMessage(msg);\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t}, new DialogInterface.OnClickListener() {\r\n\r\n\t\t\t\t\t\t\t@Override\r\n\t\t\t\t\t\t\tpublic void onClick(DialogInterface dialog,\r\n\t\t\t\t\t\t\t\t\tint which) {\r\n\t\t\t\t\t\t\t\tdialog.dismiss();\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t} });\r\n\r\n\t\talertDialogForMustUpdate = ContextUtils\r\n\t\t\t\t.showAlertDialog(\r\n\t\t\t\t\t\tmContext,\r\n\t\t\t\t\t\tgetDialogTitle(),\r\n\t\t\t\t\t\t\"\",\r\n\t\t\t\t\t\tnew int[] { android.R.string.ok },\r\n\t\t\t\t\t\tnew DialogInterface.OnClickListener[] { new DialogInterface.OnClickListener() {\r\n\r\n\t\t\t\t\t\t\t@Override\r\n\t\t\t\t\t\t\tpublic void onClick(DialogInterface arg0, int arg1) {\r\n\t\t\t\t\t\t\t\tif (mFuture != null) {\r\n\t\t\t\t\t\t\t\t\tMessage msg = mHandler.obtainMessage();\r\n\t\t\t\t\t\t\t\t\tmsg.what = Constants.MSG_START_DOWNLOAD;\r\n\t\t\t\t\t\t\t\t\tmsg.obj = mFuture.get();\r\n\t\t\t\t\t\t\t\t\tmHandler.sendMessage(msg);\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t} });\r\n\t\talertDialogForMustUpdate.setCancelable(false);\r\n\r\n\t\tmProgressDialog = ContextUtils.createProgressDialog(mContext);\r\n\t\tmProgressDialog.setTitle(getProgressDialogTitle());\r\n\t\tmProgressDialog.setMessage(getProgressDialogMessage());\r\n\r\n\t}\r\n\r\n\tpublic static UpgradeManager newInstance(Context context) {\r\n\t\treturn new UpgradeManager(context);\r\n\t}\r\n\r\n\tprivate void showProgressDialog() {\r\n\t\tif (mProgressDialog != null) {\r\n\t\t\ttry {\r\n\t\t\t\tmProgressDialog.show();\r\n\t\t\t} catch (Exception e) {\r\n\t\t\t\te.printStackTrace();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void askForNewVersion() {\r\n\t\tshowProgressDialog();\r\n\t\tmThreadPool.submit(new CheckNewVersionJobWithoutClientUrl(mContext),\r\n\t\t\t\tmAskForNewVersionListener, ThreadPool.MODE_NETWORK);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void askForNewVersionFlag(\r\n\t\t\tCheckNewVersionListener checkversionListener) {\r\n\t\tif (checkversionListener == null) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tmAskForNewVersionFlagListener = checkversionListener;\r\n\t\tAskForNewVersionFlag mAskForNewVersionFlagListener = new AskForNewVersionFlag();\r\n\t\tmThreadPool.submit(new CheckNewVersionJobWithoutClientUrl(mContext),\r\n\t\t\t\tmAskForNewVersionFlagListener, ThreadPool.MODE_NETWORK);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void downloadNewVersion(UpgradeInfo upgradeInfo) {\r\n\t\tmThreadPool.submit(new DownloadNewVersionJob(mContext, upgradeInfo),\r\n\t\t\t\tnull, ThreadPool.MODE_CPU);\r\n\t}\r\n\r\n\tprivate void createHandlers() {\r\n\t\thandlers = new HashMap<String, LocaleHandler>();\r\n\t\thandlers.put(LocaleChinese.defaultLocale, new LocaleChinese());\r\n\t\thandlers.put(LocaleChinaTW.defaultLocale, new LocaleChinaTW());\r\n\t\thandlers.put(LocaleEnglish.defaultLocale, new LocaleEnglish());\r\n\t\thandlers.put(Locale.CHINA.toString(), new LocaleChina());\r\n\t\thandlers.put(Locale.US.toString(), new LocaleUS());\r\n\t}\r\n\r\n\tprivate LocaleHandler lookupHandlerBy(String handlerName) {\r\n\t\tLocaleHandler handler = handlers.get(handlerName);\r\n\t\tif (handler == null)\r\n\t\t\treturn handlers.get(Locale.ENGLISH.getLanguage());\r\n\t\treturn handlers.get(handlerName);\r\n\t}\r\n\r\n\tprivate void showDialog(UpgradeInfo description) {\r\n\t\tint mustUpdate = Integer.valueOf(description.getMustUpdate());\r\n\t\tboolean result = Boolean.parseBoolean(description.isResult());\r\n\t\tString descriptionStr = description.getChangeLog();\r\n\t\tLog.i(\"liweiping\", \"mustUpdate = \" + mustUpdate + \", result = \"\r\n\t\t\t\t+ result + \", alertDialog = \" + alertDialog\r\n\t\t\t\t+ \",  alertDialogForMustUpdate = \" + alertDialogForMustUpdate);\r\n\t\tif (mustUpdate == Constants.NOT_MUST_UPDATE) {\r\n\t\t\tif (alertDialog != null && result) {\r\n\t\t\t\talertDialog.setTitle(getDialogTitle());\r\n\t\t\t\talertDialog.setMessage(descriptionStr);\r\n\t\t\t\talertDialog.show();\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tif (alertDialogForMustUpdate != null && result) {\r\n\t\t\t\talertDialogForMustUpdate.setTitle(getDialogTitle());\r\n\t\t\t\talertDialogForMustUpdate.setMessage(descriptionStr);\r\n\t\t\t\talertDialogForMustUpdate.show();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tprivate String getLocaleLanguage() {\r\n\t\treturn Locale.getDefault().toString();\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getDialogTitle() {\r\n\t\tLocaleHandler handler = lookupHandlerBy(getLocaleLanguage());\r\n\t\treturn handler.getDialogTitle();\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getProgressDialogTitle() {\r\n\t\tLocaleHandler handler = lookupHandlerBy(getLocaleLanguage());\r\n\t\treturn handler.getProgressDialogTitle();\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getProgressDialogMessage() {\r\n\t\tLocaleHandler handler = lookupHandlerBy(getLocaleLanguage());\r\n\t\treturn handler.getProgressDialogMessage();\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getToastMessage() {\r\n\t\tLocaleHandler handler = lookupHandlerBy(getLocaleLanguage());\r\n\t\treturn handler.getToastMessage();\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getToastNetErrorMessage() {\r\n\t\tLocaleHandler handler = lookupHandlerBy(getLocaleLanguage());\r\n\t\treturn handler.getToastNetErrorMessage();\r\n\t}\r\n}\r\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/job/AbstractCheckNewVersionJob.java",
    "content": "package com.way.upgrade.job;\r\n\r\nimport java.io.IOException;\r\n\r\nimport org.apache.http.client.ClientProtocolException;\r\nimport org.json.JSONException;\r\nimport org.json.JSONObject;\r\n\r\nimport android.content.Context;\r\nimport android.content.pm.ApplicationInfo;\r\nimport android.content.pm.PackageManager;\r\nimport android.content.pm.PackageManager.NameNotFoundException;\r\nimport android.util.Log;\r\n\r\nimport com.way.upgrade.bean.UpgradeInfo;\r\nimport com.way.upgrade.parser.json.FirVersionJsonParsing;\r\nimport com.way.upgrade.utils.Constants;\r\nimport com.way.upgrade.utils.NetUtils;\r\nimport com.way.upgrade.utils.NetUtils.NETWORK_STATUS;\r\nimport com.way.upgrade.utils.thread.ThreadPool;\r\nimport com.way.upgrade.utils.thread.ThreadPool.JobContext;\r\n\r\n/**\r\n * \r\n * @author way\r\n * @since 2014/4/28\r\n */\r\npublic abstract class AbstractCheckNewVersionJob implements\r\n\t\tThreadPool.Job<UpgradeInfo> {\r\n\tprotected Context mContext;\r\n\tprotected String bundle_id;\r\n\tprotected String api_token;\r\n\r\n\tpublic AbstractCheckNewVersionJob(Context context) {\r\n\t\tthis.mContext = context;\r\n\t\ttry {\r\n\t\t\tApplicationInfo appInfo = mContext.getPackageManager()\r\n\t\t\t\t\t.getApplicationInfo(mContext.getPackageName(),\r\n\t\t\t\t\t\t\tPackageManager.GET_META_DATA);\r\n\t\t\tbundle_id = appInfo.metaData.getString(\"bundle_id\");\r\n\t\t\tapi_token = appInfo.metaData.getString(\"api_token\");\r\n\t\t} catch (NameNotFoundException e) {\r\n\t\t\tthrow new NullPointerException(\r\n\t\t\t\t\t\"app_id and token must not null in AndroidManifest.xml \"\r\n\t\t\t\t\t\t\t+ e);\r\n\t\t}\r\n\t\tLog.i(\"liweiping\", \"AbstractCheckNewVersionJob app_id = \" + bundle_id\r\n\t\t\t\t+ \",  api_token = \" + api_token);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic UpgradeInfo run(JobContext jc) {\r\n\r\n\t\tUpgradeInfo upgradeInfo = null;\r\n\t\tif (NetUtils.getNetworkType(mContext) == NETWORK_STATUS.STATE_NONE_NETWORK) {\r\n\t\t\tLog.i(\"liweiping\", \"NETWORK_STATUS.STATE_NONE_NETWORK\");\r\n\t\t\tupgradeInfo = new UpgradeInfo();\r\n\t\t\tupgradeInfo.setErrorCode(Constants.ERROR_CODE_NET);\r\n\t\t\treturn upgradeInfo;\r\n\t\t}\r\n\t\ttry {\r\n\t\t\tString checkUpdateUrl = String.format(getCheckVersionUrl(), bundle_id,\r\n\t\t\t\t\tapi_token);\r\n\t\t\tLog.i(\"liweiping\", \"checkUpdateUrl = \" + checkUpdateUrl);\r\n\r\n\t\t\tJSONObject jo = NetUtils.getJSONArrayByGet(checkUpdateUrl);\r\n\t\t\tFirVersionJsonParsing firVersionJsonParsing = new FirVersionJsonParsing(\r\n\t\t\t\t\tmContext);\r\n\t\t\tLog.i(\"liweiping\",\r\n\t\t\t\t\t\"upgradeInfo = \" + upgradeInfo + \", checkUpdateUrl = \"\r\n\t\t\t\t\t\t\t+ checkUpdateUrl + \", jo = \" + jo.toString());\r\n\t\t\tupgradeInfo = firVersionJsonParsing.readJsonItem(jo);\r\n\t\t} catch (Exception e) {\r\n\t\t\tupgradeInfo = new UpgradeInfo();\r\n\t\t\tupgradeInfo.setErrorCode(Constants.ERROR_CODE_NET);\r\n\t\t\tLog.i(\"liweiping\",e.getMessage());\r\n\t\t\te.printStackTrace();\r\n\t\t}\r\n\t\treturn upgradeInfo;\r\n\t}\r\n\r\n\tpublic abstract String getCheckVersionUrl();\r\n\r\n}\r\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/job/CheckNewVersionJobWithoutClientUrl.java",
    "content": "package com.way.upgrade.job;\r\n\r\nimport android.content.Context;\r\n\r\nimport com.way.upgrade.utils.Constants;\r\n\r\n/**\r\n * \r\n * @author way\r\n * @since 2014/4/28\r\n */\r\npublic class CheckNewVersionJobWithoutClientUrl extends\r\n\t\tAbstractCheckNewVersionJob {\r\n\tpublic CheckNewVersionJobWithoutClientUrl(Context context) {\r\n\t\tsuper(context);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getCheckVersionUrl() {\r\n\t\treturn Constants.BASE_URL;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/job/DownloadNewVersionJob.java",
    "content": "package com.way.upgrade.job;\r\n\r\nimport java.io.File;\r\n\r\nimport android.app.DownloadManager;\r\nimport android.app.DownloadManager.Request;\r\nimport android.content.Context;\r\nimport android.content.Intent;\r\nimport android.content.pm.ApplicationInfo;\r\nimport android.content.pm.PackageManager;\r\nimport android.database.Cursor;\r\nimport android.net.Uri;\r\nimport android.os.Environment;\r\nimport android.util.Log;\r\n\r\nimport com.way.upgrade.bean.UpgradeInfo;\r\nimport com.way.upgrade.utils.Constants;\r\nimport com.way.upgrade.utils.Preferences;\r\nimport com.way.upgrade.utils.thread.ThreadPool;\r\nimport com.way.upgrade.utils.thread.ThreadPool.JobContext;\r\n\r\npublic class DownloadNewVersionJob implements ThreadPool.Job<Void> {\r\n\tprivate Context mContext;\r\n\tprivate UpgradeInfo mUpgradeInfo;\r\n\r\n\tprivate boolean allowMobileDownload = false;\r\n\tprivate static final long MAX_ALLOWED_DOWNLOAD_BYTES_BY_MOBILE = 3145725;\r\n\r\n\tpublic DownloadNewVersionJob(Context context, UpgradeInfo upgradeInfo) {\r\n\t\tthis.mContext = context;\r\n\t\tthis.mUpgradeInfo = upgradeInfo;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Void run(JobContext jc) {\r\n\t\ttry {\r\n\t\t\tif (checkDownloadRunning())\r\n\t\t\t\treturn null;\r\n\t\t\tif (checkApkExist()) {\r\n\t\t\t\tIntent installApkIntent = new Intent();\r\n\t\t\t\tinstallApkIntent.setAction(Intent.ACTION_VIEW);\r\n\t\t\t\tinstallApkIntent.setDataAndType(\r\n\t\t\t\t\t\tUri.parse(Preferences.getDownloadPath(mContext)),\r\n\t\t\t\t\t\t\"application/vnd.android.package-archive\");\r\n\t\t\t\tinstallApkIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK\r\n\t\t\t\t\t\t| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);\r\n\t\t\t\tmContext.startActivity(installApkIntent);\r\n\t\t\t} else {\r\n\t\t\t\tString apkName = mContext.getPackageName()\r\n\t\t\t\t\t\t+ System.currentTimeMillis() + Constants.APK_SUFFIX;\r\n\t\t\t\t// 系统下载程序\r\n\t\t\t\tfinal DownloadManager downloadManager = (DownloadManager) mContext\r\n\t\t\t\t\t\t.getSystemService(mContext.DOWNLOAD_SERVICE);\r\n\r\n\t\t\t\tLong recommendedMaxBytes = DownloadManager\r\n\t\t\t\t\t\t.getRecommendedMaxBytesOverMobile(mContext);\r\n\r\n\t\t\t\t// 可以在移动网络下下载\r\n\t\t\t\tif (recommendedMaxBytes == null\r\n\t\t\t\t\t\t|| recommendedMaxBytes.longValue() > MAX_ALLOWED_DOWNLOAD_BYTES_BY_MOBILE) {\r\n\t\t\t\t\tallowMobileDownload = true;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tUri uri = Uri.parse(mUpgradeInfo.getUrl());\r\n\r\n\t\t\t\tfinal Request request = new Request(uri);\r\n\r\n\t\t\t\trequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);\r\n\t\t\t\trequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);\r\n\r\n\t\t\t\tint NETWORK_TYPE = DownloadManager.Request.NETWORK_WIFI;\r\n\t\t\t\tif (allowMobileDownload) {\r\n\t\t\t\t\tNETWORK_TYPE |= DownloadManager.Request.NETWORK_MOBILE;\r\n\t\t\t\t}\r\n\t\t\t\trequest.setAllowedNetworkTypes(NETWORK_TYPE);\r\n\t\t\t\trequest.allowScanningByMediaScanner();\r\n\t\t\t\trequest.setShowRunningNotification(true);\r\n\t\t\t\trequest.setVisibleInDownloadsUi(true);\r\n\t\t\t\trequest.setDestinationInExternalPublicDir(\r\n\t\t\t\t\t\tEnvironment.DIRECTORY_DOWNLOADS, apkName);\r\n\t\t\t\tPackageManager packageManager = mContext.getPackageManager();\r\n\t\t\t\tApplicationInfo applicationInfo = packageManager\r\n\t\t\t\t\t\t.getApplicationInfo(mContext.getPackageName(), 0);\r\n\t\t\t\tLog.i(\"liweiping\",\r\n\t\t\t\t\t\t\"appName = \"\r\n\t\t\t\t\t\t\t\t+ packageManager\r\n\t\t\t\t\t\t\t\t\t\t.getApplicationLabel(applicationInfo));\r\n\t\t\t\trequest.setTitle(packageManager\r\n\t\t\t\t\t\t.getApplicationLabel(applicationInfo));\r\n\t\t\t\trequest.setMimeType(\"application/vnd.android.package-archive\");\r\n\r\n\t\t\t\t// id 保存起来跟之后的广播接收器作对比\r\n\t\t\t\tlong id = downloadManager.enqueue(request);\r\n\r\n\t\t\t\tlong oldId = Preferences.getDownloadId(mContext);\r\n\t\t\t\tif (oldId != -1) {\r\n\t\t\t\t\tdownloadManager.remove(oldId);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tPreferences.removeAll(mContext);\r\n\t\t\t\tPreferences.setDownloadId(mContext, id);\r\n\t\t\t\tPreferences.setUpgradeInfo(mContext, mUpgradeInfo);\r\n\t\t\t\tPreferences.setDownloadStatus(mContext,\r\n\t\t\t\t\t\tConstants.DOWNLOAD_STATUS_RUNNING);\r\n\t\t\t}\r\n\t\t} catch (Exception e) {\r\n\t\t\te.printStackTrace();\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\tprivate boolean checkApkExist() {\r\n\t\tUpgradeInfo prefUpgradeInfo = Preferences.getUpgradeInfo(mContext);\r\n\t\tString version = prefUpgradeInfo.getVersion();\r\n\t\tString downloadPath = Preferences.getDownloadPath(mContext);\r\n\r\n\t\tif (version != null && version.trim().length() != 0\r\n\t\t\t\t&& version.equals(mUpgradeInfo.getVersion())\r\n\t\t\t\t&& downloadPath != null && downloadPath.trim().length() != 0) {\r\n\t\t\tString path = Uri.parse(downloadPath).getPath();\r\n\t\t\tif (path != null && path.endsWith(Constants.APK_SUFFIX)) {\r\n\t\t\t\tFile file = new File(path);\r\n\t\t\t\tif (file.exists()) {\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@SuppressWarnings(\"static-access\")\r\n\tprivate boolean checkDownloadRunning() {\r\n\t\tUpgradeInfo prefUpgradeInfo = Preferences.getUpgradeInfo(mContext);\r\n\t\tString version = prefUpgradeInfo.getVersion();\r\n\t\tint downloadStatus = Preferences.getDownloadStatus(mContext);\r\n\t\tif (version != null && version.trim().length() != 0\r\n\t\t\t\t&& version.equals(mUpgradeInfo.getVersion())) {\r\n\t\t\tlong downloadId = Preferences.getDownloadId(mContext);\r\n\t\t\tif (downloadId != -1) {\r\n\t\t\t\tfinal DownloadManager downloadManager = (DownloadManager) mContext\r\n\t\t\t\t\t\t.getSystemService(mContext.DOWNLOAD_SERVICE);\r\n\t\t\t\tDownloadManager.Query mDownloadQuery = new DownloadManager.Query();\r\n\t\t\t\tmDownloadQuery.setFilterById(downloadId);\r\n\t\t\t\tCursor cursor = downloadManager.query(mDownloadQuery);\r\n\t\t\t\tif (cursor != null && cursor.moveToFirst()) {\r\n\t\t\t\t\tint status = cursor.getInt(cursor\r\n\t\t\t\t\t\t\t.getColumnIndex(DownloadManager.COLUMN_STATUS));\r\n\t\t\t\t\tif (status == DownloadManager.STATUS_RUNNING\r\n\t\t\t\t\t\t\t|| downloadStatus == Constants.DOWNLOAD_STATUS_RUNNING) {\r\n\t\t\t\t\t\treturn true;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/locale/LocaleChina.java",
    "content": "package com.way.upgrade.locale;\r\n\r\n/**\r\n * \r\n * @author way\r\n * @since 2014/10/29\r\n */\r\npublic class LocaleChina extends LocaleChinese {\r\n\r\n}\r\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/locale/LocaleChinaTW.java",
    "content": "package com.way.upgrade.locale;\r\n\r\n/**\r\n * \r\n * @author way\r\n * @since 2014/10/29\r\n */\r\npublic class LocaleChinaTW extends LocaleChinese {\r\n\tpublic static final String defaultLocale = \"zh_TW\";\r\n\r\n\t@Override\r\n\tpublic String getDialogTitle() {\r\n\t\treturn \"發現新版本，是否升級？\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getProgressDialogTitle() {\r\n\t\treturn \"檢查更新\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getProgressDialogMessage() {\r\n\t\treturn \"正在檢查...\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getToastMessage() {\r\n\t\treturn \"已是最新版本\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getToastNetErrorMessage() {\r\n\t\treturn \"網路異常\";\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/locale/LocaleChinese.java",
    "content": "package com.way.upgrade.locale;\r\n\r\n/**\r\n * \r\n * @author way\r\n * @since 2014/10/29\r\n */\r\npublic class LocaleChinese extends LocaleHandler {\r\n\tpublic static final String defaultLocale = \"zh\";\r\n\r\n\t@Override\r\n\tpublic String getDialogTitle() {\r\n\t\treturn \"发现新版本，是否升级？\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getProgressDialogTitle() {\r\n\t\treturn \"检查更新\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getProgressDialogMessage() {\r\n\t\treturn \"正在检查...\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getToastMessage() {\r\n\t\treturn \"已是最新版本\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getToastNetErrorMessage() {\r\n\t\treturn \"网络异常\";\r\n\t}\r\n}\r\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/locale/LocaleEnglish.java",
    "content": "package com.way.upgrade.locale;\r\n\r\npublic class LocaleEnglish extends LocaleHandler {\r\n\tpublic static final String defaultLocale = \"en\";\r\n\r\n\t@Override\r\n\tpublic String getDialogTitle() {\r\n\t\treturn \"Find New Version,Update Now?\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getProgressDialogTitle() {\r\n\t\treturn \"Check Update\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getProgressDialogMessage() {\r\n\t\treturn \"Checking\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getToastMessage() {\r\n\t\treturn \"Is the latest version\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getToastNetErrorMessage() {\r\n\t\treturn \"Net Error\";\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/locale/LocaleHandler.java",
    "content": "package com.way.upgrade.locale;\r\n\r\n/**\r\n * \r\n * @author way\r\n * @since 2014/10/29\r\n */\r\npublic abstract class LocaleHandler {\r\n\tprotected String value;\r\n\r\n\tpublic abstract String getDialogTitle();\r\n\r\n\tpublic abstract String getProgressDialogTitle();\r\n\r\n\tpublic abstract String getProgressDialogMessage();\r\n\r\n\tpublic abstract String getToastMessage();\r\n\r\n\tpublic abstract String getToastNetErrorMessage();\r\n\r\n\tpublic void setUpgradeDescription(String value) {\r\n\t\tthis.value = value;\r\n\t}\r\n\r\n\tpublic String getUpgradeDescription() {\r\n\t\treturn value;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/locale/LocaleUS.java",
    "content": "package com.way.upgrade.locale;\r\n\r\npublic class LocaleUS extends LocaleEnglish {\r\n}\r\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/parser/json/AbstractJsonParsing.java",
    "content": "package com.way.upgrade.parser.json;\n\nimport java.util.ArrayList;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\npublic abstract class AbstractJsonParsing<T> {\n\n\tpublic abstract T readJsonItem(JSONObject jsonItem) throws JSONException;\n\n\t/**\n\t * 输入InputStream\n\t * \n\t * @param inStream\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic ArrayList<T> readJsonArray(JSONArray jsonArray) {\n\n\t\ttry {\n\t\t\tArrayList<T> listOperating = new ArrayList<T>();\n\n\t\t\tfor (int i = 0; i < jsonArray.length(); i++) {\n\t\t\t\tJSONObject item = jsonArray.getJSONObject(i);\n\t\t\t\tT itemBean = readJsonItem(item);\n\t\t\t\tlistOperating.add(itemBean);\n\t\t\t}\n\t\t\treturn listOperating;\n\t\t} catch (JSONException e) {\n\t\t\te.printStackTrace();\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/parser/json/FirVersionJsonParsing.java",
    "content": "package com.way.upgrade.parser.json;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport android.content.Context;\n\nimport com.way.upgrade.bean.UpgradeInfo;\nimport com.way.upgrade.utils.Constants;\nimport com.way.upgrade.utils.ContextUtils;\n\npublic class FirVersionJsonParsing extends AbstractJsonParsing<UpgradeInfo> {\n\tprivate Context mContext;\n\n\tpublic FirVersionJsonParsing(Context context) {\n\t\tmContext = context;\n\t}\n\n\t@Override\n\tpublic UpgradeInfo readJsonItem(JSONObject versionJsonObj)\n\t\t\tthrows JSONException {\n\t\tUpgradeInfo upgradeInfo = new UpgradeInfo();\n\t\tString url = versionJsonObj.getString(\"installUrl\");\n\t\tString firVersionCode = versionJsonObj.getString(\"version\");\n\t\tString firVersionName = versionJsonObj.getString(\"versionShort\");\n\t\tString changeLog = versionJsonObj.getString(\"changelog\");\n\t\tupgradeInfo.setUrl(url);\n\t\tupgradeInfo.setVersion(firVersionCode);\n\t\tboolean result = ContextUtils.getVersionCode(mContext) < Integer\n\t\t\t\t.parseInt(firVersionCode)\n\t\t\t\t|| (ContextUtils.getVersionCode(mContext) == Integer\n\t\t\t\t\t\t.parseInt(firVersionCode) && !ContextUtils\n\t\t\t\t\t\t.getVersionName(mContext).equals(firVersionName));\n\t\tupgradeInfo.setResult(String.valueOf(result));\n\t\tupgradeInfo.setVersionName(firVersionName);\n\t\tupgradeInfo.setMustUpdate(Constants.NOT_MUST_UPDATE + \"\");\n\t\tupgradeInfo.setChangeLog(changeLog);\n\t\treturn upgradeInfo;\n\t}\n\n}\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/receiver/DownloadCompleteReveiver.java",
    "content": "package com.way.upgrade.receiver;\r\n\r\nimport android.app.DownloadManager;\r\nimport android.app.DownloadManager.Query;\r\nimport android.content.BroadcastReceiver;\r\nimport android.content.Context;\r\nimport android.content.Intent;\r\nimport android.database.Cursor;\r\nimport android.net.Uri;\r\n\r\nimport com.way.upgrade.utils.Preferences;\r\n\r\n/**\r\n * 下载完成通知接收器\r\n * \r\n * @author way\r\n * \r\n */\r\npublic class DownloadCompleteReveiver extends BroadcastReceiver {\r\n\tprivate DownloadManager downloadManager;\r\n\r\n\t@Override\r\n\tpublic void onReceive(Context context, Intent intent) {\r\n\t\tString action = intent.getAction();\r\n\r\n\t\tif (action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {\r\n\t\t\tlong id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0);\r\n\t\t\tif (id == Preferences.getDownloadId(context)) {\r\n\t\t\t\tQuery query = new Query();\r\n\t\t\t\tquery.setFilterById(id);\r\n\t\t\t\tdownloadManager = (DownloadManager) context\r\n\t\t\t\t\t\t.getSystemService(Context.DOWNLOAD_SERVICE);\r\n\t\t\t\tCursor cursor = downloadManager.query(query);\r\n\r\n\t\t\t\tint columnCount = cursor.getColumnCount();\r\n\t\t\t\tString path = null;\r\n\t\t\t\twhile (cursor.moveToNext()) {\r\n\t\t\t\t\tfor (int j = 0; j < columnCount; j++) {\r\n\t\t\t\t\t\tString columnName = cursor.getColumnName(j);\r\n\t\t\t\t\t\tString string = cursor.getString(j);\r\n\t\t\t\t\t\tif (\"local_uri\".equals(columnName)) {\r\n\t\t\t\t\t\t\tpath = string;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tcursor.close();\r\n\r\n\t\t\t\tif (path != null) {\r\n\t\t\t\t\tPreferences.setDownloadPath(context, path);\r\n\t\t\t\t\tPreferences.setDownloadStatus(context, -1);\r\n\t\t\t\t\tIntent installApkIntent = new Intent();\r\n\t\t\t\t\tinstallApkIntent.setAction(Intent.ACTION_VIEW);\r\n\t\t\t\t\tinstallApkIntent.setDataAndType(Uri.parse(path),\r\n\t\t\t\t\t\t\t\"application/vnd.android.package-archive\");\r\n\t\t\t\t\tinstallApkIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK\r\n\t\t\t\t\t\t\t| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);\r\n\t\t\t\t\tcontext.startActivity(installApkIntent);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} else if (action.equals(DownloadManager.ACTION_NOTIFICATION_CLICKED)) {\r\n\t\t\tlong[] ids = intent\r\n\t\t\t\t\t.getLongArrayExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS);\r\n\t\t\tif (ids.length > 0 && ids[0] == Preferences.getDownloadId(context)) {\r\n\t\t\t\tIntent downloadIntent = new Intent();\r\n\t\t\t\tdownloadIntent.setAction(DownloadManager.ACTION_VIEW_DOWNLOADS);\r\n\t\t\t\tdownloadIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK\r\n\t\t\t\t\t\t| Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);\r\n\t\t\t\tcontext.startActivity(downloadIntent);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/utils/Constants.java",
    "content": "package com.way.upgrade.utils;\r\n\r\n/**\r\n * \r\n * @author way 2014/4/28\r\n */\r\npublic class Constants {\r\n\t//public static final String BASE_URL = \"http://fir.im/api/v2/app/version/%s?token=%s\";\r\n\tpublic static final String BASE_URL = \"http://api.fir.im/apps/latest/%s?api_token=%s\";\r\n\r\n\tpublic static final String DOWNLOAD_FILE_PATH = \"/upgrade\";\r\n\r\n\tpublic static final int MSG_HAVA_NEW_VERSION = 1;\r\n\tpublic static final int MSG_NO_NEW_VERSION = 2;\r\n\tpublic static final int MSG_START_DOWNLOAD = 3;\r\n\tpublic static final int MSG_VERSION_RESULT = 4;\r\n\tpublic static final int MSG_NET_ERROR = 5;\r\n\r\n\tpublic static final int MUST_UPDATE = 1;\r\n\tpublic static final int NOT_MUST_UPDATE = 0;\r\n\r\n\tpublic static final String APK_SUFFIX = \".apk\";\r\n\r\n\tpublic static final Integer DOWNLOAD_STATUS_RUNNING = 1;\r\n\r\n\tpublic static final String ERROR_CODE_NET = \"net_error\";\r\n}\r\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/utils/ContextUtils.java",
    "content": "package com.way.upgrade.utils;\r\n\r\nimport java.lang.reflect.Method;\r\n\r\nimport android.app.AlertDialog;\r\nimport android.app.Dialog;\r\nimport android.app.ProgressDialog;\r\nimport android.content.ComponentName;\r\nimport android.content.Context;\r\nimport android.content.DialogInterface.OnClickListener;\r\nimport android.content.Intent;\r\nimport android.content.pm.PackageInfo;\r\nimport android.content.pm.PackageManager;\r\nimport android.content.pm.PackageManager.NameNotFoundException;\r\nimport android.content.pm.ResolveInfo;\r\nimport android.os.Build;\r\nimport android.util.Log;\r\nimport android.widget.Toast;\r\n\r\n/**\r\n * 上下文操作，如创建对话框，进度条,版本信息...\r\n * \r\n * @author way 2013/12/6\r\n */\r\npublic class ContextUtils {\r\n\tprivate static final String TAG = \"ContextUtils\";\r\n\r\n\t/**\r\n\t * Show a Toast(Toast.LENGTH_SHORT).\r\n\t * \r\n\t * @param text\r\n\t *            the content shown on the Toast.\r\n\t */\r\n\tpublic static void showToast(Context context, String text, int length) {\r\n\t\tToast.makeText(context, text, length).show();\r\n\t}\r\n\r\n\t/**\r\n\t * Show a Toast(Toast.LENGTH_SHORT).\r\n\t * \r\n\t * @param text\r\n\t *            the content shown on the Toast.\r\n\t */\r\n\tpublic static void showToast(Context context, int resId, int length) {\r\n\t\tToast.makeText(context, resId, length).show();\r\n\t}\r\n\r\n\tpublic static ProgressDialog createProgressDialog(Context context) {\r\n\t\tProgressDialog dialog = new ProgressDialog(context);\r\n\t\tdialog.setIndeterminate(true);\r\n\t\tdialog.setCancelable(true);\r\n\t\treturn dialog;\r\n\t}\r\n\r\n\t/**\r\n\t * show progress dialog\r\n\t * \r\n\t * @param context\r\n\t * @param title\r\n\t *            Dialog title\r\n\t * @param message\r\n\t *            Dialog message\r\n\t * @return\r\n\t */\r\n\tpublic static ProgressDialog showProgressDialog(Context context, int title,\r\n\t\t\tint message) {\r\n\t\tProgressDialog dialog = new ProgressDialog(context);\r\n\t\tdialog.setTitle(title);\r\n\t\tdialog.setMessage(context.getResources().getString(message));\r\n\t\tdialog.setIndeterminate(true);\r\n\t\tdialog.setCancelable(true);\r\n\t\tdialog.show();\r\n\t\treturn dialog;\r\n\t}\r\n\r\n\t/**\r\n\t * close progress dialog\r\n\t * \r\n\t * @param progressDialog\r\n\t */\r\n\tpublic static void closeProgressDialog(Dialog progressDialog) {\r\n\t\tif (progressDialog != null && progressDialog.isShowing()) {\r\n\t\t\tprogressDialog.cancel();\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * \r\n\t * @param context\r\n\t * @param title\r\n\t * @param message\r\n\t * @param buttonTexts\r\n\t * @param listeners\r\n\t * @return\r\n\t */\r\n\tpublic static AlertDialog.Builder showDialog(Context context, int title,\r\n\t\t\tint message, int[] buttonTexts, OnClickListener[] listeners) {\r\n\t\tAlertDialog.Builder dialog = new AlertDialog.Builder(context);\r\n\t\tdialog.setTitle(title);\r\n\t\tdialog.setMessage(message);\r\n\t\tif (buttonTexts.length == 1) {\r\n\t\t\tdialog.setNeutralButton(buttonTexts[0], listeners[0]);\r\n\t\t} else if (buttonTexts.length == 2) {\r\n\t\t\tdialog.setPositiveButton(buttonTexts[0], listeners[0]);\r\n\t\t\tdialog.setNegativeButton(buttonTexts[1], listeners[1]);\r\n\t\t} else if (buttonTexts.length == 3) {\r\n\t\t\tdialog.setPositiveButton(buttonTexts[0], listeners[0]);\r\n\t\t\tdialog.setNeutralButton(buttonTexts[1], listeners[1]);\r\n\t\t\tdialog.setNegativeButton(buttonTexts[2], listeners[2]);\r\n\t\t}\r\n\t\tdialog.show();\r\n\t\treturn dialog;\r\n\t}\r\n\r\n\t/**\r\n\t * \r\n\t * @param context\r\n\t * @param title\r\n\t * @param message\r\n\t * @param buttonTexts\r\n\t * @param listeners\r\n\t * @return\r\n\t */\r\n\tpublic static AlertDialog.Builder showDialog(Context context, String title,\r\n\t\t\tString message, int[] buttonTexts, OnClickListener[] listeners) {\r\n\t\tAlertDialog.Builder dialog = new AlertDialog.Builder(context);\r\n\t\tdialog.setTitle(title);\r\n\t\tdialog.setMessage(message);\r\n\t\tif (buttonTexts.length == 1) {\r\n\t\t\tdialog.setNeutralButton(buttonTexts[0], listeners[0]);\r\n\t\t} else if (buttonTexts.length == 2) {\r\n\t\t\tdialog.setPositiveButton(buttonTexts[0], listeners[0]);\r\n\t\t\tdialog.setNegativeButton(buttonTexts[1], listeners[1]);\r\n\t\t} else if (buttonTexts.length == 3) {\r\n\t\t\tdialog.setPositiveButton(buttonTexts[0], listeners[0]);\r\n\t\t\tdialog.setNeutralButton(buttonTexts[1], listeners[1]);\r\n\t\t\tdialog.setNegativeButton(buttonTexts[2], listeners[2]);\r\n\t\t}\r\n\t\tdialog.show();\r\n\t\treturn dialog;\r\n\t}\r\n\r\n\t/**\r\n\t * \r\n\t * @param context\r\n\t * @param title\r\n\t * @param message\r\n\t * @param buttonTexts\r\n\t * @param listeners\r\n\t * @return\r\n\t */\r\n\tpublic static AlertDialog showAlertDialog(Context context, String title,\r\n\t\t\tString message, int[] buttonTexts, OnClickListener[] listeners) {\r\n\t\tAlertDialog.Builder dialog = new AlertDialog.Builder(context);\r\n\t\tdialog.setTitle(title);\r\n\t\tdialog.setMessage(message);\r\n\t\tif (buttonTexts.length == 1) {\r\n\t\t\tdialog.setNeutralButton(buttonTexts[0], listeners[0]);\r\n\t\t} else if (buttonTexts.length == 2) {\r\n\t\t\tdialog.setPositiveButton(buttonTexts[0], listeners[0]);\r\n\t\t\tdialog.setNegativeButton(buttonTexts[1], listeners[1]);\r\n\t\t} else if (buttonTexts.length == 3) {\r\n\t\t\tdialog.setPositiveButton(buttonTexts[0], listeners[0]);\r\n\t\t\tdialog.setNeutralButton(buttonTexts[1], listeners[1]);\r\n\t\t\tdialog.setNegativeButton(buttonTexts[2], listeners[2]);\r\n\t\t}\r\n\t\treturn dialog.create();\r\n\t}\r\n\r\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\r\n\tpublic static String getCustomVersion() {\r\n\t\ttry {\r\n\t\t\tString className = \"android.os.SystemProperties\";\r\n\t\t\tString methodName = \"get\";\r\n\t\t\tString key = \"ro.custom.build.version\";\r\n\t\t\tClass clazz = Class.forName(className);\r\n\t\t\t// Constructor con = clazz.getEnclosingConstructor();\r\n\t\t\tMethod method = clazz.getDeclaredMethod(methodName, String.class);\r\n\t\t\treturn (String) method.invoke(clazz, key);\r\n\t\t} catch (Exception e) {\r\n\t\t\t// TODO Auto-generated catch block\r\n\t\t\te.printStackTrace();\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\t/**\r\n\t * 获取版本号\r\n\t * \r\n\t * @param context\r\n\t * @return\r\n\t */\r\n\tpublic static int getVersionCode(Context context) {\r\n\t\tint versionCode = -1;\r\n\t\ttry {\r\n\t\t\tPackageInfo info = context.getPackageManager().getPackageInfo(\r\n\t\t\t\t\tcontext.getPackageName(), 0);\r\n\t\t\tversionCode = info.versionCode;\r\n\t\t} catch (NameNotFoundException e) {\r\n\t\t\te.printStackTrace();\r\n\t\t\tLog.e(TAG, e.getMessage());\r\n\t\t}\r\n\t\treturn versionCode;\r\n\t}\r\n\r\n\t/**\r\n\t * 获取版本信息\r\n\t * \r\n\t * @param mContext\r\n\t * @return\r\n\t */\r\n\tpublic static String getVersionName(Context context) {\r\n\t\tString versionName = null;\r\n\t\ttry {\r\n\t\t\tPackageInfo info = context.getPackageManager().getPackageInfo(\r\n\t\t\t\t\tcontext.getPackageName(), 0);\r\n\t\t\tversionName = info.versionName;\r\n\t\t\t// int versionCode = info.versionCode;\r\n\t\t} catch (NameNotFoundException e) {\r\n\t\t\te.printStackTrace();\r\n\t\t\tLog.e(TAG, e.getMessage());\r\n\t\t}\r\n\t\treturn versionName;\r\n\t}\r\n\r\n\tpublic static String getUserAgent(Context context) {\r\n\t\tPackageInfo packageInfo;\r\n\t\ttry {\r\n\t\t\tpackageInfo = context.getPackageManager().getPackageInfo(\r\n\t\t\t\t\tcontext.getPackageName(), 0);\r\n\t\t} catch (NameNotFoundException e) {\r\n\t\t\tthrow new IllegalStateException(\"getPackageInfo failed\");\r\n\t\t}\r\n\t\treturn String.format(\"%s/%s; %s/%s/%s/%s; %s/%s/%s\",\r\n\t\t\t\tpackageInfo.packageName, packageInfo.versionName, Build.BRAND,\r\n\t\t\t\tBuild.DEVICE, Build.MODEL, Build.ID, Build.VERSION.SDK_INT,\r\n\t\t\t\tBuild.VERSION.RELEASE, Build.VERSION.INCREMENTAL);\r\n\t}\r\n\r\n\tpublic static Intent getActivityIntent(Context context, String packageName,\r\n\t\t\tString className) {\r\n\t\tPackageManager pm = context.getPackageManager();\r\n\t\tIntent intent = new Intent();\r\n\t\tComponentName compoentName = new ComponentName(packageName, className);\r\n\t\tintent.setComponent(compoentName);\r\n\t\tResolveInfo ri = pm.resolveActivity(intent,\r\n\t\t\t\tPackageManager.MATCH_DEFAULT_ONLY);\r\n\t\tif (ri != null) {\r\n\t\t\treturn intent;\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/utils/FileUtils.java",
    "content": "package com.way.upgrade.utils;\r\n\r\nimport java.io.File;\r\n\r\n/**\r\n * @author way 2013/12/6\r\n */\r\npublic class FileUtils {\r\n\tprivate static final String TAG = \"FileUtils\";\r\n\r\n\t/**\r\n\t * @param f\r\n\t * @throws Exception\r\n\t */\r\n\tpublic static void createNewFile(File f) throws Exception {\r\n\t\tLog.i(TAG, \"create file:\" + f, Log.APP);\r\n\t\tif (!f.getParentFile().exists()) {\r\n\t\t\tf.getParentFile().mkdirs();\r\n\t\t}\r\n\t\tif (!f.exists()) {\r\n\t\t\tf.createNewFile();\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/utils/Log.java",
    "content": "package com.way.upgrade.utils;\r\n\r\npublic class Log {\r\n\tpublic static final boolean DEBUG = true;\r\n\tpublic static final boolean DATA_DEBUG = true;\r\n\tpublic static final boolean APP_DEBUG = true;\r\n\tpublic static final boolean DISPLAY_DEBUG = false;\r\n\tpublic static final int DATA = 0;\r\n\tpublic static final int DISPLAY = 1;\r\n\tpublic static final int APP = 2;\r\n\r\n\tpublic static final String TAG = \"zhujianwen\";\r\n\r\n\tpublic static void v(String tag, String msg, int mod) {\r\n\t\tif (!DEBUG) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (mod == DATA && DATA_DEBUG) {\r\n\t\t\tandroid.util.Log.v(tag, msg);\r\n\t\t} else if (mod == DISPLAY && DISPLAY_DEBUG) {\r\n\t\t\tandroid.util.Log.v(tag, msg);\r\n\t\t} else if (mod == APP && APP_DEBUG) {\r\n\t\t\tandroid.util.Log.v(tag, msg);\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static void v(String msg, int mod) {\r\n\t\tv(TAG, msg, mod);\r\n\t}\r\n\r\n\tpublic static void d(String tag, String msg, int mod) {\r\n\t\tif (!DEBUG) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (mod == DATA && DATA_DEBUG) {\r\n\t\t\tandroid.util.Log.d(tag, msg);\r\n\t\t} else if (mod == DISPLAY && DISPLAY_DEBUG) {\r\n\t\t\tandroid.util.Log.d(tag, msg);\r\n\t\t} else if (mod == APP && APP_DEBUG) {\r\n\t\t\tandroid.util.Log.d(tag, msg);\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static void d(String msg, int mod) {\r\n\t\td(TAG, msg, mod);\r\n\t}\r\n\r\n\tpublic static void i(String tag, String msg, int mod) {\r\n\t\tif (!DEBUG) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (mod == DATA && DATA_DEBUG) {\r\n\t\t\tandroid.util.Log.i(tag, msg);\r\n\t\t} else if (mod == DISPLAY && DISPLAY_DEBUG) {\r\n\t\t\tandroid.util.Log.i(tag, msg);\r\n\t\t} else if (mod == APP && APP_DEBUG) {\r\n\t\t\tandroid.util.Log.i(tag, msg);\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static void i(String msg, int mod) {\r\n\t\ti(TAG, msg, mod);\r\n\t}\r\n\r\n\tpublic static void w(String tag, String msg, int mod) {\r\n\t\tif (!DEBUG) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (mod == DATA && DATA_DEBUG) {\r\n\t\t\tandroid.util.Log.w(tag, msg);\r\n\t\t} else if (mod == DISPLAY && DISPLAY_DEBUG) {\r\n\t\t\tandroid.util.Log.w(tag, msg);\r\n\t\t} else if (mod == APP && APP_DEBUG) {\r\n\t\t\tandroid.util.Log.w(tag, msg);\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static void w(String msg, int mod) {\r\n\t\tw(TAG, msg, mod);\r\n\t}\r\n\r\n\tpublic static void e(String tag, String msg, int mod) {\r\n\t\tif (!DEBUG) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (mod == DATA && DATA_DEBUG) {\r\n\t\t\tandroid.util.Log.e(tag, msg);\r\n\t\t} else if (mod == DISPLAY && DISPLAY_DEBUG) {\r\n\t\t\tandroid.util.Log.e(tag, msg);\r\n\t\t} else if (mod == APP && APP_DEBUG) {\r\n\t\t\tandroid.util.Log.e(tag, msg);\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static void e(String msg, int mod) {\r\n\t\te(TAG, msg, mod);\r\n\t}\r\n}\r\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/utils/NetUtils.java",
    "content": "package com.way.upgrade.utils;\r\n\r\nimport java.io.BufferedReader;\r\nimport java.io.DataOutputStream;\r\nimport java.io.File;\r\nimport java.io.FileInputStream;\r\nimport java.io.IOException;\r\nimport java.io.InputStream;\r\nimport java.io.InputStreamReader;\r\nimport java.net.HttpURLConnection;\r\nimport java.net.URL;\r\nimport java.util.Map;\r\n\r\nimport org.apache.http.HttpResponse;\r\nimport org.apache.http.client.ClientProtocolException;\r\nimport org.apache.http.client.HttpClient;\r\nimport org.apache.http.client.methods.HttpGet;\r\nimport org.apache.http.client.methods.HttpPost;\r\nimport org.apache.http.impl.client.DefaultHttpClient;\r\nimport org.apache.http.params.BasicHttpParams;\r\nimport org.apache.http.params.HttpConnectionParams;\r\nimport org.apache.http.params.HttpParams;\r\nimport org.json.JSONArray;\r\nimport org.json.JSONException;\r\nimport org.json.JSONObject;\r\n\r\nimport android.content.Context;\r\nimport android.net.ConnectivityManager;\r\nimport android.net.NetworkInfo;\r\n\r\n/**\r\n * 网络操作\r\n * \r\n * @author way 2013/12/6\r\n */\r\npublic class NetUtils {\r\n\tprivate static final String TAG = \"NetUtils\";\r\n\r\n\tprivate static final String NETTYPE_WIFI = \"WIFI\";\r\n\r\n\tprivate static String multipart_form_data = \"multipart/form-data\";\r\n\tprivate static String twoHyphens = \"--\";\r\n\tprivate static String boundary = java.util.UUID.randomUUID().toString(); // 数据分隔\r\n\tprivate static String lineEnd = \"\\r\\n\"; // The value is \"\\r\\n\" in Windows.\r\n\r\n\tpublic enum NETWORK_STATUS {\r\n\t\tSTATE_WIFI, STATE_GPRS, STATE_NONE_NETWORK\r\n\t}\r\n\r\n\tprivate NetUtils() {\r\n\t}\r\n\r\n\tpublic static NETWORK_STATUS getNetworkType(Context context) {\r\n\t\tNETWORK_STATUS ret = NETWORK_STATUS.STATE_NONE_NETWORK;\r\n\r\n\t\tConnectivityManager connetManager = (ConnectivityManager) context\r\n\t\t\t\t.getSystemService(Context.CONNECTIVITY_SERVICE);\r\n\t\tif (connetManager == null) {\r\n\t\t\tLog.e(TAG, \"isNetWorkAvailable connetManager = null\", Log.APP);\r\n\t\t\treturn ret;\r\n\t\t}\r\n\t\tNetworkInfo[] infos = connetManager.getAllNetworkInfo();\r\n\t\tif (infos == null) {\r\n\t\t\treturn ret;\r\n\t\t}\r\n\t\tfor (int i = 0; i < infos.length && infos[i] != null; i++) {\r\n\t\t\tif (infos[i].isConnected() && infos[i].isAvailable()) {\r\n\r\n\t\t\t\tif (infos[i].getTypeName().equalsIgnoreCase(NETTYPE_WIFI)) {\r\n\t\t\t\t\tret = NETWORK_STATUS.STATE_WIFI;\r\n\t\t\t\t} else {\r\n\t\t\t\t\tret = NETWORK_STATUS.STATE_GPRS;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tLog.i(TAG, \"get network stype is : \" + ret, Log.APP);\r\n\t\treturn ret;\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * \r\n\t * @param context\r\n\t * @param typeName\r\n\t *            (\"\",WIFI,MOBILE)\r\n\t * @return\r\n\t */\r\n\tpublic static boolean isNetWorkAvailable(Context context, String typeName) {\r\n\r\n\t\tLog.i(TAG, \">>> isNetWorkAvailable context = \" + context\r\n\t\t\t\t+ \"typeName = \" + typeName, Log.APP);\r\n\r\n\t\tboolean ret = false;\r\n\r\n\t\tConnectivityManager connetManager = (ConnectivityManager) context\r\n\t\t\t\t.getSystemService(Context.CONNECTIVITY_SERVICE);\r\n\t\tif (connetManager == null) {\r\n\t\t\tLog.e(TAG, \"isNetWorkAvailable connetManager = null\", Log.APP);\r\n\t\t\treturn ret;\r\n\t\t}\r\n\t\tNetworkInfo[] infos = connetManager.getAllNetworkInfo();\r\n\t\tif (infos == null) {\r\n\t\t\treturn ret;\r\n\t\t}\r\n\t\tif ((typeName == null) || (typeName.length() <= 0)) {\r\n\t\t\tfor (int i = 0; i < infos.length && infos[i] != null; i++) {\r\n\t\t\t\tif (infos[i].isConnected() && infos[i].isAvailable()) {\r\n\t\t\t\t\tret = true;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tfor (int i = 0; i < infos.length && infos[i] != null; i++) {\r\n\t\t\t\tif (infos[i].getTypeName().equalsIgnoreCase(typeName)\r\n\t\t\t\t\t\t&& infos[i].isConnected() && infos[i].isAvailable()) {\r\n\t\t\t\t\tLog.i(TAG,\r\n\t\t\t\t\t\t\t\"isNetWorkAvailable name is : \"\r\n\t\t\t\t\t\t\t\t\t+ infos[i].getTypeName(), Log.APP);\r\n\t\t\t\t\tret = true;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tLog.i(TAG, \"isNetWorkAvailable >>> result is : \" + ret, Log.APP);\r\n\t\treturn ret;\r\n\t}\r\n\r\n\t/**\r\n\t * \r\n\t * @param url\r\n\t * @return\r\n\t * @throws IOException\r\n\t */\r\n\tpublic static synchronized InputStream getInputStreamByGet(String url)\r\n\t\t\tthrows IOException {\r\n\t\tLog.i(TAG, \"<getInputStreamByGet> url:\" + url, Log.APP);\r\n\t\tHttpURLConnection httpConnection = null;\r\n\t\tint currentSize = 0;\r\n\t\tif (url == null) {\r\n\t\t\treturn null;\r\n\t\t}\r\n\t\tURL uri = new URL(url);\r\n\t\thttpConnection = (HttpURLConnection) uri.openConnection();\r\n\t\thttpConnection.setRequestProperty(\"User-Agent\", \"PacificHttpClient\");\r\n\t\tif (currentSize > 0) {\r\n\t\t\thttpConnection.setRequestProperty(\"RANGE\", \"bytes=\" + currentSize\r\n\t\t\t\t\t+ \"-\");\r\n\t\t}\r\n\t\t// 设置超时时间\r\n\t\thttpConnection.setConnectTimeout(10000);// 限制连接超时5秒钟\r\n\t\thttpConnection.setReadTimeout(2 * 10000);\r\n\t\thttpConnection.setRequestProperty(\"Content-type\",\r\n\t\t\t\t\"text/html;charset=UTF-8\");\r\n\r\n\t\thttpConnection.setDoOutput(true);\r\n\t\thttpConnection.setRequestMethod(\"GET\");\r\n\t\thttpConnection.setUseCaches(false);\r\n\t\tint requestCode = httpConnection.getResponseCode();\r\n\t\tif (requestCode == 200) {\r\n\t\t\tInputStream in = httpConnection.getInputStream();\r\n\t\t\treturn in;\r\n\t\t}\r\n\t\treturn null;\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * post方式从服务器获取json数组\r\n\t * \r\n\t * @return\r\n\t * @throws IOException\r\n\t * @throws ClientProtocolException\r\n\t * @throws JSONException\r\n\t */\r\n\tpublic static JSONArray getJSONArrayByPost(String uri)\r\n\t\t\tthrows ClientProtocolException, IOException, JSONException {\r\n\t\tLog.i(TAG, \"<getJSONArrayByPost> uri:\" + uri, Log.APP);\r\n\t\tStringBuilder builder = new StringBuilder();\r\n\t\tHttpParams httpParameters = new BasicHttpParams();\r\n\t\t// Set the default socket timeout (SO_TIMEOUT) in milliseconds which is\r\n\t\t// the timeout for waiting for data.\r\n\t\tHttpConnectionParams.setConnectionTimeout(httpParameters, 5000);\r\n\t\tHttpConnectionParams.setSoTimeout(httpParameters, 10000);\r\n\t\tHttpClient client = new DefaultHttpClient(httpParameters);\r\n\t\tHttpPost post = new HttpPost(uri);\r\n\r\n\t\tHttpResponse response = client.execute(post);\r\n\r\n\t\tBufferedReader reader = new BufferedReader(new InputStreamReader(\r\n\t\t\t\tresponse.getEntity().getContent()));\r\n\t\tfor (String s = reader.readLine(); s != null; s = reader.readLine()) {\r\n\t\t\tbuilder.append(s);\r\n\t\t}\r\n\r\n\t\tString jsonString = new String(builder.toString());\r\n\r\n\t\tif (\"{}\".equals(jsonString)) {\r\n\t\t\treturn null;\r\n\t\t}\r\n\t\tLog.i(TAG, \"<getJSONArrayByPost> jsonString:\" + jsonString, Log.DATA);\r\n\t\treturn new JSONArray(jsonString);\r\n\t}\r\n\r\n\t/**\r\n\t * get方式从服务器获取json数组\r\n\t * \r\n\t * @return\r\n\t * @throws IOException\r\n\t * @throws ClientProtocolException\r\n\t * @throws JSONException\r\n\t */\r\n\tpublic static JSONObject getJSONArrayByGet(String uri)\r\n\t\t\tthrows ClientProtocolException, IOException, JSONException {\r\n\t\tLog.i(TAG, \"<getJSONArrayByGet> uri:\" + uri, Log.APP);\r\n\t\tStringBuilder builder = new StringBuilder();\r\n\t\tHttpClient client = new DefaultHttpClient();\r\n\t\tHttpGet get = new HttpGet(uri);\r\n\r\n\t\tHttpResponse response = client.execute(get);\r\n\r\n\t\tBufferedReader reader = new BufferedReader(new InputStreamReader(\r\n\t\t\t\tresponse.getEntity().getContent()));\r\n\t\tfor (String s = reader.readLine(); s != null; s = reader.readLine()) {\r\n\t\t\tbuilder.append(s);\r\n\t\t}\r\n\r\n\t\tString jsonString = new String(builder.toString());\r\n\r\n\t\tif (\"{}\".equals(jsonString)) {\r\n\t\t\treturn null;\r\n\t\t}\r\n\t\tLog.i(TAG, \"<getJSONArrayByGet> jsonString:\" + jsonString, Log.DATA);\r\n\t\treturn new JSONObject(jsonString);\r\n\t}\r\n\r\n\t/**\r\n\t * 使用post的方式，提交表单，不包括文件上传(新服务器)\r\n\t * \r\n\t * @param actionUrl\r\n\t * @param params\r\n\t * @param files\r\n\t * @return\r\n\t * @throws IOException\r\n\t */\r\n\tpublic static boolean uploadParamsByPost(String actionUrl,\r\n\t\t\tMap<String, String> params) throws IOException {\r\n\t\tLog.i(TAG, \"<uploadParamsByPost> actionUrl:\" + actionUrl + \" params:\"\r\n\t\t\t\t+ params, Log.APP);\r\n\t\tString BOUNDARY = java.util.UUID.randomUUID().toString();\r\n\t\tString PREFIX = \"--\", LINEND = \"\\r\\n\";\r\n\t\tString MULTIPART_FROM_DATA = \"multipart/form-data\";\r\n\t\tString CHARSET = \"UTF-8\";\r\n\r\n\t\tURL uri = new URL(actionUrl);\r\n\t\tHttpURLConnection conn = (HttpURLConnection) uri.openConnection();\r\n\r\n\t\tconn.setReadTimeout(10 * 1000);\r\n\t\tconn.setConnectTimeout(10 * 1000);\r\n\t\tconn.setDoInput(true);// 允许输入\r\n\t\tconn.setDoOutput(true);// 允许输出\r\n\t\tconn.setUseCaches(false);\r\n\t\tconn.setRequestMethod(\"POST\"); // Post方式\r\n\t\tconn.setRequestProperty(\"connection\", \"keep-alive\");\r\n\t\tconn.setRequestProperty(\"Charsert\", \"UTF-8\");\r\n\r\n\t\tconn.setRequestProperty(\"Content-Type\", MULTIPART_FROM_DATA\r\n\t\t\t\t+ \";boundary=\" + BOUNDARY);\r\n\r\n\t\t// 首先组拼文本类型的参数\r\n\t\tStringBuilder sb = new StringBuilder();\r\n\t\tfor (Map.Entry<String, String> entry : params.entrySet()) {\r\n\t\t\tsb.append(PREFIX);\r\n\t\t\tsb.append(BOUNDARY);\r\n\t\t\tsb.append(LINEND);\r\n\t\t\tsb.append(\"Content-Disposition: form-data; name=\\\"\"\r\n\t\t\t\t\t+ entry.getKey() + \"\\\"\" + LINEND);\r\n\t\t\tsb.append(\"Content-Type: text/plain; charset=\" + CHARSET + LINEND);\r\n\t\t\tsb.append(\"Content-Transfer-Encoding: 8bit\" + LINEND);\r\n\t\t\tsb.append(LINEND);\r\n\t\t\tsb.append(entry.getValue());\r\n\t\t\tsb.append(LINEND);\r\n\t\t}\r\n\r\n\t\tDataOutputStream outStream = new DataOutputStream(\r\n\t\t\t\tconn.getOutputStream());\r\n\t\toutStream.write(sb.toString().getBytes());\r\n\r\n\t\t// 请求结束标志\r\n\t\tbyte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINEND).getBytes();\r\n\t\toutStream.write(end_data);\r\n\t\toutStream.flush();\r\n\r\n\t\toutStream.close();\r\n\t\t// 得到响应号\r\n\t\tint res = conn.getResponseCode();\r\n\t\tif (res == 200) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\tconn.disconnect();\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\t/**\r\n\t * 使用post的方式，提交表单，不包括文件上传(老服务器写的代码使用这种方式)\r\n\t * \r\n\t * @param actionUrl\r\n\t *            :http://xxx/xxx.json\r\n\t * @param query\r\n\t *            :Helpers.combinaStr(\"login_name=#&password=#&email=#&name=\",\r\n\t *            listParams);\r\n\t * @return\r\n\t */\r\n\tpublic static JSONObject uploadParamsByPost(String actionUrl, String query) {\r\n\t\tLog.i(TAG, \"<uploadParamsByPost> actionUrl:\" + actionUrl + \" query:\"\r\n\t\t\t\t+ query, Log.APP);\r\n\t\ttry {\r\n\t\t\tURL uri = new URL(actionUrl);\r\n\t\t\tHttpURLConnection conn = (HttpURLConnection) uri.openConnection();\r\n\r\n\t\t\tconn.setReadTimeout(10 * 1000);\r\n\t\t\tconn.setDoInput(true);// 允许输入\r\n\t\t\tconn.setDoOutput(true);// 允许输出\r\n\t\t\tconn.setUseCaches(false);\r\n\t\t\tconn.setRequestMethod(\"POST\"); // Post方式\r\n\t\t\tconn.setRequestProperty(\"connection\", \"keep-alive\");\r\n\t\t\tconn.setRequestProperty(\"Charsert\", \"UTF-8\");\r\n\r\n\t\t\t// query is your body\r\n\t\t\tconn.setRequestProperty(\"Content-Type\",\r\n\t\t\t\t\t\"application/x-www-form-urlencoded\");// 请求�?\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// 必须设置\r\n\t\t\tconn.setRequestProperty(\"Content-Length\", query.toString()\r\n\t\t\t\t\t.getBytes(\"UTF-8\").length + \"\");// 注意是字节长�?\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t// 不是字符长度\r\n\t\t\tconn.getOutputStream().write(query.toString().getBytes(\"UTF-8\"));\r\n\t\t\t// 得到响应�?\r\n\t\t\tint res = conn.getResponseCode();\r\n\r\n\t\t\tif (res == HttpURLConnection.HTTP_OK) {\r\n\t\t\t\tStringBuffer stringBuffer = new StringBuffer();\r\n\t\t\t\tString readLine;\r\n\t\t\t\tBufferedReader responseReader;\r\n\t\t\t\t// 处理响应流，必须与服务器响应流输出的编码�?��\r\n\t\t\t\tresponseReader = new BufferedReader(new InputStreamReader(\r\n\t\t\t\t\t\tconn.getInputStream(), \"UTF-8\"));\r\n\t\t\t\twhile ((readLine = responseReader.readLine()) != null) {\r\n\t\t\t\t\tstringBuffer.append(readLine).append(\"/n\");\r\n\t\t\t\t}\r\n\t\t\t\tresponseReader.close();\r\n\r\n\t\t\t\treturn new JSONObject(stringBuffer.toString());\r\n\t\t\t}\r\n\r\n\t\t} catch (Exception e) {\r\n\t\t\te.printStackTrace();\r\n\t\t}\r\n\r\n\t\treturn null;\r\n\t}\r\n\r\n\tpublic int uploadFilesByPost(String actionUrl, String fileName, File file) {\r\n\t\tLog.i(TAG, \"<uploadFilesByPost> actionUrl:\" + actionUrl + \" fileName:\"\r\n\t\t\t\t+ fileName, Log.APP);\r\n\t\tString CHARSET = \"UTF-8\";\r\n\r\n\t\t// 得到响应�?\r\n\t\tint res = 0;\r\n\t\ttry {\r\n\t\t\tURL uri = new URL(actionUrl);\r\n\t\t\tHttpURLConnection conn = (HttpURLConnection) uri.openConnection();\r\n\t\t\tconn.setReadTimeout(10 * 1000);\r\n\t\t\tconn.setDoInput(true);// 允许输入\r\n\t\t\tconn.setDoOutput(true);// 允许输出\r\n\t\t\tconn.setUseCaches(false);\r\n\t\t\tconn.setRequestMethod(\"POST\"); // Post方式\r\n\t\t\tconn.setRequestProperty(\"connection\", \"keep-alive\");\r\n\t\t\tconn.setRequestProperty(\"Charsert\", \"UTF-8\");\r\n\r\n\t\t\tconn.setRequestProperty(\"Content-Type\", multipart_form_data\r\n\t\t\t\t\t+ \";boundary=\" + boundary);\r\n\r\n\t\t\t// 输出�?\r\n\t\t\tDataOutputStream outStream = new DataOutputStream(\r\n\t\t\t\t\tconn.getOutputStream());\r\n\r\n\t\t\t// 发�?文件数据\r\n\t\t\tif (file != null) {\r\n\t\t\t\tStringBuilder sb1 = new StringBuilder();\r\n\t\t\t\tsb1.append(twoHyphens);\r\n\t\t\t\tsb1.append(boundary);\r\n\t\t\t\tsb1.append(lineEnd);\r\n\t\t\t\t// actionData 是自己定义的\r\n\t\t\t\tsb1.append(\"Content-Disposition: form-data; name=\\\"actionData\\\"; filename=\\\"\"\r\n\t\t\t\t\t\t+ fileName + \"\\\"\" + lineEnd);\r\n\t\t\t\tsb1.append(\"Content-Type: application/octet-stream; charset=\"\r\n\t\t\t\t\t\t+ CHARSET + lineEnd);\r\n\t\t\t\tsb1.append(lineEnd);\r\n\t\t\t\toutStream.write(sb1.toString().getBytes());\r\n\r\n\t\t\t\tInputStream is = new FileInputStream(file);\r\n\t\t\t\tbyte[] buffer = new byte[1024];\r\n\t\t\t\tint len = 0;\r\n\t\t\t\twhile ((len = is.read(buffer)) != -1) {\r\n\t\t\t\t\toutStream.write(buffer, 0, len);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tis.close();\r\n\t\t\t\toutStream.write(lineEnd.getBytes());\r\n\t\t\t}\r\n\r\n\t\t\t// 请求结束标志\r\n\t\t\tbyte[] end_data = (twoHyphens + boundary + twoHyphens + lineEnd)\r\n\t\t\t\t\t.getBytes();\r\n\r\n\t\t\toutStream.write(end_data);\r\n\t\t\toutStream.flush();\r\n\t\t\tres = conn.getResponseCode();\r\n\r\n\t\t\toutStream.close();\r\n\t\t\tconn.disconnect();\r\n\t\t} catch (IOException e) {\r\n\t\t\te.printStackTrace();\r\n\t\t}\r\n\t\treturn res;\r\n\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/utils/Preferences.java",
    "content": "package com.way.upgrade.utils;\r\n\r\nimport android.content.Context;\r\nimport android.content.SharedPreferences;\r\n\r\nimport com.way.upgrade.bean.UpgradeInfo;\r\n\r\n/**\r\n * \r\n * @author way 2014/11/3\r\n */\r\npublic class Preferences {\r\n\tpublic static final String PREFERENCES_NAME = \"com_upgrade_manager\";\r\n\tpublic static final String KEY_DOWNLOAD_ID = \"downloadId\";\r\n\tprotected static final String KEY_APPKEY = \"key\";\r\n\tprotected static final String KEY_VERSION = \"version\";\r\n\tprotected static final String KEY_URL = \"url\";\r\n\tprotected static final String KEY_DESCRIPTION = \"description\";\r\n\tprotected static final String KEY_DOWNLOAD_SIZE = \"downloadSize\";\r\n\tprotected static final String KEY_DOWNLOAD_PATH = \"download_path\";\r\n\tprotected static final String KEY_DOWNLOAD_STATUS = \"download_status\";\r\n\r\n\tpublic static void setDownloadId(Context context, long downloadId) {\r\n\t\tSharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME,\r\n\t\t\t\tContext.MODE_APPEND);\r\n\t\tSharedPreferences.Editor editor = pref.edit();\r\n\t\teditor.putLong(KEY_DOWNLOAD_ID, downloadId);\r\n\t\teditor.commit();\r\n\t}\r\n\r\n\tpublic static long getDownloadId(Context context) {\r\n\t\tSharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME,\r\n\t\t\t\tContext.MODE_APPEND);\r\n\t\treturn pref.getLong(KEY_DOWNLOAD_ID, -1);\r\n\t}\r\n\r\n\tpublic static void setUpgradeInfo(Context context, UpgradeInfo upgrade) {\r\n\t\tSharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME,\r\n\t\t\t\tContext.MODE_APPEND);\r\n\t\tSharedPreferences.Editor editor = pref.edit();\r\n\t\teditor.putString(KEY_APPKEY, upgrade.getKey());\r\n\t\teditor.putString(KEY_VERSION, upgrade.getVersion());\r\n\t\teditor.putString(KEY_URL, upgrade.getUrl());\r\n\t\teditor.putString(KEY_DESCRIPTION, upgrade.getDescription());\r\n\t\teditor.putLong(KEY_DOWNLOAD_SIZE, upgrade.getDownloadSize());\r\n\t\teditor.commit();\r\n\t}\r\n\r\n\tpublic static UpgradeInfo getUpgradeInfo(Context context) {\r\n\t\tUpgradeInfo upgradeInfo = new UpgradeInfo();\r\n\t\tSharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME,\r\n\t\t\t\tContext.MODE_APPEND);\r\n\t\tupgradeInfo.setKey(pref.getString(KEY_APPKEY, \"\"));\r\n\t\tupgradeInfo.setVersion(pref.getString(KEY_VERSION, \"\"));\r\n\t\tupgradeInfo.setUrl(pref.getString(KEY_URL, \"\"));\r\n\t\tupgradeInfo.setDescription(pref.getString(KEY_DESCRIPTION, \"\"));\r\n\t\tupgradeInfo.setDownloadSize(pref.getLong(KEY_DOWNLOAD_SIZE, -1));\r\n\t\treturn upgradeInfo;\r\n\t}\r\n\r\n\tpublic static void setDownloadPath(Context context, String downloadPath) {\r\n\t\tSharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME,\r\n\t\t\t\tContext.MODE_APPEND);\r\n\t\tSharedPreferences.Editor editor = pref.edit();\r\n\t\teditor.putString(KEY_DOWNLOAD_PATH, downloadPath);\r\n\t\teditor.commit();\r\n\t}\r\n\r\n\tpublic static String getDownloadPath(Context context) {\r\n\t\tSharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME,\r\n\t\t\t\tContext.MODE_APPEND);\r\n\t\treturn pref.getString(KEY_DOWNLOAD_PATH, \"\");\r\n\t}\r\n\r\n\tpublic static void removeAll(Context context) {\r\n\t\tSharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME,\r\n\t\t\t\tContext.MODE_APPEND);\r\n\t\tSharedPreferences.Editor editor = pref.edit();\r\n\t\teditor.clear();\r\n\t\teditor.commit();\r\n\t}\r\n\r\n\tpublic static void setDownloadStatus(Context context, int downloadId) {\r\n\t\tSharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME,\r\n\t\t\t\tContext.MODE_APPEND);\r\n\t\tSharedPreferences.Editor editor = pref.edit();\r\n\t\teditor.putInt(KEY_DOWNLOAD_STATUS, downloadId);\r\n\t\teditor.commit();\r\n\t}\r\n\r\n\tpublic static int getDownloadStatus(Context context) {\r\n\t\tSharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME,\r\n\t\t\t\tContext.MODE_APPEND);\r\n\t\treturn pref.getInt(KEY_DOWNLOAD_STATUS, -1);\r\n\t}\r\n}\r\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/utils/Utils.java",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.way.upgrade.utils;\n\nimport java.io.Closeable;\nimport java.io.InterruptedIOException;\nimport java.util.List;\n\nimport android.database.Cursor;\nimport android.os.Build;\nimport android.os.ParcelFileDescriptor;\nimport android.text.TextUtils;\n\npublic class Utils {\n\tprivate static final String TAG = \"Utils\";\n\tprivate static final String DEBUG_TAG = \"GalleryDebug\";\n\n\tprivate static final long POLY64REV = 0x95AC9329AC4BC9B5L;\n\tprivate static final long INITIALCRC = 0xFFFFFFFFFFFFFFFFL;\n\n\tprivate static long[] sCrcTable = new long[256];\n\n\tprivate static final boolean IS_DEBUG_BUILD = Build.TYPE.equals(\"eng\")\n\t\t\t|| Build.TYPE.equals(\"userdebug\");\n\n\tprivate static final String MASK_STRING = \"********************************\";\n\n\t// Throws AssertionError if the input is false.\n\tpublic static void assertTrue(boolean cond) {\n\t\tif (!cond) {\n\t\t\tthrow new AssertionError();\n\t\t}\n\t}\n\n\t// Throws AssertionError with the message. We had a method having the form\n\t// assertTrue(boolean cond, String message, Object ... args);\n\t// However a call to that method will cause memory allocation even if the\n\t// condition is false (due to autoboxing generated by \"Object ... args\"),\n\t// so we don't use that anymore.\n\tpublic static void fail(String message, Object... args) {\n\t\tthrow new AssertionError(args.length == 0 ? message : String.format(\n\t\t\t\tmessage, args));\n\t}\n\n\t// Throws NullPointerException if the input is null.\n\tpublic static <T> T checkNotNull(T object) {\n\t\tif (object == null)\n\t\t\tthrow new NullPointerException();\n\t\treturn object;\n\t}\n\n\t// Returns true if two input Object are both null or equal\n\t// to each other.\n\tpublic static boolean equals(Object a, Object b) {\n\t\treturn (a == b) || (a == null ? false : a.equals(b));\n\t}\n\n\t// Returns the next power of two.\n\t// Returns the input if it is already power of 2.\n\t// Throws IllegalArgumentException if the input is <= 0 or\n\t// the answer overflows.\n\tpublic static int nextPowerOf2(int n) {\n\t\tif (n <= 0 || n > (1 << 30))\n\t\t\tthrow new IllegalArgumentException(\"n is invalid: \" + n);\n\t\tn -= 1;\n\t\tn |= n >> 16;\n\t\tn |= n >> 8;\n\t\tn |= n >> 4;\n\t\tn |= n >> 2;\n\t\tn |= n >> 1;\n\t\treturn n + 1;\n\t}\n\n\t// Returns the previous power of two.\n\t// Returns the input if it is already power of 2.\n\t// Throws IllegalArgumentException if the input is <= 0\n\tpublic static int prevPowerOf2(int n) {\n\t\tif (n <= 0)\n\t\t\tthrow new IllegalArgumentException();\n\t\treturn Integer.highestOneBit(n);\n\t}\n\n\t// Returns the input value x clamped to the range [min, max].\n\tpublic static int clamp(int x, int min, int max) {\n\t\tif (x > max)\n\t\t\treturn max;\n\t\tif (x < min)\n\t\t\treturn min;\n\t\treturn x;\n\t}\n\n\t// Returns the input value x clamped to the range [min, max].\n\tpublic static float clamp(float x, float min, float max) {\n\t\tif (x > max)\n\t\t\treturn max;\n\t\tif (x < min)\n\t\t\treturn min;\n\t\treturn x;\n\t}\n\n\t// Returns the input value x clamped to the range [min, max].\n\tpublic static long clamp(long x, long min, long max) {\n\t\tif (x > max)\n\t\t\treturn max;\n\t\tif (x < min)\n\t\t\treturn min;\n\t\treturn x;\n\t}\n\n\tpublic static boolean isOpaque(int color) {\n\t\treturn color >>> 24 == 0xFF;\n\t}\n\n\tpublic static void swap(int[] array, int i, int j) {\n\t\tint temp = array[i];\n\t\tarray[i] = array[j];\n\t\tarray[j] = temp;\n\t}\n\n\t/**\n\t * A function thats returns a 64-bit crc for string\n\t * \n\t * @param in\n\t *            input string\n\t * @return a 64-bit crc value\n\t */\n\tpublic static final long crc64Long(String in) {\n\t\tif (in == null || in.length() == 0) {\n\t\t\treturn 0;\n\t\t}\n\t\treturn crc64Long(getBytes(in));\n\t}\n\n\tstatic {\n\t\t// http://bioinf.cs.ucl.ac.uk/downloads/crc64/crc64.c\n\t\tlong part;\n\t\tfor (int i = 0; i < 256; i++) {\n\t\t\tpart = i;\n\t\t\tfor (int j = 0; j < 8; j++) {\n\t\t\t\tlong x = ((int) part & 1) != 0 ? POLY64REV : 0;\n\t\t\t\tpart = (part >> 1) ^ x;\n\t\t\t}\n\t\t\tsCrcTable[i] = part;\n\t\t}\n\t}\n\n\tpublic static final long crc64Long(byte[] buffer) {\n\t\tlong crc = INITIALCRC;\n\t\tfor (int k = 0, n = buffer.length; k < n; ++k) {\n\t\t\tcrc = sCrcTable[(((int) crc) ^ buffer[k]) & 0xff] ^ (crc >> 8);\n\t\t}\n\t\treturn crc;\n\t}\n\n\tpublic static byte[] getBytes(String in) {\n\t\tbyte[] result = new byte[in.length() * 2];\n\t\tint output = 0;\n\t\tfor (char ch : in.toCharArray()) {\n\t\t\tresult[output++] = (byte) (ch & 0xFF);\n\t\t\tresult[output++] = (byte) (ch >> 8);\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic static void closeSilently(Closeable c) {\n\t\tif (c == null)\n\t\t\treturn;\n\t\ttry {\n\t\t\tc.close();\n\t\t} catch (Throwable t) {\n\t\t\tLog.w(TAG, \"close fail\", Log.APP);\n\t\t}\n\t}\n\n\tpublic static int compare(long a, long b) {\n\t\treturn a < b ? -1 : a == b ? 0 : 1;\n\t}\n\n\tpublic static int ceilLog2(float value) {\n\t\tint i;\n\t\tfor (i = 0; i < 31; i++) {\n\t\t\tif ((1 << i) >= value)\n\t\t\t\tbreak;\n\t\t}\n\t\treturn i;\n\t}\n\n\tpublic static int floorLog2(float value) {\n\t\tint i;\n\t\tfor (i = 0; i < 31; i++) {\n\t\t\tif ((1 << i) > value)\n\t\t\t\tbreak;\n\t\t}\n\t\treturn i - 1;\n\t}\n\n\tpublic static void closeSilently(ParcelFileDescriptor fd) {\n\t\ttry {\n\t\t\tif (fd != null)\n\t\t\t\tfd.close();\n\t\t} catch (Throwable t) {\n\t\t\tLog.w(TAG, \"fail to close\", Log.APP);\n\t\t}\n\t}\n\n\tpublic static void closeSilently(Cursor cursor) {\n\t\ttry {\n\t\t\tif (cursor != null)\n\t\t\t\tcursor.close();\n\t\t} catch (Throwable t) {\n\t\t\tLog.w(TAG, \"fail to close\", Log.APP);\n\t\t}\n\t}\n\n\tpublic static float interpolateAngle(float source, float target,\n\t\t\tfloat progress) {\n\t\t// interpolate the angle from source to target\n\t\t// We make the difference in the range of [-179, 180], this is the\n\t\t// shortest path to change source to target.\n\t\tfloat diff = target - source;\n\t\tif (diff < 0)\n\t\t\tdiff += 360f;\n\t\tif (diff > 180)\n\t\t\tdiff -= 360f;\n\n\t\tfloat result = source + diff * progress;\n\t\treturn result < 0 ? result + 360f : result;\n\t}\n\n\tpublic static float interpolateScale(float source, float target,\n\t\t\tfloat progress) {\n\t\treturn source + progress * (target - source);\n\t}\n\n\tpublic static String ensureNotNull(String value) {\n\t\treturn value == null ? \"\" : value;\n\t}\n\n\tpublic static String ensureNotNull(Object obj) {\n\t\treturn obj == null ? \"\" : obj.toString();\n\t}\n\n\tpublic static float parseFloatSafely(String content, float defaultValue) {\n\t\tif (content == null)\n\t\t\treturn defaultValue;\n\t\ttry {\n\t\t\treturn Float.parseFloat(content);\n\t\t} catch (NumberFormatException e) {\n\t\t\treturn defaultValue;\n\t\t}\n\t}\n\n\tpublic static int parseIntSafely(String content, int defaultValue) {\n\t\tif (content == null)\n\t\t\treturn defaultValue;\n\t\ttry {\n\t\t\treturn Integer.parseInt(content);\n\t\t} catch (NumberFormatException e) {\n\t\t\treturn defaultValue;\n\t\t}\n\t}\n\n\tpublic static boolean isNullOrEmpty(String exifMake) {\n\t\treturn TextUtils.isEmpty(exifMake);\n\t}\n\n\tpublic static void waitWithoutInterrupt(Object object) {\n\t\ttry {\n\t\t\tobject.wait();\n\t\t} catch (InterruptedException e) {\n\t\t\tLog.w(TAG, \"unexpected interrupt: \" + object, Log.APP);\n\t\t}\n\t}\n\n\tpublic static boolean handleInterrruptedException(Throwable e) {\n\t\t// A helper to deal with the interrupt exception\n\t\t// If an interrupt detected, we will setup the bit again.\n\t\tif (e instanceof InterruptedIOException\n\t\t\t\t|| e instanceof InterruptedException) {\n\t\t\tThread.currentThread().interrupt();\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * @return String with special XML characters escaped.\n\t */\n\tpublic static String escapeXml(String s) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (int i = 0, len = s.length(); i < len; ++i) {\n\t\t\tchar c = s.charAt(i);\n\t\t\tswitch (c) {\n\t\t\tcase '<':\n\t\t\t\tsb.append(\"&lt;\");\n\t\t\t\tbreak;\n\t\t\tcase '>':\n\t\t\t\tsb.append(\"&gt;\");\n\t\t\t\tbreak;\n\t\t\tcase '\\\"':\n\t\t\t\tsb.append(\"&quot;\");\n\t\t\t\tbreak;\n\t\t\tcase '\\'':\n\t\t\t\tsb.append(\"&#039;\");\n\t\t\t\tbreak;\n\t\t\tcase '&':\n\t\t\t\tsb.append(\"&amp;\");\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tsb.append(c);\n\t\t\t}\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic static String[] copyOf(String[] source, int newSize) {\n\t\tString[] result = new String[newSize];\n\t\tnewSize = Math.min(source.length, newSize);\n\t\tSystem.arraycopy(source, 0, result, 0, newSize);\n\t\treturn result;\n\t}\n\n\t// Mask information for debugging only. It returns\n\t// <code>info.toString()</code> directly\n\t// for debugging build (i.e., 'eng' and 'userdebug') and returns a mask\n\t// (\"****\")\n\t// in release build to protect the information (e.g. for privacy issue).\n\tpublic static String maskDebugInfo(Object info) {\n\t\tif (info == null)\n\t\t\treturn null;\n\t\tString s = info.toString();\n\t\tint length = Math.min(s.length(), MASK_STRING.length());\n\t\treturn IS_DEBUG_BUILD ? s : MASK_STRING.substring(0, length);\n\t}\n\n\t// This method should be ONLY used for debugging.\n\tpublic static void debug(String message, Object... args) {\n\t\tLog.v(DEBUG_TAG, String.format(message, args), Log.APP);\n\t}\n\n\t/**\n\t * combina the url. usr the param list repleace the [#] in the url.\n\t * \n\t * @param str\n\t * @param params\n\t * @return\n\t * @exception RuntimeException\n\t *                May throw the runtime exception while the method was done.\n\t * @author MichaelHuang\n\t */\n\tpublic static String combinaStr(String str, List<String> params)\n\t\t\tthrows RuntimeException {\n\t\ttry {\n\t\t\tString[] strs = str.split(\"[#]\");\n\t\t\tString newStr = \"\";\n\t\t\tfor (int i = 0; i < strs.length; i++) {\n\t\t\t\tString itemUrl = strs[i];\n\t\t\t\tnewStr += itemUrl;\n\t\t\t\tif (i >= 0 && i < params.size())\n\t\t\t\t\tnewStr += params.get(i);\n\t\t\t}\n\t\t\tLog.i(TAG, \"<combinaStr> newStr:\" + newStr, Log.DATA);\n\t\t\treturn newStr;\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/utils/thread/Future.java",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.way.upgrade.utils.thread;\n\n// This Future differs from the java.util.concurrent.Future in these aspects:\n//\n// - Once cancel() is called, isCancelled() always returns true. It is a sticky\n//   flag used to communicate to the implementation. The implmentation may\n//   ignore that flag. Regardless whether the Future is cancelled, a return\n//   value will be provided to get(). The implementation may choose to return\n//   null if it finds the Future is cancelled.\n//\n// - get() does not throw exceptions.\n//\npublic interface Future<T> {\n\tpublic void cancel();\n\n\tpublic boolean isCancelled();\n\n\tpublic boolean isDone();\n\n\tpublic T get();\n\n\tpublic void waitDone();\n}\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/utils/thread/FutureListener.java",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.way.upgrade.utils.thread;\n/**\n * \n * @author way\n *\n * @param <T>\n */\npublic interface FutureListener<T> {\n\tpublic void onFutureDone(Future<T> future);\n}\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/utils/thread/PriorityThreadFactory.java",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.way.upgrade.utils.thread;\n\nimport android.os.Process;\n\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * A thread factory that creates threads with a given thread priority.\n */\npublic class PriorityThreadFactory implements ThreadFactory {\n\n\tprivate final int mPriority;\n\tprivate final AtomicInteger mNumber = new AtomicInteger();\n\tprivate final String mName;\n\n\tpublic PriorityThreadFactory(String name, int priority) {\n\t\tmName = name;\n\t\tmPriority = priority;\n\t}\n\n\t@Override\n\tpublic Thread newThread(Runnable r) {\n\t\treturn new Thread(r, mName + '-' + mNumber.getAndIncrement()) {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tProcess.setThreadPriority(mPriority);\n\t\t\t\tsuper.run();\n\t\t\t}\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "Upgrade-lib/src/com/way/upgrade/utils/thread/ThreadPool.java",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.way.upgrade.utils.thread;\n\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport com.way.upgrade.utils.Log;\n/**\n * \n * @author way\n *\n */\npublic class ThreadPool {\n\tprivate static final String TAG = \"ThreadPool\";\n\tprivate static final int CORE_POOL_SIZE = 4;\n\tprivate static final int MAX_POOL_SIZE = 8;\n\tprivate static final int KEEP_ALIVE_TIME = 20; // 10 seconds\n\n\t// Resource type\n\tpublic static final int MODE_NONE = 0;\n\tpublic static final int MODE_CPU = 1;\n\tpublic static final int MODE_NETWORK = 2;\n\n\tpublic static final JobContext JOB_CONTEXT_STUB = new JobContextStub();\n\n\t// M: in order to take full advantage of MT6589 CPU Core, change cpuCounter\n\t// to 4 from 2\n\tResourceCounter mCpuCounter = new ResourceCounter(4);\n\tResourceCounter mNetworkCounter = new ResourceCounter(6);\n\n\t// A Job is like a Callable, but it has an addition JobContext parameter.\n\tpublic interface Job<T> {\n\t\tpublic T run(JobContext jc);\n\t}\n\n\tpublic interface JobContext {\n\t\tboolean isCancelled();\n\n\t\tvoid setCancelListener(CancelListener listener);\n\n\t\tboolean setMode(int mode);\n\t}\n\n\tprivate static class JobContextStub implements JobContext {\n\t\t@Override\n\t\tpublic boolean isCancelled() {\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic void setCancelListener(CancelListener listener) {\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean setMode(int mode) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tpublic interface CancelListener {\n\t\tpublic void onCancel();\n\t}\n\n\tprivate static class ResourceCounter {\n\t\tpublic int value;\n\n\t\tpublic ResourceCounter(int v) {\n\t\t\tvalue = v;\n\t\t}\n\t}\n\n\tprivate final Executor mExecutor;\n\n\tpublic ThreadPool() {\n\t\tthis(CORE_POOL_SIZE, MAX_POOL_SIZE);\n\t}\n\n\tpublic ThreadPool(int initPoolSize, int maxPoolSize) {\n\t\tmExecutor = new ThreadPoolExecutor(initPoolSize, maxPoolSize,\n\t\t\t\tKEEP_ALIVE_TIME, TimeUnit.SECONDS,\n\t\t\t\tnew LinkedBlockingQueue<Runnable>(), new PriorityThreadFactory(\n\t\t\t\t\t\t\"thread-pool\",\n\t\t\t\t\t\tandroid.os.Process.THREAD_PRIORITY_BACKGROUND));\n\t}\n\n\t// Submit a job to the thread pool. The listener will be called when the\n\t// job is finished (or cancelled).\n\tpublic <T> Future<T> submit(Job<T> job, FutureListener<T> listener,\n\t\t\tint threadMode) {\n\t\tLog.i(TAG, \"<submit> Job:\" + job, Log.APP);\n\t\tWorker<T> w = new Worker<T>(job, listener, threadMode);\n\t\tmExecutor.execute(w);\n\t\treturn w;\n\t}\n\n\tpublic <T> Future<T> submit(Job<T> job) {\n\t\treturn submit(job, null, MODE_CPU);\n\t}\n\n\tprivate class Worker<T> implements Runnable, Future<T>, JobContext {\n\t\tprivate static final String TAG = \"Worker\";\n\t\tprivate Job<T> mJob;\n\t\tprivate FutureListener<T> mListener;\n\t\tprivate CancelListener mCancelListener;\n\t\tprivate ResourceCounter mWaitOnResource;\n\t\tprivate volatile boolean mIsCancelled;\n\t\tprivate boolean mIsDone;\n\t\tprivate T mResult;\n\t\tprivate int mMode;\n\t\tprivate int mTargetMode;\n\n\t\tpublic Worker(Job<T> job, FutureListener<T> listener, int threadMode) {\n\t\t\tmJob = job;\n\t\t\tmListener = listener;\n\t\t\tmTargetMode = threadMode;\n\t\t}\n\n\t\t// This is called by a thread in the thread pool.\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tLog.i(TAG, \"<run> start\", Log.APP);\n\t\t\tT result = null;\n\n\t\t\t// A job is in CPU mode by default. setMode returns false\n\t\t\t// if the job is cancelled.\n\t\t\tif (setMode(mTargetMode)) {\n\t\t\t\ttry {\n\t\t\t\t\tresult = mJob.run(this);\n\t\t\t\t} catch (Throwable ex) {\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsynchronized (this) {\n\t\t\t\tsetMode(MODE_NONE);\n\t\t\t\tmResult = result;\n\t\t\t\tmIsDone = true;\n\t\t\t\tnotifyAll();\n\t\t\t}\n\t\t\tif (mListener != null)\n\t\t\t\tmListener.onFutureDone(this);\n\t\t\tLog.i(TAG, \"<run> end\", Log.APP);\n\t\t}\n\n\t\t// Below are the methods for Future.\n\t\t@Override\n\t\tpublic synchronized void cancel() {\n\t\t\tif (mIsCancelled)\n\t\t\t\treturn;\n\t\t\tmIsCancelled = true;\n\t\t\tif (mWaitOnResource != null) {\n\t\t\t\tsynchronized (mWaitOnResource) {\n\t\t\t\t\tmWaitOnResource.notifyAll();\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (mCancelListener != null) {\n\t\t\t\tmCancelListener.onCancel();\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isCancelled() {\n\t\t\treturn mIsCancelled;\n\t\t}\n\n\t\t@Override\n\t\tpublic synchronized boolean isDone() {\n\t\t\treturn mIsDone;\n\t\t}\n\n\t\t@Override\n\t\tpublic synchronized T get() {\n\t\t\twhile (!mIsDone) {\n\t\t\t\ttry {\n\t\t\t\t\twait();\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn mResult;\n\t\t}\n\n\t\t@Override\n\t\tpublic void waitDone() {\n\t\t\tget();\n\t\t}\n\n\t\t// Below are the methods for JobContext (only called from the\n\t\t// thread running the job)\n\t\t@Override\n\t\tpublic synchronized void setCancelListener(CancelListener listener) {\n\t\t\tmCancelListener = listener;\n\t\t\tif (mIsCancelled && mCancelListener != null) {\n\t\t\t\tmCancelListener.onCancel();\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean setMode(int mode) {\n\t\t\t// Release old resource\n\t\t\tLog.i(TAG, \"<setMode> start mode:\" + mode, Log.APP);\n\t\t\tResourceCounter rc = modeToCounter(mMode);\n\t\t\tif (rc != null)\n\t\t\t\treleaseResource(rc);\n\t\t\tmMode = MODE_NONE;\n\n\t\t\t// Acquire new resource\n\t\t\trc = modeToCounter(mode);\n\t\t\tif (rc != null) {\n\t\t\t\tif (!acquireResource(rc)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tmMode = mode;\n\t\t\t}\n\t\t\tLog.i(TAG, \"<setMode> end\", Log.APP);\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate ResourceCounter modeToCounter(int mode) {\n\t\t\tif (mode == MODE_CPU) {\n\t\t\t\tLog.i(TAG, \"<modeToCounter> MODE_CPU\", Log.APP);\n\t\t\t\treturn mCpuCounter;\n\t\t\t} else if (mode == MODE_NETWORK) {\n\t\t\t\tLog.i(TAG, \"<modeToCounter> MODE_NETWORK\", Log.APP);\n\t\t\t\treturn mNetworkCounter;\n\t\t\t} else {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tprivate boolean acquireResource(ResourceCounter counter) {\n\t\t\tLog.i(TAG, \"<acquireResource> start\", Log.APP);\n\t\t\twhile (true) {\n\t\t\t\tsynchronized (this) {\n\t\t\t\t\tif (mIsCancelled) {\n\t\t\t\t\t\tmWaitOnResource = null;\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tmWaitOnResource = counter;\n\t\t\t\t}\n\n\t\t\t\tsynchronized (counter) {\n\t\t\t\t\tif (counter.value > 0) {\n\t\t\t\t\t\tcounter.value--;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tcounter.wait();\n\t\t\t\t\t\t} catch (InterruptedException ex) {\n\t\t\t\t\t\t\t// ignore.\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsynchronized (this) {\n\t\t\t\tmWaitOnResource = null;\n\t\t\t}\n\t\t\tLog.i(TAG, \"<acquireResource> end\", Log.APP);\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate void releaseResource(ResourceCounter counter) {\n\t\t\tLog.i(TAG, \"<releaseResource> start\", Log.APP);\n\t\t\tsynchronized (counter) {\n\t\t\t\tcounter.value++;\n\t\t\t\tcounter.notifyAll();\n\t\t\t}\n\t\t\tLog.i(TAG, \"<releaseResource> end\", Log.APP);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "WayHoo/.settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6\norg.eclipse.jdt.core.compiler.compliance=1.6\norg.eclipse.jdt.core.compiler.source=1.6\n"
  },
  {
    "path": "WayHoo/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.way.yahoo\"\n    android:versionCode=\"20150725\"\n    android:versionName=\"2.0.0\" >\n\n    <uses-sdk\n        android:minSdkVersion=\"14\"\n        android:targetSdkVersion=\"21\" />\n    <!-- 这个权限用于进行网络定位 -->\n    <uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\" />\n    <!-- 这个权限用于访问GPS定位 -->\n    <uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />\n    <!-- 用于访问wifi网络信息，wifi信息会用于进行网络定位 -->\n    <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />\n    <!-- 获取运营商信息，用于支持提供运营商信息相关的接口 -->\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n    <!-- 这个权限用于获取wifi的获取权限，wifi信息会用来进行网络定位 -->\n    <uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\" />\n    <!-- 用于读取手机当前的状态 -->\n    <uses-permission android:name=\"android.permission.READ_PHONE_STATE\" />\n    <!-- 写入扩展存储，向扩展卡写入数据，用于写入离线定位数据 -->\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n    <!-- 访问网络，网络定位需要上网 -->\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <!-- SD卡读取权限，用户写入离线定位数据 -->\n    <uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\" />\n    <!-- 允许应用读取低级别的系统日志文件 -->\n    <uses-permission android:name=\"android.permission.READ_LOGS\" />\n    \n    <!-- 联网权限，更新apk使用 -->\n    <uses-permission android:name=\"android.permission.GET_TASKS\" />\n\n    <application\n        android:name=\"com.way.yahoo.App\"\n        android:icon=\"@drawable/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme\" >\n        <activity\n            android:name=\"com.way.yahoo.SplashActivity\"\n            android:label=\"@string/app_name\"\n            android:screenOrientation=\"portrait\" >\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\"com.way.yahoo.MainActivity\"\n            android:label=\"@string/app_name\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <activity\n            android:name=\"com.way.yahoo.ManagerCityActivity\"\n            android:screenOrientation=\"portrait\"\n            android:theme=\"@style/SecondAppTheme\" />\n        <activity\n            android:name=\"com.way.yahoo.QueryCityActivity\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"portrait\"\n            android:theme=\"@style/SecondAppTheme\"\n            android:windowSoftInputMode=\"stateHidden|adjustPan\" />\n\n        <provider\n            android:name=\"com.way.db.CityProvider\"\n            android:authorities=\"com.way.yahoo.provider.Citys\" />\n\n        <activity\n            android:name=\"com.way.yahoo.AboutActivity\"\n            android:screenOrientation=\"portrait\"\n            android:theme=\"@style/SecondAppTheme\"\n            android:windowSoftInputMode=\"stateHidden|adjustPan\" />\n        <activity\n            android:name=\"com.way.yahoo.FeedBackActivity\"\n            android:screenOrientation=\"portrait\"\n            android:theme=\"@style/SecondAppTheme\"\n            android:windowSoftInputMode=\"stateHidden|adjustPan\" />\n            android:windowSoftInputMode=\"stateHidden|adjustResize\"\n\n        <!-- 接受下载apk成功广播，更新apk使用 -->\n        <receiver android:name=\"com.way.upgrade.receiver.DownloadCompleteReveiver\" >\n            <intent-filter>\n                <action android:name=\"android.intent.action.DOWNLOAD_COMPLETE\" />\n                <action android:name=\"android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED\" />\n            </intent-filter>\n        </receiver>\n\n        <meta-data\n            android:name=\"BUG_HD_SDK_GENERAL_KEY\"\n            android:value=\"d9be96d1d636733c2ddc1a1b4381e5b1\" />\n        <meta-data\n            android:name=\"bundle_id\"\n            android:value=\"55b382b8692d655ba7000001\" />\n        <meta-data\n            android:name=\"api_token\"\n            android:value=\"b6bb01fe261cafbabf03e16a9bdf33ad\" />\n\n        <service\n            android:name=\"com.baidu.location.f\"\n            android:enabled=\"true\"\n            android:process=\":remote\" >\n        </service>\n\n        <meta-data\n            android:name=\"com.baidu.lbsapi.API_KEY\"\n            android:value=\"W9xG4m71Ek8xz2GyhZvGiuWb\" />\n    </application>\n\n</manifest>"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/MainActivity.java",
    "content": "package com.way.upgrade;\r\n\r\nimport android.app.Activity;\r\nimport android.os.Bundle;\r\n\r\nimport com.way.upgrade.core.UpgradeManager;\r\n\r\npublic class MainActivity extends Activity {\r\n\r\n\t@Override\r\n\tprotected void onCreate(Bundle savedInstanceState) {\r\n\t\tsuper.onCreate(savedInstanceState);\r\n\r\n\t\tfinal UpgradeManager upgradeMangeer = UpgradeManager.newInstance(this);\r\n\r\n\t\tupgradeMangeer.askForNewVersion();\r\n\t}\r\n}\r\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/bean/UpgradeInfo.java",
    "content": "package com.way.upgrade.bean;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\n/**\n * 版本更新信息\n * \n * @author way\n * @since 2014/4/28\n */\npublic class UpgradeInfo implements Parcelable{\n\tprivate int id;\n\t\n\t/**唯一标识apk*/\n\tprivate String key;\n\n\t/** 版本信息 */\n\tprivate String version;\n\t\n\t/** 版本名称 */\n\tprivate String versionName;\n\t\n\t/** 是否有更新 */\n\tprivate String result;\n\n\t/** 新版本地址 */\n\tprivate String url;\n\n\t/** 版本描述 */\n\tprivate String description;\n\t\n\t/** 已下载的大小  */\n\tprivate long downloadSize;\n\t\n\tprivate String mustUpdate;\n\tprivate String changeLog;\n\t\n\tprivate String errorCode;\n\t\n\t\n\tpublic static final Parcelable.Creator<UpgradeInfo> CREATOR = new Creator<UpgradeInfo>() {\n\n\t\t@Override\n\t\tpublic UpgradeInfo[] newArray(int size) {\n\t\t\treturn new UpgradeInfo[size];\n\t\t}\n\n\t\t@Override\n\t\tpublic UpgradeInfo createFromParcel(Parcel source) {\n\t\t\treturn new UpgradeInfo(source);\n\t\t}\n\t};\n\n\tpublic UpgradeInfo() {\n\n\t}\n\t\n\tpublic UpgradeInfo(Parcel in) {\n\t\tid = in.readInt();\n\t\tkey = in.readString();\n\t\tversion = in.readString();\n\t\tversionName = in.readString();\n\t\tresult = in.readString();\n\t\turl = in.readString();\n\t\tdescription = in.readString();\n\t\tdownloadSize = in.readLong();\n\t\tresult = in.readString();\n\t\tmustUpdate = in.readString();\n\t\tchangeLog = in.readString();\n\t\terrorCode = in.readString();\n\t}\n\n\t@Override\n\tpublic int describeContents() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void writeToParcel(Parcel dest, int flags) {\n\t\tdest.writeInt(id);\n\t\tdest.writeString(key);\n\t\tdest.writeString(version);\n\t\tdest.writeString(versionName);\n\t\tdest.writeString(result);\n\t\tdest.writeString(url);\n\t\tdest.writeString(description);\n\t\tdest.writeLong(downloadSize);\n\t\tdest.writeString(result);\n\t\tdest.writeString(mustUpdate);\n\t\tdest.writeString(changeLog);\n\t\tdest.writeString(errorCode);\n\t}\n\n\tpublic int getId() {\n\t\treturn id;\n\t}\n\n\tpublic void setId(int id) {\n\t\tthis.id = id;\n\t}\n\n\tpublic String getKey() {\n\t\treturn key;\n\t}\n\n\tpublic void setKey(String key) {\n\t\tthis.key = key;\n\t}\n\n\tpublic String getVersion() {\n\t\treturn version;\n\t}\n\n\tpublic void setVersion(String version) {\n\t\tthis.version = version;\n\t}\n\n\tpublic String getUrl() {\n\t\treturn url;\n\t}\n\n\tpublic void setUrl(String url) {\n\t\tthis.url = url;\n\t}\n\n\tpublic String getDescription() {\n\t\treturn description;\n\t}\n\n\tpublic void setDescription(String description) {\n\t\tthis.description = description;\n\t}\n\n\tpublic long getDownloadSize() {\n\t\treturn downloadSize;\n\t}\n\n\tpublic void setDownloadSize(long downloadSize) {\n\t\tthis.downloadSize = downloadSize;\n\t}\n\n\tpublic String getVersionName() {\n\t\treturn versionName;\n\t}\n\n\tpublic void setVersionName(String versionName) {\n\t\tthis.versionName = versionName;\n\t}\n\n\tpublic String isResult() {\n\t\treturn result;\n\t}\n\n\tpublic void setResult(String result) {\n\t\tthis.result = result;\n\t}\n\n\tpublic String getMustUpdate() {\n\t\treturn mustUpdate;\n\t}\n\n\tpublic void setMustUpdate(String mustUpdate) {\n\t\tthis.mustUpdate = mustUpdate;\n\t}\n\t\n\tpublic String getChangeLog() {\n\t\treturn changeLog;\n\t}\n\n\tpublic void setChangeLog(String changeLog) {\n\t\tthis.changeLog = changeLog;\n\t}\n\n\tpublic String getResult() {\n\t\treturn result;\n\t}\n\n\n\tpublic String getErrorCode() {\n\t\treturn errorCode;\n\t}\n\n\tpublic void setErrorCode(String errorCode) {\n\t\tthis.errorCode = errorCode;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"UpgradeInfo [id=\" + id + \", key=\" + key + \", version=\"\n\t\t\t\t+ version + \", versionName=\" + versionName + \", result=\"\n\t\t\t\t+ result + \", url=\" + url + \", description=\" + description\n\t\t\t\t+ \", downloadSize=\" + downloadSize + \", mustUpdate=\"\n\t\t\t\t+ mustUpdate + \", changeLog=\" + changeLog\n\t\t\t\t+ \", errorCode=\" + errorCode + \"]\";\n\t}\n\n\t\n}\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/core/CheckNewVersionListener.java",
    "content": "package com.way.upgrade.core;\r\n\r\n/**\r\n * \r\n * @author way\r\n * @sine 2014/10/29\r\n */\r\npublic interface CheckNewVersionListener {\r\n\tpublic void checkNewVersion(boolean result);\r\n}\r\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/core/UpgradeInterface.java",
    "content": "package com.way.upgrade.core;\r\n\r\nimport com.way.upgrade.bean.UpgradeInfo;\r\n\r\n/**\r\n * \r\n * @author way\r\n * @since 2014/4/28\r\n */\r\npublic interface UpgradeInterface {\r\n\t/**\r\n\t * 请求服务器是否有新版本,并弹出对话框\r\n\t * \r\n\t * @return\r\n\t */\r\n\tpublic void askForNewVersion();\r\n\r\n\t/**\r\n\t * 请求服务器是否有新版本\r\n\t * \r\n\t * @return\r\n\t */\r\n\tpublic void askForNewVersionFlag(\r\n\t\t\tCheckNewVersionListener checkversionListener);\r\n\r\n\t/**\r\n\t * 下载新版本\r\n\t * \r\n\t * @return\r\n\t */\r\n\tpublic void downloadNewVersion(UpgradeInfo upgradeInfo);\r\n}\r\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/core/UpgradeManager.java",
    "content": "package com.way.upgrade.core;\r\n\r\nimport java.util.HashMap;\r\nimport java.util.Locale;\r\nimport java.util.Map;\r\n\r\nimport android.app.AlertDialog;\r\nimport android.app.ProgressDialog;\r\nimport android.content.Context;\r\nimport android.content.DialogInterface;\r\nimport android.os.Handler;\r\nimport android.os.Message;\r\nimport android.util.Log;\r\nimport android.widget.Toast;\r\n\r\nimport com.way.upgrade.bean.UpgradeInfo;\r\nimport com.way.upgrade.job.CheckNewVersionJobWithoutClientUrl;\r\nimport com.way.upgrade.job.DownloadNewVersionJob;\r\nimport com.way.upgrade.locale.LocaleChina;\r\nimport com.way.upgrade.locale.LocaleChinaTW;\r\nimport com.way.upgrade.locale.LocaleChinese;\r\nimport com.way.upgrade.locale.LocaleEnglish;\r\nimport com.way.upgrade.locale.LocaleHandler;\r\nimport com.way.upgrade.locale.LocaleUS;\r\nimport com.way.upgrade.utils.Constants;\r\nimport com.way.upgrade.utils.ContextUtils;\r\nimport com.way.upgrade.utils.thread.Future;\r\nimport com.way.upgrade.utils.thread.FutureListener;\r\nimport com.way.upgrade.utils.thread.ThreadPool;\r\n\r\n/**\r\n * \r\n * @author way\r\n * @since 2014/4/28\r\n */\r\npublic class UpgradeManager extends LocaleHandler implements UpgradeInterface {\r\n\tprivate Context mContext;\r\n\tprivate static ThreadPool mThreadPool;\r\n\tprivate Handler mHandler;\r\n\tprivate AlertDialog alertDialog;\r\n\tprivate AlertDialog alertDialogForMustUpdate;\r\n\tprivate ProgressDialog mProgressDialog;\r\n\r\n\tprivate Map<String, LocaleHandler> handlers;\r\n\r\n\tprivate static Future<UpgradeInfo> mFuture;\r\n\r\n\tprivate CheckNewVersionListener mAskForNewVersionFlagListener;\r\n\r\n\tprivate FutureListener<UpgradeInfo> mAskForNewVersionListener = new FutureListener<UpgradeInfo>() {\r\n\t\tpublic void onFutureDone(Future<UpgradeInfo> future) {\r\n\t\t\tUpgradeInfo upgradeInfo = future.get();\r\n\t\t\tMessage msg = mHandler.obtainMessage();\r\n\t\t\tif (upgradeInfo != null) {\r\n\t\t\t\tif (upgradeInfo.getErrorCode() != null) {\r\n\t\t\t\t\tif (upgradeInfo.getErrorCode().equals(\r\n\t\t\t\t\t\t\tConstants.ERROR_CODE_NET)) {\r\n\t\t\t\t\t\tmsg.what = Constants.MSG_NET_ERROR;\r\n\t\t\t\t\t}\r\n\t\t\t\t} else if (Boolean.parseBoolean(upgradeInfo.getResult())) {\r\n\t\t\t\t\tmFuture = future;\r\n\t\t\t\t\tmsg.what = Constants.MSG_HAVA_NEW_VERSION;\r\n\t\t\t\t\tmsg.obj = upgradeInfo;\r\n\t\t\t\t} else if (!Boolean.parseBoolean(upgradeInfo.getResult())) {\r\n\t\t\t\t\tmsg.what = Constants.MSG_NO_NEW_VERSION;\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\tmsg.what = Constants.MSG_NO_NEW_VERSION;\r\n\t\t\t}\r\n\t\t\tmHandler.sendMessage(msg);\r\n\t\t};\r\n\t};\r\n\r\n\tpublic class AskForNewVersionFlag implements FutureListener<UpgradeInfo> {\r\n\t\t@Override\r\n\t\tpublic void onFutureDone(Future<UpgradeInfo> future) {\r\n\t\t\tUpgradeInfo upgradeInfo = future.get();\r\n\t\t\tboolean result = false;\r\n\t\t\tif (upgradeInfo != null) {\r\n\t\t\t\tresult = Boolean.parseBoolean(upgradeInfo.getResult());\r\n\t\t\t}\r\n\t\t\tMessage msg = mHandler.obtainMessage();\r\n\t\t\tmsg.what = Constants.MSG_VERSION_RESULT;\r\n\t\t\tmsg.obj = result;\r\n\t\t\tmHandler.sendMessage(msg);\r\n\t\t}\r\n\r\n\t}\r\n\r\n\tpublic UpgradeManager(Context context) {\r\n\t\tcreateHandlers();\r\n\t\tmContext = context;\r\n\t\tinit();\r\n\t}\r\n\r\n\tprivate void init() {\r\n\t\tif (mThreadPool == null) {\r\n\t\t\tmThreadPool = new ThreadPool();\r\n\t\t}\r\n\t\tmHandler = new Handler(mContext.getMainLooper()) {\r\n\t\t\t@Override\r\n\t\t\tpublic void handleMessage(Message msg) {\r\n\t\t\t\tswitch (msg.what) {\r\n\t\t\t\tcase Constants.MSG_HAVA_NEW_VERSION:\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tContextUtils.closeProgressDialog(mProgressDialog);\r\n\t\t\t\t\t\tUpgradeInfo description = (UpgradeInfo) msg.obj;\r\n\t\t\t\t\t\tshowDialog(description);\r\n\t\t\t\t\t} catch (Exception e1) {\r\n\t\t\t\t\t\te1.printStackTrace();\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase Constants.MSG_NO_NEW_VERSION:\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tContextUtils.closeProgressDialog(mProgressDialog);\r\n\t\t\t\t\t\tContextUtils.showToast(mContext, getToastMessage(),\r\n\t\t\t\t\t\t\t\tToast.LENGTH_SHORT);\r\n\t\t\t\t\t} catch (Exception e) {\r\n\t\t\t\t\t\te.printStackTrace();\r\n\t\t\t\t\t}\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase Constants.MSG_START_DOWNLOAD:\r\n\t\t\t\t\tUpgradeInfo description1 = (UpgradeInfo) msg.obj;\r\n\t\t\t\t\tdownloadNewVersion(description1);\r\n\t\t\t\t\tbreak;\r\n\r\n\t\t\t\tcase Constants.MSG_VERSION_RESULT:\r\n\t\t\t\t\tBoolean result = (Boolean) msg.obj;\r\n\t\t\t\t\tmAskForNewVersionFlagListener.checkNewVersion(result);\r\n\t\t\t\t\tbreak;\r\n\t\t\t\tcase Constants.MSG_NET_ERROR:\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tContextUtils.closeProgressDialog(mProgressDialog);\r\n\t\t\t\t\t} catch (Exception e) {\r\n\t\t\t\t\t\te.printStackTrace();\r\n\t\t\t\t\t}\r\n\t\t\t\t\tContextUtils.showToast(mContext, getToastNetErrorMessage(),\r\n\t\t\t\t\t\t\tToast.LENGTH_SHORT);\r\n\t\t\t\tdefault:\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\talertDialog = ContextUtils.showAlertDialog(mContext, getDialogTitle(),\r\n\t\t\t\t\"\", new int[] { android.R.string.ok, android.R.string.cancel },\r\n\t\t\t\tnew DialogInterface.OnClickListener[] {\r\n\t\t\t\t\t\tnew DialogInterface.OnClickListener() {\r\n\r\n\t\t\t\t\t\t\t@Override\r\n\t\t\t\t\t\t\tpublic void onClick(DialogInterface arg0, int arg1) {\r\n\t\t\t\t\t\t\t\tif (mFuture != null) {\r\n\t\t\t\t\t\t\t\t\tMessage msg = mHandler.obtainMessage();\r\n\t\t\t\t\t\t\t\t\tmsg.what = Constants.MSG_START_DOWNLOAD;\r\n\t\t\t\t\t\t\t\t\tmsg.obj = mFuture.get();\r\n\t\t\t\t\t\t\t\t\tmHandler.sendMessage(msg);\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t}, new DialogInterface.OnClickListener() {\r\n\r\n\t\t\t\t\t\t\t@Override\r\n\t\t\t\t\t\t\tpublic void onClick(DialogInterface dialog,\r\n\t\t\t\t\t\t\t\t\tint which) {\r\n\t\t\t\t\t\t\t\tdialog.dismiss();\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t} });\r\n\r\n\t\talertDialogForMustUpdate = ContextUtils\r\n\t\t\t\t.showAlertDialog(\r\n\t\t\t\t\t\tmContext,\r\n\t\t\t\t\t\tgetDialogTitle(),\r\n\t\t\t\t\t\t\"\",\r\n\t\t\t\t\t\tnew int[] { android.R.string.ok },\r\n\t\t\t\t\t\tnew DialogInterface.OnClickListener[] { new DialogInterface.OnClickListener() {\r\n\r\n\t\t\t\t\t\t\t@Override\r\n\t\t\t\t\t\t\tpublic void onClick(DialogInterface arg0, int arg1) {\r\n\t\t\t\t\t\t\t\tif (mFuture != null) {\r\n\t\t\t\t\t\t\t\t\tMessage msg = mHandler.obtainMessage();\r\n\t\t\t\t\t\t\t\t\tmsg.what = Constants.MSG_START_DOWNLOAD;\r\n\t\t\t\t\t\t\t\t\tmsg.obj = mFuture.get();\r\n\t\t\t\t\t\t\t\t\tmHandler.sendMessage(msg);\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t} });\r\n\t\talertDialogForMustUpdate.setCancelable(false);\r\n\r\n\t\tmProgressDialog = ContextUtils.createProgressDialog(mContext);\r\n\t\tmProgressDialog.setTitle(getProgressDialogTitle());\r\n\t\tmProgressDialog.setMessage(getProgressDialogMessage());\r\n\r\n\t}\r\n\r\n\tpublic static UpgradeManager newInstance(Context context) {\r\n\t\treturn new UpgradeManager(context);\r\n\t}\r\n\r\n\tprivate void showProgressDialog() {\r\n\t\tif (mProgressDialog != null) {\r\n\t\t\ttry {\r\n\t\t\t\tmProgressDialog.show();\r\n\t\t\t} catch (Exception e) {\r\n\t\t\t\te.printStackTrace();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void askForNewVersion() {\r\n\t\tshowProgressDialog();\r\n\t\tmThreadPool.submit(new CheckNewVersionJobWithoutClientUrl(mContext),\r\n\t\t\t\tmAskForNewVersionListener, ThreadPool.MODE_NETWORK);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void askForNewVersionFlag(\r\n\t\t\tCheckNewVersionListener checkversionListener) {\r\n\t\tif (checkversionListener == null) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tmAskForNewVersionFlagListener = checkversionListener;\r\n\t\tAskForNewVersionFlag mAskForNewVersionFlagListener = new AskForNewVersionFlag();\r\n\t\tmThreadPool.submit(new CheckNewVersionJobWithoutClientUrl(mContext),\r\n\t\t\t\tmAskForNewVersionFlagListener, ThreadPool.MODE_NETWORK);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void downloadNewVersion(UpgradeInfo upgradeInfo) {\r\n\t\tmThreadPool.submit(new DownloadNewVersionJob(mContext, upgradeInfo),\r\n\t\t\t\tnull, ThreadPool.MODE_CPU);\r\n\t}\r\n\r\n\tprivate void createHandlers() {\r\n\t\thandlers = new HashMap<String, LocaleHandler>();\r\n\t\thandlers.put(LocaleChinese.defaultLocale, new LocaleChinese());\r\n\t\thandlers.put(LocaleChinaTW.defaultLocale, new LocaleChinaTW());\r\n\t\thandlers.put(LocaleEnglish.defaultLocale, new LocaleEnglish());\r\n\t\thandlers.put(Locale.CHINA.toString(), new LocaleChina());\r\n\t\thandlers.put(Locale.US.toString(), new LocaleUS());\r\n\t}\r\n\r\n\tprivate LocaleHandler lookupHandlerBy(String handlerName) {\r\n\t\tLocaleHandler handler = handlers.get(handlerName);\r\n\t\tif (handler == null)\r\n\t\t\treturn handlers.get(Locale.ENGLISH.getLanguage());\r\n\t\treturn handlers.get(handlerName);\r\n\t}\r\n\r\n\tprivate void showDialog(UpgradeInfo description) {\r\n\t\tint mustUpdate = Integer.valueOf(description.getMustUpdate());\r\n\t\tboolean result = Boolean.parseBoolean(description.isResult());\r\n\t\tString descriptionStr = description.getChangeLog();\r\n\t\tLog.i(\"liweiping\", \"mustUpdate = \" + mustUpdate + \", result = \"\r\n\t\t\t\t+ result + \", alertDialog = \" + alertDialog\r\n\t\t\t\t+ \",  alertDialogForMustUpdate = \" + alertDialogForMustUpdate);\r\n\t\tif (mustUpdate == Constants.NOT_MUST_UPDATE) {\r\n\t\t\tif (alertDialog != null && result) {\r\n\t\t\t\talertDialog.setTitle(getDialogTitle());\r\n\t\t\t\talertDialog.setMessage(descriptionStr);\r\n\t\t\t\talertDialog.show();\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tif (alertDialogForMustUpdate != null && result) {\r\n\t\t\t\talertDialogForMustUpdate.setTitle(getDialogTitle());\r\n\t\t\t\talertDialogForMustUpdate.setMessage(descriptionStr);\r\n\t\t\t\talertDialogForMustUpdate.show();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tprivate String getLocaleLanguage() {\r\n\t\treturn Locale.getDefault().toString();\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getDialogTitle() {\r\n\t\tLocaleHandler handler = lookupHandlerBy(getLocaleLanguage());\r\n\t\treturn handler.getDialogTitle();\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getProgressDialogTitle() {\r\n\t\tLocaleHandler handler = lookupHandlerBy(getLocaleLanguage());\r\n\t\treturn handler.getProgressDialogTitle();\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getProgressDialogMessage() {\r\n\t\tLocaleHandler handler = lookupHandlerBy(getLocaleLanguage());\r\n\t\treturn handler.getProgressDialogMessage();\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getToastMessage() {\r\n\t\tLocaleHandler handler = lookupHandlerBy(getLocaleLanguage());\r\n\t\treturn handler.getToastMessage();\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getToastNetErrorMessage() {\r\n\t\tLocaleHandler handler = lookupHandlerBy(getLocaleLanguage());\r\n\t\treturn handler.getToastNetErrorMessage();\r\n\t}\r\n}\r\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/job/AbstractCheckNewVersionJob.java",
    "content": "package com.way.upgrade.job;\r\n\r\nimport java.io.IOException;\r\n\r\nimport org.apache.http.client.ClientProtocolException;\r\nimport org.json.JSONException;\r\nimport org.json.JSONObject;\r\n\r\nimport android.content.Context;\r\nimport android.content.pm.ApplicationInfo;\r\nimport android.content.pm.PackageManager;\r\nimport android.content.pm.PackageManager.NameNotFoundException;\r\nimport android.util.Log;\r\n\r\nimport com.way.upgrade.bean.UpgradeInfo;\r\nimport com.way.upgrade.parser.json.FirVersionJsonParsing;\r\nimport com.way.upgrade.utils.Constants;\r\nimport com.way.upgrade.utils.NetUtils;\r\nimport com.way.upgrade.utils.NetUtils.NETWORK_STATUS;\r\nimport com.way.upgrade.utils.thread.ThreadPool;\r\nimport com.way.upgrade.utils.thread.ThreadPool.JobContext;\r\n\r\n/**\r\n * \r\n * @author way\r\n * @since 2014/4/28\r\n */\r\npublic abstract class AbstractCheckNewVersionJob implements\r\n\t\tThreadPool.Job<UpgradeInfo> {\r\n\tprotected Context mContext;\r\n\tprotected String bundle_id;\r\n\tprotected String api_token;\r\n\r\n\tpublic AbstractCheckNewVersionJob(Context context) {\r\n\t\tthis.mContext = context;\r\n\t\ttry {\r\n\t\t\tApplicationInfo appInfo = mContext.getPackageManager()\r\n\t\t\t\t\t.getApplicationInfo(mContext.getPackageName(),\r\n\t\t\t\t\t\t\tPackageManager.GET_META_DATA);\r\n\t\t\tbundle_id = appInfo.metaData.getString(\"bundle_id\");\r\n\t\t\tapi_token = appInfo.metaData.getString(\"api_token\");\r\n\t\t} catch (NameNotFoundException e) {\r\n\t\t\tthrow new NullPointerException(\r\n\t\t\t\t\t\"app_id and token must not null in AndroidManifest.xml \"\r\n\t\t\t\t\t\t\t+ e);\r\n\t\t}\r\n\t\tLog.i(\"liweiping\", \"AbstractCheckNewVersionJob app_id = \" + bundle_id\r\n\t\t\t\t+ \",  api_token = \" + api_token);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic UpgradeInfo run(JobContext jc) {\r\n\r\n\t\tUpgradeInfo upgradeInfo = null;\r\n\t\tif (NetUtils.getNetworkType(mContext) == NETWORK_STATUS.STATE_NONE_NETWORK) {\r\n\t\t\tLog.i(\"liweiping\", \"NETWORK_STATUS.STATE_NONE_NETWORK\");\r\n\t\t\tupgradeInfo = new UpgradeInfo();\r\n\t\t\tupgradeInfo.setErrorCode(Constants.ERROR_CODE_NET);\r\n\t\t\treturn upgradeInfo;\r\n\t\t}\r\n\t\ttry {\r\n\t\t\tString checkUpdateUrl = String.format(getCheckVersionUrl(), bundle_id,\r\n\t\t\t\t\tapi_token);\r\n\t\t\tLog.i(\"liweiping\", \"checkUpdateUrl = \" + checkUpdateUrl);\r\n\r\n\t\t\tJSONObject jo = NetUtils.getJSONArrayByGet(checkUpdateUrl);\r\n\t\t\tFirVersionJsonParsing firVersionJsonParsing = new FirVersionJsonParsing(\r\n\t\t\t\t\tmContext);\r\n\t\t\tLog.i(\"liweiping\",\r\n\t\t\t\t\t\"upgradeInfo = \" + upgradeInfo + \", checkUpdateUrl = \"\r\n\t\t\t\t\t\t\t+ checkUpdateUrl + \", jo = \" + jo.toString());\r\n\t\t\tupgradeInfo = firVersionJsonParsing.readJsonItem(jo);\r\n\t\t} catch (Exception e) {\r\n\t\t\tupgradeInfo = new UpgradeInfo();\r\n\t\t\tupgradeInfo.setErrorCode(Constants.ERROR_CODE_NET);\r\n\t\t\tLog.i(\"liweiping\",e.getMessage());\r\n\t\t\te.printStackTrace();\r\n\t\t}\r\n\t\treturn upgradeInfo;\r\n\t}\r\n\r\n\tpublic abstract String getCheckVersionUrl();\r\n\r\n}\r\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/job/CheckNewVersionJobWithoutClientUrl.java",
    "content": "package com.way.upgrade.job;\r\n\r\nimport android.content.Context;\r\n\r\nimport com.way.upgrade.utils.Constants;\r\n\r\n/**\r\n * \r\n * @author way\r\n * @since 2014/4/28\r\n */\r\npublic class CheckNewVersionJobWithoutClientUrl extends\r\n\t\tAbstractCheckNewVersionJob {\r\n\tpublic CheckNewVersionJobWithoutClientUrl(Context context) {\r\n\t\tsuper(context);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getCheckVersionUrl() {\r\n\t\treturn Constants.BASE_URL;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/job/DownloadNewVersionJob.java",
    "content": "package com.way.upgrade.job;\r\n\r\nimport java.io.File;\r\n\r\nimport android.app.DownloadManager;\r\nimport android.app.DownloadManager.Request;\r\nimport android.content.Context;\r\nimport android.content.Intent;\r\nimport android.content.pm.ApplicationInfo;\r\nimport android.content.pm.PackageManager;\r\nimport android.database.Cursor;\r\nimport android.net.Uri;\r\nimport android.os.Environment;\r\nimport android.util.Log;\r\n\r\nimport com.way.upgrade.bean.UpgradeInfo;\r\nimport com.way.upgrade.utils.Constants;\r\nimport com.way.upgrade.utils.Preferences;\r\nimport com.way.upgrade.utils.thread.ThreadPool;\r\nimport com.way.upgrade.utils.thread.ThreadPool.JobContext;\r\n\r\npublic class DownloadNewVersionJob implements ThreadPool.Job<Void> {\r\n\tprivate Context mContext;\r\n\tprivate UpgradeInfo mUpgradeInfo;\r\n\r\n\tprivate boolean allowMobileDownload = false;\r\n\tprivate static final long MAX_ALLOWED_DOWNLOAD_BYTES_BY_MOBILE = 3145725;\r\n\r\n\tpublic DownloadNewVersionJob(Context context, UpgradeInfo upgradeInfo) {\r\n\t\tthis.mContext = context;\r\n\t\tthis.mUpgradeInfo = upgradeInfo;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic Void run(JobContext jc) {\r\n\t\ttry {\r\n\t\t\tif (checkDownloadRunning())\r\n\t\t\t\treturn null;\r\n\t\t\tif (checkApkExist()) {\r\n\t\t\t\tIntent installApkIntent = new Intent();\r\n\t\t\t\tinstallApkIntent.setAction(Intent.ACTION_VIEW);\r\n\t\t\t\tinstallApkIntent.setDataAndType(\r\n\t\t\t\t\t\tUri.parse(Preferences.getDownloadPath(mContext)),\r\n\t\t\t\t\t\t\"application/vnd.android.package-archive\");\r\n\t\t\t\tinstallApkIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK\r\n\t\t\t\t\t\t| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);\r\n\t\t\t\tmContext.startActivity(installApkIntent);\r\n\t\t\t} else {\r\n\t\t\t\tString apkName = mContext.getPackageName()\r\n\t\t\t\t\t\t+ System.currentTimeMillis() + Constants.APK_SUFFIX;\r\n\t\t\t\t// 系统下载程序\r\n\t\t\t\tfinal DownloadManager downloadManager = (DownloadManager) mContext\r\n\t\t\t\t\t\t.getSystemService(mContext.DOWNLOAD_SERVICE);\r\n\r\n\t\t\t\tLong recommendedMaxBytes = DownloadManager\r\n\t\t\t\t\t\t.getRecommendedMaxBytesOverMobile(mContext);\r\n\r\n\t\t\t\t// 可以在移动网络下下载\r\n\t\t\t\tif (recommendedMaxBytes == null\r\n\t\t\t\t\t\t|| recommendedMaxBytes.longValue() > MAX_ALLOWED_DOWNLOAD_BYTES_BY_MOBILE) {\r\n\t\t\t\t\tallowMobileDownload = true;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tUri uri = Uri.parse(mUpgradeInfo.getUrl());\r\n\r\n\t\t\t\tfinal Request request = new Request(uri);\r\n\r\n\t\t\t\trequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);\r\n\t\t\t\trequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);\r\n\r\n\t\t\t\tint NETWORK_TYPE = DownloadManager.Request.NETWORK_WIFI;\r\n\t\t\t\tif (allowMobileDownload) {\r\n\t\t\t\t\tNETWORK_TYPE |= DownloadManager.Request.NETWORK_MOBILE;\r\n\t\t\t\t}\r\n\t\t\t\trequest.setAllowedNetworkTypes(NETWORK_TYPE);\r\n\t\t\t\trequest.allowScanningByMediaScanner();\r\n\t\t\t\trequest.setShowRunningNotification(true);\r\n\t\t\t\trequest.setVisibleInDownloadsUi(true);\r\n\t\t\t\trequest.setDestinationInExternalPublicDir(\r\n\t\t\t\t\t\tEnvironment.DIRECTORY_DOWNLOADS, apkName);\r\n\t\t\t\tPackageManager packageManager = mContext.getPackageManager();\r\n\t\t\t\tApplicationInfo applicationInfo = packageManager\r\n\t\t\t\t\t\t.getApplicationInfo(mContext.getPackageName(), 0);\r\n\t\t\t\tLog.i(\"liweiping\",\r\n\t\t\t\t\t\t\"appName = \"\r\n\t\t\t\t\t\t\t\t+ packageManager\r\n\t\t\t\t\t\t\t\t\t\t.getApplicationLabel(applicationInfo));\r\n\t\t\t\trequest.setTitle(packageManager\r\n\t\t\t\t\t\t.getApplicationLabel(applicationInfo));\r\n\t\t\t\trequest.setMimeType(\"application/vnd.android.package-archive\");\r\n\r\n\t\t\t\t// id 保存起来跟之后的广播接收器作对比\r\n\t\t\t\tlong id = downloadManager.enqueue(request);\r\n\r\n\t\t\t\tlong oldId = Preferences.getDownloadId(mContext);\r\n\t\t\t\tif (oldId != -1) {\r\n\t\t\t\t\tdownloadManager.remove(oldId);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tPreferences.removeAll(mContext);\r\n\t\t\t\tPreferences.setDownloadId(mContext, id);\r\n\t\t\t\tPreferences.setUpgradeInfo(mContext, mUpgradeInfo);\r\n\t\t\t\tPreferences.setDownloadStatus(mContext,\r\n\t\t\t\t\t\tConstants.DOWNLOAD_STATUS_RUNNING);\r\n\t\t\t}\r\n\t\t} catch (Exception e) {\r\n\t\t\te.printStackTrace();\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\tprivate boolean checkApkExist() {\r\n\t\tUpgradeInfo prefUpgradeInfo = Preferences.getUpgradeInfo(mContext);\r\n\t\tString version = prefUpgradeInfo.getVersion();\r\n\t\tString downloadPath = Preferences.getDownloadPath(mContext);\r\n\r\n\t\tif (version != null && version.trim().length() != 0\r\n\t\t\t\t&& version.equals(mUpgradeInfo.getVersion())\r\n\t\t\t\t&& downloadPath != null && downloadPath.trim().length() != 0) {\r\n\t\t\tString path = Uri.parse(downloadPath).getPath();\r\n\t\t\tif (path != null && path.endsWith(Constants.APK_SUFFIX)) {\r\n\t\t\t\tFile file = new File(path);\r\n\t\t\t\tif (file.exists()) {\r\n\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@SuppressWarnings(\"static-access\")\r\n\tprivate boolean checkDownloadRunning() {\r\n\t\tUpgradeInfo prefUpgradeInfo = Preferences.getUpgradeInfo(mContext);\r\n\t\tString version = prefUpgradeInfo.getVersion();\r\n\t\tint downloadStatus = Preferences.getDownloadStatus(mContext);\r\n\t\tif (version != null && version.trim().length() != 0\r\n\t\t\t\t&& version.equals(mUpgradeInfo.getVersion())) {\r\n\t\t\tlong downloadId = Preferences.getDownloadId(mContext);\r\n\t\t\tif (downloadId != -1) {\r\n\t\t\t\tfinal DownloadManager downloadManager = (DownloadManager) mContext\r\n\t\t\t\t\t\t.getSystemService(mContext.DOWNLOAD_SERVICE);\r\n\t\t\t\tDownloadManager.Query mDownloadQuery = new DownloadManager.Query();\r\n\t\t\t\tmDownloadQuery.setFilterById(downloadId);\r\n\t\t\t\tCursor cursor = downloadManager.query(mDownloadQuery);\r\n\t\t\t\tif (cursor != null && cursor.moveToFirst()) {\r\n\t\t\t\t\tint status = cursor.getInt(cursor\r\n\t\t\t\t\t\t\t.getColumnIndex(DownloadManager.COLUMN_STATUS));\r\n\t\t\t\t\tif (status == DownloadManager.STATUS_RUNNING\r\n\t\t\t\t\t\t\t|| downloadStatus == Constants.DOWNLOAD_STATUS_RUNNING) {\r\n\t\t\t\t\t\treturn true;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/locale/LocaleChina.java",
    "content": "package com.way.upgrade.locale;\r\n\r\n/**\r\n * \r\n * @author way\r\n * @since 2014/10/29\r\n */\r\npublic class LocaleChina extends LocaleChinese {\r\n\r\n}\r\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/locale/LocaleChinaTW.java",
    "content": "package com.way.upgrade.locale;\r\n\r\n/**\r\n * \r\n * @author way\r\n * @since 2014/10/29\r\n */\r\npublic class LocaleChinaTW extends LocaleChinese {\r\n\tpublic static final String defaultLocale = \"zh_TW\";\r\n\r\n\t@Override\r\n\tpublic String getDialogTitle() {\r\n\t\treturn \"發現新版本，是否升級？\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getProgressDialogTitle() {\r\n\t\treturn \"檢查更新\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getProgressDialogMessage() {\r\n\t\treturn \"正在檢查...\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getToastMessage() {\r\n\t\treturn \"已是最新版本\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getToastNetErrorMessage() {\r\n\t\treturn \"網路異常\";\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/locale/LocaleChinese.java",
    "content": "package com.way.upgrade.locale;\r\n\r\n/**\r\n * \r\n * @author way\r\n * @since 2014/10/29\r\n */\r\npublic class LocaleChinese extends LocaleHandler {\r\n\tpublic static final String defaultLocale = \"zh\";\r\n\r\n\t@Override\r\n\tpublic String getDialogTitle() {\r\n\t\treturn \"发现新版本，是否升级？\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getProgressDialogTitle() {\r\n\t\treturn \"检查更新\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getProgressDialogMessage() {\r\n\t\treturn \"正在检查...\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getToastMessage() {\r\n\t\treturn \"已是最新版本\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getToastNetErrorMessage() {\r\n\t\treturn \"网络异常\";\r\n\t}\r\n}\r\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/locale/LocaleEnglish.java",
    "content": "package com.way.upgrade.locale;\r\n\r\npublic class LocaleEnglish extends LocaleHandler {\r\n\tpublic static final String defaultLocale = \"en\";\r\n\r\n\t@Override\r\n\tpublic String getDialogTitle() {\r\n\t\treturn \"Find New Version,Update Now?\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getProgressDialogTitle() {\r\n\t\treturn \"Check Update\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getProgressDialogMessage() {\r\n\t\treturn \"Checking\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getToastMessage() {\r\n\t\treturn \"Is the latest version\";\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String getToastNetErrorMessage() {\r\n\t\treturn \"Net Error\";\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/locale/LocaleHandler.java",
    "content": "package com.way.upgrade.locale;\r\n\r\n/**\r\n * \r\n * @author way\r\n * @since 2014/10/29\r\n */\r\npublic abstract class LocaleHandler {\r\n\tprotected String value;\r\n\r\n\tpublic abstract String getDialogTitle();\r\n\r\n\tpublic abstract String getProgressDialogTitle();\r\n\r\n\tpublic abstract String getProgressDialogMessage();\r\n\r\n\tpublic abstract String getToastMessage();\r\n\r\n\tpublic abstract String getToastNetErrorMessage();\r\n\r\n\tpublic void setUpgradeDescription(String value) {\r\n\t\tthis.value = value;\r\n\t}\r\n\r\n\tpublic String getUpgradeDescription() {\r\n\t\treturn value;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/locale/LocaleUS.java",
    "content": "package com.way.upgrade.locale;\r\n\r\npublic class LocaleUS extends LocaleEnglish {\r\n}\r\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/parser/json/AbstractJsonParsing.java",
    "content": "package com.way.upgrade.parser.json;\n\nimport java.util.ArrayList;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\npublic abstract class AbstractJsonParsing<T> {\n\n\tpublic abstract T readJsonItem(JSONObject jsonItem) throws JSONException;\n\n\t/**\n\t * 输入InputStream\n\t * \n\t * @param inStream\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic ArrayList<T> readJsonArray(JSONArray jsonArray) {\n\n\t\ttry {\n\t\t\tArrayList<T> listOperating = new ArrayList<T>();\n\n\t\t\tfor (int i = 0; i < jsonArray.length(); i++) {\n\t\t\t\tJSONObject item = jsonArray.getJSONObject(i);\n\t\t\t\tT itemBean = readJsonItem(item);\n\t\t\t\tlistOperating.add(itemBean);\n\t\t\t}\n\t\t\treturn listOperating;\n\t\t} catch (JSONException e) {\n\t\t\te.printStackTrace();\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/parser/json/FirVersionJsonParsing.java",
    "content": "package com.way.upgrade.parser.json;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport android.content.Context;\n\nimport com.way.upgrade.bean.UpgradeInfo;\nimport com.way.upgrade.utils.Constants;\nimport com.way.upgrade.utils.ContextUtils;\n\npublic class FirVersionJsonParsing extends AbstractJsonParsing<UpgradeInfo> {\n\tprivate Context mContext;\n\n\tpublic FirVersionJsonParsing(Context context) {\n\t\tmContext = context;\n\t}\n\n\t@Override\n\tpublic UpgradeInfo readJsonItem(JSONObject versionJsonObj)\n\t\t\tthrows JSONException {\n\t\tUpgradeInfo upgradeInfo = new UpgradeInfo();\n\t\tString url = versionJsonObj.getString(\"installUrl\");\n\t\tString firVersionCode = versionJsonObj.getString(\"version\");\n\t\tString firVersionName = versionJsonObj.getString(\"versionShort\");\n\t\tString changeLog = versionJsonObj.getString(\"changelog\");\n\t\tupgradeInfo.setUrl(url);\n\t\tupgradeInfo.setVersion(firVersionCode);\n\t\tboolean result = ContextUtils.getVersionCode(mContext) < Integer\n\t\t\t\t.parseInt(firVersionCode)\n\t\t\t\t|| (ContextUtils.getVersionCode(mContext) == Integer\n\t\t\t\t\t\t.parseInt(firVersionCode) && !ContextUtils\n\t\t\t\t\t\t.getVersionName(mContext).equals(firVersionName));\n\t\tupgradeInfo.setResult(String.valueOf(result));\n\t\tupgradeInfo.setVersionName(firVersionName);\n\t\tupgradeInfo.setMustUpdate(Constants.NOT_MUST_UPDATE + \"\");\n\t\tupgradeInfo.setChangeLog(changeLog);\n\t\treturn upgradeInfo;\n\t}\n\n}\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/receiver/DownloadCompleteReveiver.java",
    "content": "package com.way.upgrade.receiver;\r\n\r\nimport android.app.DownloadManager;\r\nimport android.app.DownloadManager.Query;\r\nimport android.content.BroadcastReceiver;\r\nimport android.content.Context;\r\nimport android.content.Intent;\r\nimport android.database.Cursor;\r\nimport android.net.Uri;\r\n\r\nimport com.way.upgrade.utils.Preferences;\r\n\r\n/**\r\n * 下载完成通知接收器\r\n * \r\n * @author way\r\n * \r\n */\r\npublic class DownloadCompleteReveiver extends BroadcastReceiver {\r\n\tprivate DownloadManager downloadManager;\r\n\r\n\t@Override\r\n\tpublic void onReceive(Context context, Intent intent) {\r\n\t\tString action = intent.getAction();\r\n\r\n\t\tif (action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {\r\n\t\t\tlong id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0);\r\n\t\t\tif (id == Preferences.getDownloadId(context)) {\r\n\t\t\t\tQuery query = new Query();\r\n\t\t\t\tquery.setFilterById(id);\r\n\t\t\t\tdownloadManager = (DownloadManager) context\r\n\t\t\t\t\t\t.getSystemService(Context.DOWNLOAD_SERVICE);\r\n\t\t\t\tCursor cursor = downloadManager.query(query);\r\n\r\n\t\t\t\tint columnCount = cursor.getColumnCount();\r\n\t\t\t\tString path = null;\r\n\t\t\t\twhile (cursor.moveToNext()) {\r\n\t\t\t\t\tfor (int j = 0; j < columnCount; j++) {\r\n\t\t\t\t\t\tString columnName = cursor.getColumnName(j);\r\n\t\t\t\t\t\tString string = cursor.getString(j);\r\n\t\t\t\t\t\tif (\"local_uri\".equals(columnName)) {\r\n\t\t\t\t\t\t\tpath = string;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tcursor.close();\r\n\r\n\t\t\t\tif (path != null) {\r\n\t\t\t\t\tPreferences.setDownloadPath(context, path);\r\n\t\t\t\t\tPreferences.setDownloadStatus(context, -1);\r\n\t\t\t\t\tIntent installApkIntent = new Intent();\r\n\t\t\t\t\tinstallApkIntent.setAction(Intent.ACTION_VIEW);\r\n\t\t\t\t\tinstallApkIntent.setDataAndType(Uri.parse(path),\r\n\t\t\t\t\t\t\t\"application/vnd.android.package-archive\");\r\n\t\t\t\t\tinstallApkIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK\r\n\t\t\t\t\t\t\t| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);\r\n\t\t\t\t\tcontext.startActivity(installApkIntent);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} else if (action.equals(DownloadManager.ACTION_NOTIFICATION_CLICKED)) {\r\n\t\t\tlong[] ids = intent\r\n\t\t\t\t\t.getLongArrayExtra(DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS);\r\n\t\t\tif (ids.length > 0 && ids[0] == Preferences.getDownloadId(context)) {\r\n\t\t\t\tIntent downloadIntent = new Intent();\r\n\t\t\t\tdownloadIntent.setAction(DownloadManager.ACTION_VIEW_DOWNLOADS);\r\n\t\t\t\tdownloadIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK\r\n\t\t\t\t\t\t| Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);\r\n\t\t\t\tcontext.startActivity(downloadIntent);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/utils/Constants.java",
    "content": "package com.way.upgrade.utils;\r\n\r\n/**\r\n * \r\n * @author way 2014/4/28\r\n */\r\npublic class Constants {\r\n\t//public static final String BASE_URL = \"http://fir.im/api/v2/app/version/%s?token=%s\";\r\n\tpublic static final String BASE_URL = \"http://api.fir.im/apps/latest/%s?api_token=%s\";\r\n\r\n\tpublic static final String DOWNLOAD_FILE_PATH = \"/upgrade\";\r\n\r\n\tpublic static final int MSG_HAVA_NEW_VERSION = 1;\r\n\tpublic static final int MSG_NO_NEW_VERSION = 2;\r\n\tpublic static final int MSG_START_DOWNLOAD = 3;\r\n\tpublic static final int MSG_VERSION_RESULT = 4;\r\n\tpublic static final int MSG_NET_ERROR = 5;\r\n\r\n\tpublic static final int MUST_UPDATE = 1;\r\n\tpublic static final int NOT_MUST_UPDATE = 0;\r\n\r\n\tpublic static final String APK_SUFFIX = \".apk\";\r\n\r\n\tpublic static final Integer DOWNLOAD_STATUS_RUNNING = 1;\r\n\r\n\tpublic static final String ERROR_CODE_NET = \"net_error\";\r\n}\r\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/utils/ContextUtils.java",
    "content": "package com.way.upgrade.utils;\r\n\r\nimport java.lang.reflect.Method;\r\n\r\nimport android.app.AlertDialog;\r\nimport android.app.Dialog;\r\nimport android.app.ProgressDialog;\r\nimport android.content.ComponentName;\r\nimport android.content.Context;\r\nimport android.content.DialogInterface.OnClickListener;\r\nimport android.content.Intent;\r\nimport android.content.pm.PackageInfo;\r\nimport android.content.pm.PackageManager;\r\nimport android.content.pm.PackageManager.NameNotFoundException;\r\nimport android.content.pm.ResolveInfo;\r\nimport android.os.Build;\r\nimport android.util.Log;\r\nimport android.widget.Toast;\r\n\r\n/**\r\n * 上下文操作，如创建对话框，进度条,版本信息...\r\n * \r\n * @author way 2013/12/6\r\n */\r\npublic class ContextUtils {\r\n\tprivate static final String TAG = \"ContextUtils\";\r\n\r\n\t/**\r\n\t * Show a Toast(Toast.LENGTH_SHORT).\r\n\t * \r\n\t * @param text\r\n\t *            the content shown on the Toast.\r\n\t */\r\n\tpublic static void showToast(Context context, String text, int length) {\r\n\t\tToast.makeText(context, text, length).show();\r\n\t}\r\n\r\n\t/**\r\n\t * Show a Toast(Toast.LENGTH_SHORT).\r\n\t * \r\n\t * @param text\r\n\t *            the content shown on the Toast.\r\n\t */\r\n\tpublic static void showToast(Context context, int resId, int length) {\r\n\t\tToast.makeText(context, resId, length).show();\r\n\t}\r\n\r\n\tpublic static ProgressDialog createProgressDialog(Context context) {\r\n\t\tProgressDialog dialog = new ProgressDialog(context);\r\n\t\tdialog.setIndeterminate(true);\r\n\t\tdialog.setCancelable(true);\r\n\t\treturn dialog;\r\n\t}\r\n\r\n\t/**\r\n\t * show progress dialog\r\n\t * \r\n\t * @param context\r\n\t * @param title\r\n\t *            Dialog title\r\n\t * @param message\r\n\t *            Dialog message\r\n\t * @return\r\n\t */\r\n\tpublic static ProgressDialog showProgressDialog(Context context, int title,\r\n\t\t\tint message) {\r\n\t\tProgressDialog dialog = new ProgressDialog(context);\r\n\t\tdialog.setTitle(title);\r\n\t\tdialog.setMessage(context.getResources().getString(message));\r\n\t\tdialog.setIndeterminate(true);\r\n\t\tdialog.setCancelable(true);\r\n\t\tdialog.show();\r\n\t\treturn dialog;\r\n\t}\r\n\r\n\t/**\r\n\t * close progress dialog\r\n\t * \r\n\t * @param progressDialog\r\n\t */\r\n\tpublic static void closeProgressDialog(Dialog progressDialog) {\r\n\t\tif (progressDialog != null && progressDialog.isShowing()) {\r\n\t\t\tprogressDialog.cancel();\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * \r\n\t * @param context\r\n\t * @param title\r\n\t * @param message\r\n\t * @param buttonTexts\r\n\t * @param listeners\r\n\t * @return\r\n\t */\r\n\tpublic static AlertDialog.Builder showDialog(Context context, int title,\r\n\t\t\tint message, int[] buttonTexts, OnClickListener[] listeners) {\r\n\t\tAlertDialog.Builder dialog = new AlertDialog.Builder(context);\r\n\t\tdialog.setTitle(title);\r\n\t\tdialog.setMessage(message);\r\n\t\tif (buttonTexts.length == 1) {\r\n\t\t\tdialog.setNeutralButton(buttonTexts[0], listeners[0]);\r\n\t\t} else if (buttonTexts.length == 2) {\r\n\t\t\tdialog.setPositiveButton(buttonTexts[0], listeners[0]);\r\n\t\t\tdialog.setNegativeButton(buttonTexts[1], listeners[1]);\r\n\t\t} else if (buttonTexts.length == 3) {\r\n\t\t\tdialog.setPositiveButton(buttonTexts[0], listeners[0]);\r\n\t\t\tdialog.setNeutralButton(buttonTexts[1], listeners[1]);\r\n\t\t\tdialog.setNegativeButton(buttonTexts[2], listeners[2]);\r\n\t\t}\r\n\t\tdialog.show();\r\n\t\treturn dialog;\r\n\t}\r\n\r\n\t/**\r\n\t * \r\n\t * @param context\r\n\t * @param title\r\n\t * @param message\r\n\t * @param buttonTexts\r\n\t * @param listeners\r\n\t * @return\r\n\t */\r\n\tpublic static AlertDialog.Builder showDialog(Context context, String title,\r\n\t\t\tString message, int[] buttonTexts, OnClickListener[] listeners) {\r\n\t\tAlertDialog.Builder dialog = new AlertDialog.Builder(context);\r\n\t\tdialog.setTitle(title);\r\n\t\tdialog.setMessage(message);\r\n\t\tif (buttonTexts.length == 1) {\r\n\t\t\tdialog.setNeutralButton(buttonTexts[0], listeners[0]);\r\n\t\t} else if (buttonTexts.length == 2) {\r\n\t\t\tdialog.setPositiveButton(buttonTexts[0], listeners[0]);\r\n\t\t\tdialog.setNegativeButton(buttonTexts[1], listeners[1]);\r\n\t\t} else if (buttonTexts.length == 3) {\r\n\t\t\tdialog.setPositiveButton(buttonTexts[0], listeners[0]);\r\n\t\t\tdialog.setNeutralButton(buttonTexts[1], listeners[1]);\r\n\t\t\tdialog.setNegativeButton(buttonTexts[2], listeners[2]);\r\n\t\t}\r\n\t\tdialog.show();\r\n\t\treturn dialog;\r\n\t}\r\n\r\n\t/**\r\n\t * \r\n\t * @param context\r\n\t * @param title\r\n\t * @param message\r\n\t * @param buttonTexts\r\n\t * @param listeners\r\n\t * @return\r\n\t */\r\n\tpublic static AlertDialog showAlertDialog(Context context, String title,\r\n\t\t\tString message, int[] buttonTexts, OnClickListener[] listeners) {\r\n\t\tAlertDialog.Builder dialog = new AlertDialog.Builder(context);\r\n\t\tdialog.setTitle(title);\r\n\t\tdialog.setMessage(message);\r\n\t\tif (buttonTexts.length == 1) {\r\n\t\t\tdialog.setNeutralButton(buttonTexts[0], listeners[0]);\r\n\t\t} else if (buttonTexts.length == 2) {\r\n\t\t\tdialog.setPositiveButton(buttonTexts[0], listeners[0]);\r\n\t\t\tdialog.setNegativeButton(buttonTexts[1], listeners[1]);\r\n\t\t} else if (buttonTexts.length == 3) {\r\n\t\t\tdialog.setPositiveButton(buttonTexts[0], listeners[0]);\r\n\t\t\tdialog.setNeutralButton(buttonTexts[1], listeners[1]);\r\n\t\t\tdialog.setNegativeButton(buttonTexts[2], listeners[2]);\r\n\t\t}\r\n\t\treturn dialog.create();\r\n\t}\r\n\r\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\r\n\tpublic static String getCustomVersion() {\r\n\t\ttry {\r\n\t\t\tString className = \"android.os.SystemProperties\";\r\n\t\t\tString methodName = \"get\";\r\n\t\t\tString key = \"ro.custom.build.version\";\r\n\t\t\tClass clazz = Class.forName(className);\r\n\t\t\t// Constructor con = clazz.getEnclosingConstructor();\r\n\t\t\tMethod method = clazz.getDeclaredMethod(methodName, String.class);\r\n\t\t\treturn (String) method.invoke(clazz, key);\r\n\t\t} catch (Exception e) {\r\n\t\t\t// TODO Auto-generated catch block\r\n\t\t\te.printStackTrace();\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\t/**\r\n\t * 获取版本号\r\n\t * \r\n\t * @param context\r\n\t * @return\r\n\t */\r\n\tpublic static int getVersionCode(Context context) {\r\n\t\tint versionCode = -1;\r\n\t\ttry {\r\n\t\t\tPackageInfo info = context.getPackageManager().getPackageInfo(\r\n\t\t\t\t\tcontext.getPackageName(), 0);\r\n\t\t\tversionCode = info.versionCode;\r\n\t\t} catch (NameNotFoundException e) {\r\n\t\t\te.printStackTrace();\r\n\t\t\tLog.e(TAG, e.getMessage());\r\n\t\t}\r\n\t\treturn versionCode;\r\n\t}\r\n\r\n\t/**\r\n\t * 获取版本信息\r\n\t * \r\n\t * @param mContext\r\n\t * @return\r\n\t */\r\n\tpublic static String getVersionName(Context context) {\r\n\t\tString versionName = null;\r\n\t\ttry {\r\n\t\t\tPackageInfo info = context.getPackageManager().getPackageInfo(\r\n\t\t\t\t\tcontext.getPackageName(), 0);\r\n\t\t\tversionName = info.versionName;\r\n\t\t\t// int versionCode = info.versionCode;\r\n\t\t} catch (NameNotFoundException e) {\r\n\t\t\te.printStackTrace();\r\n\t\t\tLog.e(TAG, e.getMessage());\r\n\t\t}\r\n\t\treturn versionName;\r\n\t}\r\n\r\n\tpublic static String getUserAgent(Context context) {\r\n\t\tPackageInfo packageInfo;\r\n\t\ttry {\r\n\t\t\tpackageInfo = context.getPackageManager().getPackageInfo(\r\n\t\t\t\t\tcontext.getPackageName(), 0);\r\n\t\t} catch (NameNotFoundException e) {\r\n\t\t\tthrow new IllegalStateException(\"getPackageInfo failed\");\r\n\t\t}\r\n\t\treturn String.format(\"%s/%s; %s/%s/%s/%s; %s/%s/%s\",\r\n\t\t\t\tpackageInfo.packageName, packageInfo.versionName, Build.BRAND,\r\n\t\t\t\tBuild.DEVICE, Build.MODEL, Build.ID, Build.VERSION.SDK_INT,\r\n\t\t\t\tBuild.VERSION.RELEASE, Build.VERSION.INCREMENTAL);\r\n\t}\r\n\r\n\tpublic static Intent getActivityIntent(Context context, String packageName,\r\n\t\t\tString className) {\r\n\t\tPackageManager pm = context.getPackageManager();\r\n\t\tIntent intent = new Intent();\r\n\t\tComponentName compoentName = new ComponentName(packageName, className);\r\n\t\tintent.setComponent(compoentName);\r\n\t\tResolveInfo ri = pm.resolveActivity(intent,\r\n\t\t\t\tPackageManager.MATCH_DEFAULT_ONLY);\r\n\t\tif (ri != null) {\r\n\t\t\treturn intent;\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/utils/FileUtils.java",
    "content": "package com.way.upgrade.utils;\r\n\r\nimport java.io.File;\r\n\r\n/**\r\n * @author way 2013/12/6\r\n */\r\npublic class FileUtils {\r\n\tprivate static final String TAG = \"FileUtils\";\r\n\r\n\t/**\r\n\t * @param f\r\n\t * @throws Exception\r\n\t */\r\n\tpublic static void createNewFile(File f) throws Exception {\r\n\t\tLog.i(TAG, \"create file:\" + f, Log.APP);\r\n\t\tif (!f.getParentFile().exists()) {\r\n\t\t\tf.getParentFile().mkdirs();\r\n\t\t}\r\n\t\tif (!f.exists()) {\r\n\t\t\tf.createNewFile();\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/utils/Log.java",
    "content": "package com.way.upgrade.utils;\r\n\r\npublic class Log {\r\n\tpublic static final boolean DEBUG = true;\r\n\tpublic static final boolean DATA_DEBUG = true;\r\n\tpublic static final boolean APP_DEBUG = true;\r\n\tpublic static final boolean DISPLAY_DEBUG = false;\r\n\tpublic static final int DATA = 0;\r\n\tpublic static final int DISPLAY = 1;\r\n\tpublic static final int APP = 2;\r\n\r\n\tpublic static final String TAG = \"zhujianwen\";\r\n\r\n\tpublic static void v(String tag, String msg, int mod) {\r\n\t\tif (!DEBUG) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (mod == DATA && DATA_DEBUG) {\r\n\t\t\tandroid.util.Log.v(tag, msg);\r\n\t\t} else if (mod == DISPLAY && DISPLAY_DEBUG) {\r\n\t\t\tandroid.util.Log.v(tag, msg);\r\n\t\t} else if (mod == APP && APP_DEBUG) {\r\n\t\t\tandroid.util.Log.v(tag, msg);\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static void v(String msg, int mod) {\r\n\t\tv(TAG, msg, mod);\r\n\t}\r\n\r\n\tpublic static void d(String tag, String msg, int mod) {\r\n\t\tif (!DEBUG) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (mod == DATA && DATA_DEBUG) {\r\n\t\t\tandroid.util.Log.d(tag, msg);\r\n\t\t} else if (mod == DISPLAY && DISPLAY_DEBUG) {\r\n\t\t\tandroid.util.Log.d(tag, msg);\r\n\t\t} else if (mod == APP && APP_DEBUG) {\r\n\t\t\tandroid.util.Log.d(tag, msg);\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static void d(String msg, int mod) {\r\n\t\td(TAG, msg, mod);\r\n\t}\r\n\r\n\tpublic static void i(String tag, String msg, int mod) {\r\n\t\tif (!DEBUG) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (mod == DATA && DATA_DEBUG) {\r\n\t\t\tandroid.util.Log.i(tag, msg);\r\n\t\t} else if (mod == DISPLAY && DISPLAY_DEBUG) {\r\n\t\t\tandroid.util.Log.i(tag, msg);\r\n\t\t} else if (mod == APP && APP_DEBUG) {\r\n\t\t\tandroid.util.Log.i(tag, msg);\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static void i(String msg, int mod) {\r\n\t\ti(TAG, msg, mod);\r\n\t}\r\n\r\n\tpublic static void w(String tag, String msg, int mod) {\r\n\t\tif (!DEBUG) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (mod == DATA && DATA_DEBUG) {\r\n\t\t\tandroid.util.Log.w(tag, msg);\r\n\t\t} else if (mod == DISPLAY && DISPLAY_DEBUG) {\r\n\t\t\tandroid.util.Log.w(tag, msg);\r\n\t\t} else if (mod == APP && APP_DEBUG) {\r\n\t\t\tandroid.util.Log.w(tag, msg);\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static void w(String msg, int mod) {\r\n\t\tw(TAG, msg, mod);\r\n\t}\r\n\r\n\tpublic static void e(String tag, String msg, int mod) {\r\n\t\tif (!DEBUG) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (mod == DATA && DATA_DEBUG) {\r\n\t\t\tandroid.util.Log.e(tag, msg);\r\n\t\t} else if (mod == DISPLAY && DISPLAY_DEBUG) {\r\n\t\t\tandroid.util.Log.e(tag, msg);\r\n\t\t} else if (mod == APP && APP_DEBUG) {\r\n\t\t\tandroid.util.Log.e(tag, msg);\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static void e(String msg, int mod) {\r\n\t\te(TAG, msg, mod);\r\n\t}\r\n}\r\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/utils/NetUtils.java",
    "content": "package com.way.upgrade.utils;\r\n\r\nimport java.io.BufferedReader;\r\nimport java.io.DataOutputStream;\r\nimport java.io.File;\r\nimport java.io.FileInputStream;\r\nimport java.io.IOException;\r\nimport java.io.InputStream;\r\nimport java.io.InputStreamReader;\r\nimport java.net.HttpURLConnection;\r\nimport java.net.URL;\r\nimport java.util.Map;\r\n\r\nimport org.apache.http.HttpResponse;\r\nimport org.apache.http.client.ClientProtocolException;\r\nimport org.apache.http.client.HttpClient;\r\nimport org.apache.http.client.methods.HttpGet;\r\nimport org.apache.http.client.methods.HttpPost;\r\nimport org.apache.http.impl.client.DefaultHttpClient;\r\nimport org.apache.http.params.BasicHttpParams;\r\nimport org.apache.http.params.HttpConnectionParams;\r\nimport org.apache.http.params.HttpParams;\r\nimport org.json.JSONArray;\r\nimport org.json.JSONException;\r\nimport org.json.JSONObject;\r\n\r\nimport android.content.Context;\r\nimport android.net.ConnectivityManager;\r\nimport android.net.NetworkInfo;\r\n\r\n/**\r\n * 网络操作\r\n * \r\n * @author way 2013/12/6\r\n */\r\npublic class NetUtils {\r\n\tprivate static final String TAG = \"NetUtils\";\r\n\r\n\tprivate static final String NETTYPE_WIFI = \"WIFI\";\r\n\r\n\tprivate static String multipart_form_data = \"multipart/form-data\";\r\n\tprivate static String twoHyphens = \"--\";\r\n\tprivate static String boundary = java.util.UUID.randomUUID().toString(); // 数据分隔\r\n\tprivate static String lineEnd = \"\\r\\n\"; // The value is \"\\r\\n\" in Windows.\r\n\r\n\tpublic enum NETWORK_STATUS {\r\n\t\tSTATE_WIFI, STATE_GPRS, STATE_NONE_NETWORK\r\n\t}\r\n\r\n\tprivate NetUtils() {\r\n\t}\r\n\r\n\tpublic static NETWORK_STATUS getNetworkType(Context context) {\r\n\t\tNETWORK_STATUS ret = NETWORK_STATUS.STATE_NONE_NETWORK;\r\n\r\n\t\tConnectivityManager connetManager = (ConnectivityManager) context\r\n\t\t\t\t.getSystemService(Context.CONNECTIVITY_SERVICE);\r\n\t\tif (connetManager == null) {\r\n\t\t\tLog.e(TAG, \"isNetWorkAvailable connetManager = null\", Log.APP);\r\n\t\t\treturn ret;\r\n\t\t}\r\n\t\tNetworkInfo[] infos = connetManager.getAllNetworkInfo();\r\n\t\tif (infos == null) {\r\n\t\t\treturn ret;\r\n\t\t}\r\n\t\tfor (int i = 0; i < infos.length && infos[i] != null; i++) {\r\n\t\t\tif (infos[i].isConnected() && infos[i].isAvailable()) {\r\n\r\n\t\t\t\tif (infos[i].getTypeName().equalsIgnoreCase(NETTYPE_WIFI)) {\r\n\t\t\t\t\tret = NETWORK_STATUS.STATE_WIFI;\r\n\t\t\t\t} else {\r\n\t\t\t\t\tret = NETWORK_STATUS.STATE_GPRS;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tLog.i(TAG, \"get network stype is : \" + ret, Log.APP);\r\n\t\treturn ret;\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * \r\n\t * @param context\r\n\t * @param typeName\r\n\t *            (\"\",WIFI,MOBILE)\r\n\t * @return\r\n\t */\r\n\tpublic static boolean isNetWorkAvailable(Context context, String typeName) {\r\n\r\n\t\tLog.i(TAG, \">>> isNetWorkAvailable context = \" + context\r\n\t\t\t\t+ \"typeName = \" + typeName, Log.APP);\r\n\r\n\t\tboolean ret = false;\r\n\r\n\t\tConnectivityManager connetManager = (ConnectivityManager) context\r\n\t\t\t\t.getSystemService(Context.CONNECTIVITY_SERVICE);\r\n\t\tif (connetManager == null) {\r\n\t\t\tLog.e(TAG, \"isNetWorkAvailable connetManager = null\", Log.APP);\r\n\t\t\treturn ret;\r\n\t\t}\r\n\t\tNetworkInfo[] infos = connetManager.getAllNetworkInfo();\r\n\t\tif (infos == null) {\r\n\t\t\treturn ret;\r\n\t\t}\r\n\t\tif ((typeName == null) || (typeName.length() <= 0)) {\r\n\t\t\tfor (int i = 0; i < infos.length && infos[i] != null; i++) {\r\n\t\t\t\tif (infos[i].isConnected() && infos[i].isAvailable()) {\r\n\t\t\t\t\tret = true;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tfor (int i = 0; i < infos.length && infos[i] != null; i++) {\r\n\t\t\t\tif (infos[i].getTypeName().equalsIgnoreCase(typeName)\r\n\t\t\t\t\t\t&& infos[i].isConnected() && infos[i].isAvailable()) {\r\n\t\t\t\t\tLog.i(TAG,\r\n\t\t\t\t\t\t\t\"isNetWorkAvailable name is : \"\r\n\t\t\t\t\t\t\t\t\t+ infos[i].getTypeName(), Log.APP);\r\n\t\t\t\t\tret = true;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tLog.i(TAG, \"isNetWorkAvailable >>> result is : \" + ret, Log.APP);\r\n\t\treturn ret;\r\n\t}\r\n\r\n\t/**\r\n\t * \r\n\t * @param url\r\n\t * @return\r\n\t * @throws IOException\r\n\t */\r\n\tpublic static synchronized InputStream getInputStreamByGet(String url)\r\n\t\t\tthrows IOException {\r\n\t\tLog.i(TAG, \"<getInputStreamByGet> url:\" + url, Log.APP);\r\n\t\tHttpURLConnection httpConnection = null;\r\n\t\tint currentSize = 0;\r\n\t\tif (url == null) {\r\n\t\t\treturn null;\r\n\t\t}\r\n\t\tURL uri = new URL(url);\r\n\t\thttpConnection = (HttpURLConnection) uri.openConnection();\r\n\t\thttpConnection.setRequestProperty(\"User-Agent\", \"PacificHttpClient\");\r\n\t\tif (currentSize > 0) {\r\n\t\t\thttpConnection.setRequestProperty(\"RANGE\", \"bytes=\" + currentSize\r\n\t\t\t\t\t+ \"-\");\r\n\t\t}\r\n\t\t// 设置超时时间\r\n\t\thttpConnection.setConnectTimeout(10000);// 限制连接超时5秒钟\r\n\t\thttpConnection.setReadTimeout(2 * 10000);\r\n\t\thttpConnection.setRequestProperty(\"Content-type\",\r\n\t\t\t\t\"text/html;charset=UTF-8\");\r\n\r\n\t\thttpConnection.setDoOutput(true);\r\n\t\thttpConnection.setRequestMethod(\"GET\");\r\n\t\thttpConnection.setUseCaches(false);\r\n\t\tint requestCode = httpConnection.getResponseCode();\r\n\t\tif (requestCode == 200) {\r\n\t\t\tInputStream in = httpConnection.getInputStream();\r\n\t\t\treturn in;\r\n\t\t}\r\n\t\treturn null;\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * post方式从服务器获取json数组\r\n\t * \r\n\t * @return\r\n\t * @throws IOException\r\n\t * @throws ClientProtocolException\r\n\t * @throws JSONException\r\n\t */\r\n\tpublic static JSONArray getJSONArrayByPost(String uri)\r\n\t\t\tthrows ClientProtocolException, IOException, JSONException {\r\n\t\tLog.i(TAG, \"<getJSONArrayByPost> uri:\" + uri, Log.APP);\r\n\t\tStringBuilder builder = new StringBuilder();\r\n\t\tHttpParams httpParameters = new BasicHttpParams();\r\n\t\t// Set the default socket timeout (SO_TIMEOUT) in milliseconds which is\r\n\t\t// the timeout for waiting for data.\r\n\t\tHttpConnectionParams.setConnectionTimeout(httpParameters, 5000);\r\n\t\tHttpConnectionParams.setSoTimeout(httpParameters, 10000);\r\n\t\tHttpClient client = new DefaultHttpClient(httpParameters);\r\n\t\tHttpPost post = new HttpPost(uri);\r\n\r\n\t\tHttpResponse response = client.execute(post);\r\n\r\n\t\tBufferedReader reader = new BufferedReader(new InputStreamReader(\r\n\t\t\t\tresponse.getEntity().getContent()));\r\n\t\tfor (String s = reader.readLine(); s != null; s = reader.readLine()) {\r\n\t\t\tbuilder.append(s);\r\n\t\t}\r\n\r\n\t\tString jsonString = new String(builder.toString());\r\n\r\n\t\tif (\"{}\".equals(jsonString)) {\r\n\t\t\treturn null;\r\n\t\t}\r\n\t\tLog.i(TAG, \"<getJSONArrayByPost> jsonString:\" + jsonString, Log.DATA);\r\n\t\treturn new JSONArray(jsonString);\r\n\t}\r\n\r\n\t/**\r\n\t * get方式从服务器获取json数组\r\n\t * \r\n\t * @return\r\n\t * @throws IOException\r\n\t * @throws ClientProtocolException\r\n\t * @throws JSONException\r\n\t */\r\n\tpublic static JSONObject getJSONArrayByGet(String uri)\r\n\t\t\tthrows ClientProtocolException, IOException, JSONException {\r\n\t\tLog.i(TAG, \"<getJSONArrayByGet> uri:\" + uri, Log.APP);\r\n\t\tStringBuilder builder = new StringBuilder();\r\n\t\tHttpClient client = new DefaultHttpClient();\r\n\t\tHttpGet get = new HttpGet(uri);\r\n\r\n\t\tHttpResponse response = client.execute(get);\r\n\r\n\t\tBufferedReader reader = new BufferedReader(new InputStreamReader(\r\n\t\t\t\tresponse.getEntity().getContent()));\r\n\t\tfor (String s = reader.readLine(); s != null; s = reader.readLine()) {\r\n\t\t\tbuilder.append(s);\r\n\t\t}\r\n\r\n\t\tString jsonString = new String(builder.toString());\r\n\r\n\t\tif (\"{}\".equals(jsonString)) {\r\n\t\t\treturn null;\r\n\t\t}\r\n\t\tLog.i(TAG, \"<getJSONArrayByGet> jsonString:\" + jsonString, Log.DATA);\r\n\t\treturn new JSONObject(jsonString);\r\n\t}\r\n\r\n\t/**\r\n\t * 使用post的方式，提交表单，不包括文件上传(新服务器)\r\n\t * \r\n\t * @param actionUrl\r\n\t * @param params\r\n\t * @param files\r\n\t * @return\r\n\t * @throws IOException\r\n\t */\r\n\tpublic static boolean uploadParamsByPost(String actionUrl,\r\n\t\t\tMap<String, String> params) throws IOException {\r\n\t\tLog.i(TAG, \"<uploadParamsByPost> actionUrl:\" + actionUrl + \" params:\"\r\n\t\t\t\t+ params, Log.APP);\r\n\t\tString BOUNDARY = java.util.UUID.randomUUID().toString();\r\n\t\tString PREFIX = \"--\", LINEND = \"\\r\\n\";\r\n\t\tString MULTIPART_FROM_DATA = \"multipart/form-data\";\r\n\t\tString CHARSET = \"UTF-8\";\r\n\r\n\t\tURL uri = new URL(actionUrl);\r\n\t\tHttpURLConnection conn = (HttpURLConnection) uri.openConnection();\r\n\r\n\t\tconn.setReadTimeout(10 * 1000);\r\n\t\tconn.setConnectTimeout(10 * 1000);\r\n\t\tconn.setDoInput(true);// 允许输入\r\n\t\tconn.setDoOutput(true);// 允许输出\r\n\t\tconn.setUseCaches(false);\r\n\t\tconn.setRequestMethod(\"POST\"); // Post方式\r\n\t\tconn.setRequestProperty(\"connection\", \"keep-alive\");\r\n\t\tconn.setRequestProperty(\"Charsert\", \"UTF-8\");\r\n\r\n\t\tconn.setRequestProperty(\"Content-Type\", MULTIPART_FROM_DATA\r\n\t\t\t\t+ \";boundary=\" + BOUNDARY);\r\n\r\n\t\t// 首先组拼文本类型的参数\r\n\t\tStringBuilder sb = new StringBuilder();\r\n\t\tfor (Map.Entry<String, String> entry : params.entrySet()) {\r\n\t\t\tsb.append(PREFIX);\r\n\t\t\tsb.append(BOUNDARY);\r\n\t\t\tsb.append(LINEND);\r\n\t\t\tsb.append(\"Content-Disposition: form-data; name=\\\"\"\r\n\t\t\t\t\t+ entry.getKey() + \"\\\"\" + LINEND);\r\n\t\t\tsb.append(\"Content-Type: text/plain; charset=\" + CHARSET + LINEND);\r\n\t\t\tsb.append(\"Content-Transfer-Encoding: 8bit\" + LINEND);\r\n\t\t\tsb.append(LINEND);\r\n\t\t\tsb.append(entry.getValue());\r\n\t\t\tsb.append(LINEND);\r\n\t\t}\r\n\r\n\t\tDataOutputStream outStream = new DataOutputStream(\r\n\t\t\t\tconn.getOutputStream());\r\n\t\toutStream.write(sb.toString().getBytes());\r\n\r\n\t\t// 请求结束标志\r\n\t\tbyte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINEND).getBytes();\r\n\t\toutStream.write(end_data);\r\n\t\toutStream.flush();\r\n\r\n\t\toutStream.close();\r\n\t\t// 得到响应号\r\n\t\tint res = conn.getResponseCode();\r\n\t\tif (res == 200) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\tconn.disconnect();\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\t/**\r\n\t * 使用post的方式，提交表单，不包括文件上传(老服务器写的代码使用这种方式)\r\n\t * \r\n\t * @param actionUrl\r\n\t *            :http://xxx/xxx.json\r\n\t * @param query\r\n\t *            :Helpers.combinaStr(\"login_name=#&password=#&email=#&name=\",\r\n\t *            listParams);\r\n\t * @return\r\n\t */\r\n\tpublic static JSONObject uploadParamsByPost(String actionUrl, String query) {\r\n\t\tLog.i(TAG, \"<uploadParamsByPost> actionUrl:\" + actionUrl + \" query:\"\r\n\t\t\t\t+ query, Log.APP);\r\n\t\ttry {\r\n\t\t\tURL uri = new URL(actionUrl);\r\n\t\t\tHttpURLConnection conn = (HttpURLConnection) uri.openConnection();\r\n\r\n\t\t\tconn.setReadTimeout(10 * 1000);\r\n\t\t\tconn.setDoInput(true);// 允许输入\r\n\t\t\tconn.setDoOutput(true);// 允许输出\r\n\t\t\tconn.setUseCaches(false);\r\n\t\t\tconn.setRequestMethod(\"POST\"); // Post方式\r\n\t\t\tconn.setRequestProperty(\"connection\", \"keep-alive\");\r\n\t\t\tconn.setRequestProperty(\"Charsert\", \"UTF-8\");\r\n\r\n\t\t\t// query is your body\r\n\t\t\tconn.setRequestProperty(\"Content-Type\",\r\n\t\t\t\t\t\"application/x-www-form-urlencoded\");// 请求�?\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// 必须设置\r\n\t\t\tconn.setRequestProperty(\"Content-Length\", query.toString()\r\n\t\t\t\t\t.getBytes(\"UTF-8\").length + \"\");// 注意是字节长�?\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t// 不是字符长度\r\n\t\t\tconn.getOutputStream().write(query.toString().getBytes(\"UTF-8\"));\r\n\t\t\t// 得到响应�?\r\n\t\t\tint res = conn.getResponseCode();\r\n\r\n\t\t\tif (res == HttpURLConnection.HTTP_OK) {\r\n\t\t\t\tStringBuffer stringBuffer = new StringBuffer();\r\n\t\t\t\tString readLine;\r\n\t\t\t\tBufferedReader responseReader;\r\n\t\t\t\t// 处理响应流，必须与服务器响应流输出的编码�?��\r\n\t\t\t\tresponseReader = new BufferedReader(new InputStreamReader(\r\n\t\t\t\t\t\tconn.getInputStream(), \"UTF-8\"));\r\n\t\t\t\twhile ((readLine = responseReader.readLine()) != null) {\r\n\t\t\t\t\tstringBuffer.append(readLine).append(\"/n\");\r\n\t\t\t\t}\r\n\t\t\t\tresponseReader.close();\r\n\r\n\t\t\t\treturn new JSONObject(stringBuffer.toString());\r\n\t\t\t}\r\n\r\n\t\t} catch (Exception e) {\r\n\t\t\te.printStackTrace();\r\n\t\t}\r\n\r\n\t\treturn null;\r\n\t}\r\n\r\n\tpublic int uploadFilesByPost(String actionUrl, String fileName, File file) {\r\n\t\tLog.i(TAG, \"<uploadFilesByPost> actionUrl:\" + actionUrl + \" fileName:\"\r\n\t\t\t\t+ fileName, Log.APP);\r\n\t\tString CHARSET = \"UTF-8\";\r\n\r\n\t\t// 得到响应�?\r\n\t\tint res = 0;\r\n\t\ttry {\r\n\t\t\tURL uri = new URL(actionUrl);\r\n\t\t\tHttpURLConnection conn = (HttpURLConnection) uri.openConnection();\r\n\t\t\tconn.setReadTimeout(10 * 1000);\r\n\t\t\tconn.setDoInput(true);// 允许输入\r\n\t\t\tconn.setDoOutput(true);// 允许输出\r\n\t\t\tconn.setUseCaches(false);\r\n\t\t\tconn.setRequestMethod(\"POST\"); // Post方式\r\n\t\t\tconn.setRequestProperty(\"connection\", \"keep-alive\");\r\n\t\t\tconn.setRequestProperty(\"Charsert\", \"UTF-8\");\r\n\r\n\t\t\tconn.setRequestProperty(\"Content-Type\", multipart_form_data\r\n\t\t\t\t\t+ \";boundary=\" + boundary);\r\n\r\n\t\t\t// 输出�?\r\n\t\t\tDataOutputStream outStream = new DataOutputStream(\r\n\t\t\t\t\tconn.getOutputStream());\r\n\r\n\t\t\t// 发�?文件数据\r\n\t\t\tif (file != null) {\r\n\t\t\t\tStringBuilder sb1 = new StringBuilder();\r\n\t\t\t\tsb1.append(twoHyphens);\r\n\t\t\t\tsb1.append(boundary);\r\n\t\t\t\tsb1.append(lineEnd);\r\n\t\t\t\t// actionData 是自己定义的\r\n\t\t\t\tsb1.append(\"Content-Disposition: form-data; name=\\\"actionData\\\"; filename=\\\"\"\r\n\t\t\t\t\t\t+ fileName + \"\\\"\" + lineEnd);\r\n\t\t\t\tsb1.append(\"Content-Type: application/octet-stream; charset=\"\r\n\t\t\t\t\t\t+ CHARSET + lineEnd);\r\n\t\t\t\tsb1.append(lineEnd);\r\n\t\t\t\toutStream.write(sb1.toString().getBytes());\r\n\r\n\t\t\t\tInputStream is = new FileInputStream(file);\r\n\t\t\t\tbyte[] buffer = new byte[1024];\r\n\t\t\t\tint len = 0;\r\n\t\t\t\twhile ((len = is.read(buffer)) != -1) {\r\n\t\t\t\t\toutStream.write(buffer, 0, len);\r\n\t\t\t\t}\r\n\r\n\t\t\t\tis.close();\r\n\t\t\t\toutStream.write(lineEnd.getBytes());\r\n\t\t\t}\r\n\r\n\t\t\t// 请求结束标志\r\n\t\t\tbyte[] end_data = (twoHyphens + boundary + twoHyphens + lineEnd)\r\n\t\t\t\t\t.getBytes();\r\n\r\n\t\t\toutStream.write(end_data);\r\n\t\t\toutStream.flush();\r\n\t\t\tres = conn.getResponseCode();\r\n\r\n\t\t\toutStream.close();\r\n\t\t\tconn.disconnect();\r\n\t\t} catch (IOException e) {\r\n\t\t\te.printStackTrace();\r\n\t\t}\r\n\t\treturn res;\r\n\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/utils/Preferences.java",
    "content": "package com.way.upgrade.utils;\r\n\r\nimport android.content.Context;\r\nimport android.content.SharedPreferences;\r\n\r\nimport com.way.upgrade.bean.UpgradeInfo;\r\n\r\n/**\r\n * \r\n * @author way 2014/11/3\r\n */\r\npublic class Preferences {\r\n\tpublic static final String PREFERENCES_NAME = \"com_upgrade_manager\";\r\n\tpublic static final String KEY_DOWNLOAD_ID = \"downloadId\";\r\n\tprotected static final String KEY_APPKEY = \"key\";\r\n\tprotected static final String KEY_VERSION = \"version\";\r\n\tprotected static final String KEY_URL = \"url\";\r\n\tprotected static final String KEY_DESCRIPTION = \"description\";\r\n\tprotected static final String KEY_DOWNLOAD_SIZE = \"downloadSize\";\r\n\tprotected static final String KEY_DOWNLOAD_PATH = \"download_path\";\r\n\tprotected static final String KEY_DOWNLOAD_STATUS = \"download_status\";\r\n\r\n\tpublic static void setDownloadId(Context context, long downloadId) {\r\n\t\tSharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME,\r\n\t\t\t\tContext.MODE_APPEND);\r\n\t\tSharedPreferences.Editor editor = pref.edit();\r\n\t\teditor.putLong(KEY_DOWNLOAD_ID, downloadId);\r\n\t\teditor.commit();\r\n\t}\r\n\r\n\tpublic static long getDownloadId(Context context) {\r\n\t\tSharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME,\r\n\t\t\t\tContext.MODE_APPEND);\r\n\t\treturn pref.getLong(KEY_DOWNLOAD_ID, -1);\r\n\t}\r\n\r\n\tpublic static void setUpgradeInfo(Context context, UpgradeInfo upgrade) {\r\n\t\tSharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME,\r\n\t\t\t\tContext.MODE_APPEND);\r\n\t\tSharedPreferences.Editor editor = pref.edit();\r\n\t\teditor.putString(KEY_APPKEY, upgrade.getKey());\r\n\t\teditor.putString(KEY_VERSION, upgrade.getVersion());\r\n\t\teditor.putString(KEY_URL, upgrade.getUrl());\r\n\t\teditor.putString(KEY_DESCRIPTION, upgrade.getDescription());\r\n\t\teditor.putLong(KEY_DOWNLOAD_SIZE, upgrade.getDownloadSize());\r\n\t\teditor.commit();\r\n\t}\r\n\r\n\tpublic static UpgradeInfo getUpgradeInfo(Context context) {\r\n\t\tUpgradeInfo upgradeInfo = new UpgradeInfo();\r\n\t\tSharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME,\r\n\t\t\t\tContext.MODE_APPEND);\r\n\t\tupgradeInfo.setKey(pref.getString(KEY_APPKEY, \"\"));\r\n\t\tupgradeInfo.setVersion(pref.getString(KEY_VERSION, \"\"));\r\n\t\tupgradeInfo.setUrl(pref.getString(KEY_URL, \"\"));\r\n\t\tupgradeInfo.setDescription(pref.getString(KEY_DESCRIPTION, \"\"));\r\n\t\tupgradeInfo.setDownloadSize(pref.getLong(KEY_DOWNLOAD_SIZE, -1));\r\n\t\treturn upgradeInfo;\r\n\t}\r\n\r\n\tpublic static void setDownloadPath(Context context, String downloadPath) {\r\n\t\tSharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME,\r\n\t\t\t\tContext.MODE_APPEND);\r\n\t\tSharedPreferences.Editor editor = pref.edit();\r\n\t\teditor.putString(KEY_DOWNLOAD_PATH, downloadPath);\r\n\t\teditor.commit();\r\n\t}\r\n\r\n\tpublic static String getDownloadPath(Context context) {\r\n\t\tSharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME,\r\n\t\t\t\tContext.MODE_APPEND);\r\n\t\treturn pref.getString(KEY_DOWNLOAD_PATH, \"\");\r\n\t}\r\n\r\n\tpublic static void removeAll(Context context) {\r\n\t\tSharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME,\r\n\t\t\t\tContext.MODE_APPEND);\r\n\t\tSharedPreferences.Editor editor = pref.edit();\r\n\t\teditor.clear();\r\n\t\teditor.commit();\r\n\t}\r\n\r\n\tpublic static void setDownloadStatus(Context context, int downloadId) {\r\n\t\tSharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME,\r\n\t\t\t\tContext.MODE_APPEND);\r\n\t\tSharedPreferences.Editor editor = pref.edit();\r\n\t\teditor.putInt(KEY_DOWNLOAD_STATUS, downloadId);\r\n\t\teditor.commit();\r\n\t}\r\n\r\n\tpublic static int getDownloadStatus(Context context) {\r\n\t\tSharedPreferences pref = context.getSharedPreferences(PREFERENCES_NAME,\r\n\t\t\t\tContext.MODE_APPEND);\r\n\t\treturn pref.getInt(KEY_DOWNLOAD_STATUS, -1);\r\n\t}\r\n}\r\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/utils/Utils.java",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.way.upgrade.utils;\n\nimport java.io.Closeable;\nimport java.io.InterruptedIOException;\nimport java.util.List;\n\nimport android.database.Cursor;\nimport android.os.Build;\nimport android.os.ParcelFileDescriptor;\nimport android.text.TextUtils;\n\npublic class Utils {\n\tprivate static final String TAG = \"Utils\";\n\tprivate static final String DEBUG_TAG = \"GalleryDebug\";\n\n\tprivate static final long POLY64REV = 0x95AC9329AC4BC9B5L;\n\tprivate static final long INITIALCRC = 0xFFFFFFFFFFFFFFFFL;\n\n\tprivate static long[] sCrcTable = new long[256];\n\n\tprivate static final boolean IS_DEBUG_BUILD = Build.TYPE.equals(\"eng\")\n\t\t\t|| Build.TYPE.equals(\"userdebug\");\n\n\tprivate static final String MASK_STRING = \"********************************\";\n\n\t// Throws AssertionError if the input is false.\n\tpublic static void assertTrue(boolean cond) {\n\t\tif (!cond) {\n\t\t\tthrow new AssertionError();\n\t\t}\n\t}\n\n\t// Throws AssertionError with the message. We had a method having the form\n\t// assertTrue(boolean cond, String message, Object ... args);\n\t// However a call to that method will cause memory allocation even if the\n\t// condition is false (due to autoboxing generated by \"Object ... args\"),\n\t// so we don't use that anymore.\n\tpublic static void fail(String message, Object... args) {\n\t\tthrow new AssertionError(args.length == 0 ? message : String.format(\n\t\t\t\tmessage, args));\n\t}\n\n\t// Throws NullPointerException if the input is null.\n\tpublic static <T> T checkNotNull(T object) {\n\t\tif (object == null)\n\t\t\tthrow new NullPointerException();\n\t\treturn object;\n\t}\n\n\t// Returns true if two input Object are both null or equal\n\t// to each other.\n\tpublic static boolean equals(Object a, Object b) {\n\t\treturn (a == b) || (a == null ? false : a.equals(b));\n\t}\n\n\t// Returns the next power of two.\n\t// Returns the input if it is already power of 2.\n\t// Throws IllegalArgumentException if the input is <= 0 or\n\t// the answer overflows.\n\tpublic static int nextPowerOf2(int n) {\n\t\tif (n <= 0 || n > (1 << 30))\n\t\t\tthrow new IllegalArgumentException(\"n is invalid: \" + n);\n\t\tn -= 1;\n\t\tn |= n >> 16;\n\t\tn |= n >> 8;\n\t\tn |= n >> 4;\n\t\tn |= n >> 2;\n\t\tn |= n >> 1;\n\t\treturn n + 1;\n\t}\n\n\t// Returns the previous power of two.\n\t// Returns the input if it is already power of 2.\n\t// Throws IllegalArgumentException if the input is <= 0\n\tpublic static int prevPowerOf2(int n) {\n\t\tif (n <= 0)\n\t\t\tthrow new IllegalArgumentException();\n\t\treturn Integer.highestOneBit(n);\n\t}\n\n\t// Returns the input value x clamped to the range [min, max].\n\tpublic static int clamp(int x, int min, int max) {\n\t\tif (x > max)\n\t\t\treturn max;\n\t\tif (x < min)\n\t\t\treturn min;\n\t\treturn x;\n\t}\n\n\t// Returns the input value x clamped to the range [min, max].\n\tpublic static float clamp(float x, float min, float max) {\n\t\tif (x > max)\n\t\t\treturn max;\n\t\tif (x < min)\n\t\t\treturn min;\n\t\treturn x;\n\t}\n\n\t// Returns the input value x clamped to the range [min, max].\n\tpublic static long clamp(long x, long min, long max) {\n\t\tif (x > max)\n\t\t\treturn max;\n\t\tif (x < min)\n\t\t\treturn min;\n\t\treturn x;\n\t}\n\n\tpublic static boolean isOpaque(int color) {\n\t\treturn color >>> 24 == 0xFF;\n\t}\n\n\tpublic static void swap(int[] array, int i, int j) {\n\t\tint temp = array[i];\n\t\tarray[i] = array[j];\n\t\tarray[j] = temp;\n\t}\n\n\t/**\n\t * A function thats returns a 64-bit crc for string\n\t * \n\t * @param in\n\t *            input string\n\t * @return a 64-bit crc value\n\t */\n\tpublic static final long crc64Long(String in) {\n\t\tif (in == null || in.length() == 0) {\n\t\t\treturn 0;\n\t\t}\n\t\treturn crc64Long(getBytes(in));\n\t}\n\n\tstatic {\n\t\t// http://bioinf.cs.ucl.ac.uk/downloads/crc64/crc64.c\n\t\tlong part;\n\t\tfor (int i = 0; i < 256; i++) {\n\t\t\tpart = i;\n\t\t\tfor (int j = 0; j < 8; j++) {\n\t\t\t\tlong x = ((int) part & 1) != 0 ? POLY64REV : 0;\n\t\t\t\tpart = (part >> 1) ^ x;\n\t\t\t}\n\t\t\tsCrcTable[i] = part;\n\t\t}\n\t}\n\n\tpublic static final long crc64Long(byte[] buffer) {\n\t\tlong crc = INITIALCRC;\n\t\tfor (int k = 0, n = buffer.length; k < n; ++k) {\n\t\t\tcrc = sCrcTable[(((int) crc) ^ buffer[k]) & 0xff] ^ (crc >> 8);\n\t\t}\n\t\treturn crc;\n\t}\n\n\tpublic static byte[] getBytes(String in) {\n\t\tbyte[] result = new byte[in.length() * 2];\n\t\tint output = 0;\n\t\tfor (char ch : in.toCharArray()) {\n\t\t\tresult[output++] = (byte) (ch & 0xFF);\n\t\t\tresult[output++] = (byte) (ch >> 8);\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic static void closeSilently(Closeable c) {\n\t\tif (c == null)\n\t\t\treturn;\n\t\ttry {\n\t\t\tc.close();\n\t\t} catch (Throwable t) {\n\t\t\tLog.w(TAG, \"close fail\", Log.APP);\n\t\t}\n\t}\n\n\tpublic static int compare(long a, long b) {\n\t\treturn a < b ? -1 : a == b ? 0 : 1;\n\t}\n\n\tpublic static int ceilLog2(float value) {\n\t\tint i;\n\t\tfor (i = 0; i < 31; i++) {\n\t\t\tif ((1 << i) >= value)\n\t\t\t\tbreak;\n\t\t}\n\t\treturn i;\n\t}\n\n\tpublic static int floorLog2(float value) {\n\t\tint i;\n\t\tfor (i = 0; i < 31; i++) {\n\t\t\tif ((1 << i) > value)\n\t\t\t\tbreak;\n\t\t}\n\t\treturn i - 1;\n\t}\n\n\tpublic static void closeSilently(ParcelFileDescriptor fd) {\n\t\ttry {\n\t\t\tif (fd != null)\n\t\t\t\tfd.close();\n\t\t} catch (Throwable t) {\n\t\t\tLog.w(TAG, \"fail to close\", Log.APP);\n\t\t}\n\t}\n\n\tpublic static void closeSilently(Cursor cursor) {\n\t\ttry {\n\t\t\tif (cursor != null)\n\t\t\t\tcursor.close();\n\t\t} catch (Throwable t) {\n\t\t\tLog.w(TAG, \"fail to close\", Log.APP);\n\t\t}\n\t}\n\n\tpublic static float interpolateAngle(float source, float target,\n\t\t\tfloat progress) {\n\t\t// interpolate the angle from source to target\n\t\t// We make the difference in the range of [-179, 180], this is the\n\t\t// shortest path to change source to target.\n\t\tfloat diff = target - source;\n\t\tif (diff < 0)\n\t\t\tdiff += 360f;\n\t\tif (diff > 180)\n\t\t\tdiff -= 360f;\n\n\t\tfloat result = source + diff * progress;\n\t\treturn result < 0 ? result + 360f : result;\n\t}\n\n\tpublic static float interpolateScale(float source, float target,\n\t\t\tfloat progress) {\n\t\treturn source + progress * (target - source);\n\t}\n\n\tpublic static String ensureNotNull(String value) {\n\t\treturn value == null ? \"\" : value;\n\t}\n\n\tpublic static String ensureNotNull(Object obj) {\n\t\treturn obj == null ? \"\" : obj.toString();\n\t}\n\n\tpublic static float parseFloatSafely(String content, float defaultValue) {\n\t\tif (content == null)\n\t\t\treturn defaultValue;\n\t\ttry {\n\t\t\treturn Float.parseFloat(content);\n\t\t} catch (NumberFormatException e) {\n\t\t\treturn defaultValue;\n\t\t}\n\t}\n\n\tpublic static int parseIntSafely(String content, int defaultValue) {\n\t\tif (content == null)\n\t\t\treturn defaultValue;\n\t\ttry {\n\t\t\treturn Integer.parseInt(content);\n\t\t} catch (NumberFormatException e) {\n\t\t\treturn defaultValue;\n\t\t}\n\t}\n\n\tpublic static boolean isNullOrEmpty(String exifMake) {\n\t\treturn TextUtils.isEmpty(exifMake);\n\t}\n\n\tpublic static void waitWithoutInterrupt(Object object) {\n\t\ttry {\n\t\t\tobject.wait();\n\t\t} catch (InterruptedException e) {\n\t\t\tLog.w(TAG, \"unexpected interrupt: \" + object, Log.APP);\n\t\t}\n\t}\n\n\tpublic static boolean handleInterrruptedException(Throwable e) {\n\t\t// A helper to deal with the interrupt exception\n\t\t// If an interrupt detected, we will setup the bit again.\n\t\tif (e instanceof InterruptedIOException\n\t\t\t\t|| e instanceof InterruptedException) {\n\t\t\tThread.currentThread().interrupt();\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * @return String with special XML characters escaped.\n\t */\n\tpublic static String escapeXml(String s) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (int i = 0, len = s.length(); i < len; ++i) {\n\t\t\tchar c = s.charAt(i);\n\t\t\tswitch (c) {\n\t\t\tcase '<':\n\t\t\t\tsb.append(\"&lt;\");\n\t\t\t\tbreak;\n\t\t\tcase '>':\n\t\t\t\tsb.append(\"&gt;\");\n\t\t\t\tbreak;\n\t\t\tcase '\\\"':\n\t\t\t\tsb.append(\"&quot;\");\n\t\t\t\tbreak;\n\t\t\tcase '\\'':\n\t\t\t\tsb.append(\"&#039;\");\n\t\t\t\tbreak;\n\t\t\tcase '&':\n\t\t\t\tsb.append(\"&amp;\");\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tsb.append(c);\n\t\t\t}\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tpublic static String[] copyOf(String[] source, int newSize) {\n\t\tString[] result = new String[newSize];\n\t\tnewSize = Math.min(source.length, newSize);\n\t\tSystem.arraycopy(source, 0, result, 0, newSize);\n\t\treturn result;\n\t}\n\n\t// Mask information for debugging only. It returns\n\t// <code>info.toString()</code> directly\n\t// for debugging build (i.e., 'eng' and 'userdebug') and returns a mask\n\t// (\"****\")\n\t// in release build to protect the information (e.g. for privacy issue).\n\tpublic static String maskDebugInfo(Object info) {\n\t\tif (info == null)\n\t\t\treturn null;\n\t\tString s = info.toString();\n\t\tint length = Math.min(s.length(), MASK_STRING.length());\n\t\treturn IS_DEBUG_BUILD ? s : MASK_STRING.substring(0, length);\n\t}\n\n\t// This method should be ONLY used for debugging.\n\tpublic static void debug(String message, Object... args) {\n\t\tLog.v(DEBUG_TAG, String.format(message, args), Log.APP);\n\t}\n\n\t/**\n\t * combina the url. usr the param list repleace the [#] in the url.\n\t * \n\t * @param str\n\t * @param params\n\t * @return\n\t * @exception RuntimeException\n\t *                May throw the runtime exception while the method was done.\n\t * @author MichaelHuang\n\t */\n\tpublic static String combinaStr(String str, List<String> params)\n\t\t\tthrows RuntimeException {\n\t\ttry {\n\t\t\tString[] strs = str.split(\"[#]\");\n\t\t\tString newStr = \"\";\n\t\t\tfor (int i = 0; i < strs.length; i++) {\n\t\t\t\tString itemUrl = strs[i];\n\t\t\t\tnewStr += itemUrl;\n\t\t\t\tif (i >= 0 && i < params.size())\n\t\t\t\t\tnewStr += params.get(i);\n\t\t\t}\n\t\t\tLog.i(TAG, \"<combinaStr> newStr:\" + newStr, Log.DATA);\n\t\t\treturn newStr;\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/utils/thread/Future.java",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.way.upgrade.utils.thread;\n\n// This Future differs from the java.util.concurrent.Future in these aspects:\n//\n// - Once cancel() is called, isCancelled() always returns true. It is a sticky\n//   flag used to communicate to the implementation. The implmentation may\n//   ignore that flag. Regardless whether the Future is cancelled, a return\n//   value will be provided to get(). The implementation may choose to return\n//   null if it finds the Future is cancelled.\n//\n// - get() does not throw exceptions.\n//\npublic interface Future<T> {\n\tpublic void cancel();\n\n\tpublic boolean isCancelled();\n\n\tpublic boolean isDone();\n\n\tpublic T get();\n\n\tpublic void waitDone();\n}\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/utils/thread/FutureListener.java",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.way.upgrade.utils.thread;\n/**\n * \n * @author way\n *\n * @param <T>\n */\npublic interface FutureListener<T> {\n\tpublic void onFutureDone(Future<T> future);\n}\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/utils/thread/PriorityThreadFactory.java",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.way.upgrade.utils.thread;\n\nimport android.os.Process;\n\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * A thread factory that creates threads with a given thread priority.\n */\npublic class PriorityThreadFactory implements ThreadFactory {\n\n\tprivate final int mPriority;\n\tprivate final AtomicInteger mNumber = new AtomicInteger();\n\tprivate final String mName;\n\n\tpublic PriorityThreadFactory(String name, int priority) {\n\t\tmName = name;\n\t\tmPriority = priority;\n\t}\n\n\t@Override\n\tpublic Thread newThread(Runnable r) {\n\t\treturn new Thread(r, mName + '-' + mNumber.getAndIncrement()) {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tProcess.setThreadPriority(mPriority);\n\t\t\t\tsuper.run();\n\t\t\t}\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "WayHoo/Upgrade-lib/com/way/upgrade/utils/thread/ThreadPool.java",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.way.upgrade.utils.thread;\n\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport com.way.upgrade.utils.Log;\n/**\n * \n * @author way\n *\n */\npublic class ThreadPool {\n\tprivate static final String TAG = \"ThreadPool\";\n\tprivate static final int CORE_POOL_SIZE = 4;\n\tprivate static final int MAX_POOL_SIZE = 8;\n\tprivate static final int KEEP_ALIVE_TIME = 20; // 10 seconds\n\n\t// Resource type\n\tpublic static final int MODE_NONE = 0;\n\tpublic static final int MODE_CPU = 1;\n\tpublic static final int MODE_NETWORK = 2;\n\n\tpublic static final JobContext JOB_CONTEXT_STUB = new JobContextStub();\n\n\t// M: in order to take full advantage of MT6589 CPU Core, change cpuCounter\n\t// to 4 from 2\n\tResourceCounter mCpuCounter = new ResourceCounter(4);\n\tResourceCounter mNetworkCounter = new ResourceCounter(6);\n\n\t// A Job is like a Callable, but it has an addition JobContext parameter.\n\tpublic interface Job<T> {\n\t\tpublic T run(JobContext jc);\n\t}\n\n\tpublic interface JobContext {\n\t\tboolean isCancelled();\n\n\t\tvoid setCancelListener(CancelListener listener);\n\n\t\tboolean setMode(int mode);\n\t}\n\n\tprivate static class JobContextStub implements JobContext {\n\t\t@Override\n\t\tpublic boolean isCancelled() {\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic void setCancelListener(CancelListener listener) {\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean setMode(int mode) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tpublic interface CancelListener {\n\t\tpublic void onCancel();\n\t}\n\n\tprivate static class ResourceCounter {\n\t\tpublic int value;\n\n\t\tpublic ResourceCounter(int v) {\n\t\t\tvalue = v;\n\t\t}\n\t}\n\n\tprivate final Executor mExecutor;\n\n\tpublic ThreadPool() {\n\t\tthis(CORE_POOL_SIZE, MAX_POOL_SIZE);\n\t}\n\n\tpublic ThreadPool(int initPoolSize, int maxPoolSize) {\n\t\tmExecutor = new ThreadPoolExecutor(initPoolSize, maxPoolSize,\n\t\t\t\tKEEP_ALIVE_TIME, TimeUnit.SECONDS,\n\t\t\t\tnew LinkedBlockingQueue<Runnable>(), new PriorityThreadFactory(\n\t\t\t\t\t\t\"thread-pool\",\n\t\t\t\t\t\tandroid.os.Process.THREAD_PRIORITY_BACKGROUND));\n\t}\n\n\t// Submit a job to the thread pool. The listener will be called when the\n\t// job is finished (or cancelled).\n\tpublic <T> Future<T> submit(Job<T> job, FutureListener<T> listener,\n\t\t\tint threadMode) {\n\t\tLog.i(TAG, \"<submit> Job:\" + job, Log.APP);\n\t\tWorker<T> w = new Worker<T>(job, listener, threadMode);\n\t\tmExecutor.execute(w);\n\t\treturn w;\n\t}\n\n\tpublic <T> Future<T> submit(Job<T> job) {\n\t\treturn submit(job, null, MODE_CPU);\n\t}\n\n\tprivate class Worker<T> implements Runnable, Future<T>, JobContext {\n\t\tprivate static final String TAG = \"Worker\";\n\t\tprivate Job<T> mJob;\n\t\tprivate FutureListener<T> mListener;\n\t\tprivate CancelListener mCancelListener;\n\t\tprivate ResourceCounter mWaitOnResource;\n\t\tprivate volatile boolean mIsCancelled;\n\t\tprivate boolean mIsDone;\n\t\tprivate T mResult;\n\t\tprivate int mMode;\n\t\tprivate int mTargetMode;\n\n\t\tpublic Worker(Job<T> job, FutureListener<T> listener, int threadMode) {\n\t\t\tmJob = job;\n\t\t\tmListener = listener;\n\t\t\tmTargetMode = threadMode;\n\t\t}\n\n\t\t// This is called by a thread in the thread pool.\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tLog.i(TAG, \"<run> start\", Log.APP);\n\t\t\tT result = null;\n\n\t\t\t// A job is in CPU mode by default. setMode returns false\n\t\t\t// if the job is cancelled.\n\t\t\tif (setMode(mTargetMode)) {\n\t\t\t\ttry {\n\t\t\t\t\tresult = mJob.run(this);\n\t\t\t\t} catch (Throwable ex) {\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsynchronized (this) {\n\t\t\t\tsetMode(MODE_NONE);\n\t\t\t\tmResult = result;\n\t\t\t\tmIsDone = true;\n\t\t\t\tnotifyAll();\n\t\t\t}\n\t\t\tif (mListener != null)\n\t\t\t\tmListener.onFutureDone(this);\n\t\t\tLog.i(TAG, \"<run> end\", Log.APP);\n\t\t}\n\n\t\t// Below are the methods for Future.\n\t\t@Override\n\t\tpublic synchronized void cancel() {\n\t\t\tif (mIsCancelled)\n\t\t\t\treturn;\n\t\t\tmIsCancelled = true;\n\t\t\tif (mWaitOnResource != null) {\n\t\t\t\tsynchronized (mWaitOnResource) {\n\t\t\t\t\tmWaitOnResource.notifyAll();\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (mCancelListener != null) {\n\t\t\t\tmCancelListener.onCancel();\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isCancelled() {\n\t\t\treturn mIsCancelled;\n\t\t}\n\n\t\t@Override\n\t\tpublic synchronized boolean isDone() {\n\t\t\treturn mIsDone;\n\t\t}\n\n\t\t@Override\n\t\tpublic synchronized T get() {\n\t\t\twhile (!mIsDone) {\n\t\t\t\ttry {\n\t\t\t\t\twait();\n\t\t\t\t} catch (Exception ex) {\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn mResult;\n\t\t}\n\n\t\t@Override\n\t\tpublic void waitDone() {\n\t\t\tget();\n\t\t}\n\n\t\t// Below are the methods for JobContext (only called from the\n\t\t// thread running the job)\n\t\t@Override\n\t\tpublic synchronized void setCancelListener(CancelListener listener) {\n\t\t\tmCancelListener = listener;\n\t\t\tif (mIsCancelled && mCancelListener != null) {\n\t\t\t\tmCancelListener.onCancel();\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean setMode(int mode) {\n\t\t\t// Release old resource\n\t\t\tLog.i(TAG, \"<setMode> start mode:\" + mode, Log.APP);\n\t\t\tResourceCounter rc = modeToCounter(mMode);\n\t\t\tif (rc != null)\n\t\t\t\treleaseResource(rc);\n\t\t\tmMode = MODE_NONE;\n\n\t\t\t// Acquire new resource\n\t\t\trc = modeToCounter(mode);\n\t\t\tif (rc != null) {\n\t\t\t\tif (!acquireResource(rc)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tmMode = mode;\n\t\t\t}\n\t\t\tLog.i(TAG, \"<setMode> end\", Log.APP);\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate ResourceCounter modeToCounter(int mode) {\n\t\t\tif (mode == MODE_CPU) {\n\t\t\t\tLog.i(TAG, \"<modeToCounter> MODE_CPU\", Log.APP);\n\t\t\t\treturn mCpuCounter;\n\t\t\t} else if (mode == MODE_NETWORK) {\n\t\t\t\tLog.i(TAG, \"<modeToCounter> MODE_NETWORK\", Log.APP);\n\t\t\t\treturn mNetworkCounter;\n\t\t\t} else {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tprivate boolean acquireResource(ResourceCounter counter) {\n\t\t\tLog.i(TAG, \"<acquireResource> start\", Log.APP);\n\t\t\twhile (true) {\n\t\t\t\tsynchronized (this) {\n\t\t\t\t\tif (mIsCancelled) {\n\t\t\t\t\t\tmWaitOnResource = null;\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tmWaitOnResource = counter;\n\t\t\t\t}\n\n\t\t\t\tsynchronized (counter) {\n\t\t\t\t\tif (counter.value > 0) {\n\t\t\t\t\t\tcounter.value--;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tcounter.wait();\n\t\t\t\t\t\t} catch (InterruptedException ex) {\n\t\t\t\t\t\t\t// ignore.\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsynchronized (this) {\n\t\t\t\tmWaitOnResource = null;\n\t\t\t}\n\t\t\tLog.i(TAG, \"<acquireResource> end\", Log.APP);\n\t\t\treturn true;\n\t\t}\n\n\t\tprivate void releaseResource(ResourceCounter counter) {\n\t\t\tLog.i(TAG, \"<releaseResource> start\", Log.APP);\n\t\t\tsynchronized (counter) {\n\t\t\t\tcounter.value++;\n\t\t\t\tcounter.notifyAll();\n\t\t\t}\n\t\t\tLog.i(TAG, \"<releaseResource> end\", Log.APP);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "WayHoo/jni/Android.mk",
    "content": "# Copyright (C) 2009 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nLOCAL_PATH := $(call my-dir)\n\ninclude $(CLEAR_VARS)\n\nLOCAL_MODULE    := libfrostedGlass\nLOCAL_SRC_FILES := frostedGlass.c bitmapPort.c imageProcess.c\nLOCAL_LDLIBS    := -llog -ljnigraphics\n#LOCAL_SHARED_LIBRARIES += \\\n#liblog libjnigraphics\n\ninclude $(BUILD_SHARED_LIBRARY)\n"
  },
  {
    "path": "WayHoo/jni/Application.mk",
    "content": "APP_ABI\t\t:= armeabi armeabi-v7a\nAPP_PLATFORM:= android-21\nAPP_OPTIM\t:= release"
  },
  {
    "path": "WayHoo/jni/bitmapPort.c",
    "content": "\n#include <string.h>\n#include <jni.h>\n#include <malloc.h>\n#include <android/bitmap.h>\n#include \"constants.h\"\n#include \"bitmapPort.h\"\n\n\nstatic AndroidBitmapInfo imageInfo;\nstatic uint8_t* imageData;\n\n\n//\nint allocateMemory() {\n\tint ret = 0;\n\tsize_t size = 4*imageInfo.width*imageInfo.height;//color-int\n\timageData = (uint8_t*)malloc(size);\n\tif (imageData == NULL) {\n\t\tret = -1;\n\t}\n\treturn ret;\n}\n\nvoid freeMemory(){\n\tif(imageData != NULL){\n\t\tfree(imageData);\n\t\timageData = NULL;\n\t}\n}\n\nvoid getUnifiedImageData(jobject bitmap, JNIEnv *env) {\n\tif (imageData == NULL)\n\t\treturn;\n\n\tswitch (imageInfo.format) {\n\tcase ANDROID_BITMAP_FORMAT_RGBA_8888:\n\t\tLOGE(\"get ARGB 8888\");\n\t\tgetARGB8888Data(bitmap, env);\n\t\tbreak;\n\tcase ANDROID_BITMAP_FORMAT_RGBA_4444:\n\t\tLOGE(\"get ARGB 4444\");\n\t\tgetARGB4444Data(bitmap, env);\n\t\tbreak;\n\tcase ANDROID_BITMAP_FORMAT_RGB_565:\n\t\tLOGE(\"get rgb 565\");\n\t\tgetRGB565Data(bitmap, env);\n\t\tbreak;\n\tcase ANDROID_BITMAP_FORMAT_A_8:\n\t\tLOGE(\"get alpha 8\");\n\t\tgetAlpha8Data(bitmap, env);\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n}\n\nvoid getARGB8888Data(jobject bitmap, JNIEnv *env)\n{\n\tint ret, row, col;\n\tvoid *pixelscolor;\n\tif ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixelscolor)) < 0) {\n\t\tLOGE(\"AndroidBitmap_lockPixels() failed ! error=%d\", ret);\n\t}\n\tuint8_t *pData = imageData;\n\tuint32_t colorValue = 0;\n\tfor (row = 0; row < imageInfo.height; row++) {\n\t\tuint32_t * line = (uint32_t *) pixelscolor;\n\t\tfor (col = 0; col < imageInfo.width; col++) {\n\t\t\tcolorValue = line[col];\n\n\t\t\t*pData++ = colorValue & ARGB8888_MASK_BLUE;\n\t\t\t*pData++ = (colorValue & ARGB8888_MASK_GREEN) >> 8;\n\t\t\t*pData++ = (colorValue & ARGB8888_MASK_RED) >> 16;\n\t\t\t*pData++ = (colorValue & ARGB8888_MASK_ALPHA)>>24;\n\t\t}\n\t\t//stride\n\t\tpixelscolor = (uint8_t*)pixelscolor + imageInfo.stride;\n\t}\n\tAndroidBitmap_unlockPixels(env, bitmap);\n}\n\nvoid getARGB4444Data(jobject bitmap, JNIEnv *env)\n{\n\tint ret, row, col;\n\tvoid *pixelscolor;\n\tif ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixelscolor)) < 0) {\n\t\tLOGE(\"AndroidBitmap_lockPixels() failed ! error=%d\", ret);\n\t}\n\tuint8_t *pData = imageData;\n\tfor (row = 0; row < imageInfo.height; row++) {\n\t\tuint16_t *line = (uint16_t *) pixelscolor;\n\t\tfor (col = 0; col < imageInfo.width; col++) {\n\t\t\t//*pData++ = (line[col] & ARGB4444_MASK_RED) << 4;\n\t\t\t//*pData++ = (line[col] & ARGB4444_MASK_GREEN) << 4;\n\t\t\t//*pData++ = (line[col] & ARGB4444_MASK_BLUE) << 4;\n\t\t}\n\t\tpixelscolor = (uint8_t*)pixelscolor + imageInfo.stride;\n\t}\n\tAndroidBitmap_unlockPixels(env, bitmap);\n}\n\nvoid getRGB565Data(jobject bitmap, JNIEnv *env)\n{\n\tint ret, row, col;\n\tvoid *pixelscolor;\n\tif ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixelscolor)) < 0) {\n\t\tLOGE(\"AndroidBitmap_lockPixels() failed ! error=%d\", ret);\n\t}\n\tuint8_t *pData = imageData;\n\tfor (row = 0; row < imageInfo.height; row++) {\n\t\tuint16_t *line = (uint16_t *) pixelscolor;\n\t\tfor (col = 0; col < imageInfo.width; col++) {\n\t\t\t*pData++ = ((line[col] & RGB565_MASK_RED) >> 11) << 3;\n\t\t\t*pData++ = ((line[col] & RGB565_MASK_GREEN) >> 5) << 2;\n\t\t\t*pData++ = (line[col] & RGB565_MASK_BLUE) << 3;\n\t\t}\n\t\tpixelscolor = (uint8_t*)pixelscolor + imageInfo.stride;\n\t}\n\tAndroidBitmap_unlockPixels(env, bitmap);\n}\n\nvoid getAlpha8Data(jobject bitmap, JNIEnv *env)\n{\n\tint ret, row, col;\n\tvoid *pixelscolor;\n\tif ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixelscolor)) < 0) {\n\t\tLOGE(\"AndroidBitmap_lockPixels() failed ! error=%d\", ret);\n\t}\n\tuint8_t *pData = imageData;\n\tfor (row = 0; row < imageInfo.height; row++) {\n\t\tuint8_t *line = (uint8_t *) pixelscolor;\n\t\tfor (col = 0; col < imageInfo.width; col++) {\n\t\t\t*pData++ = line[col];\n\t\t\t*pData++ = line[col];\n\t\t\t*pData++ = line[col];\n\t\t}\n\t\tpixelscolor = (uint8_t*)pixelscolor + imageInfo.stride;\n\t}\n\tAndroidBitmap_unlockPixels(env, bitmap);\n}\n\nvoid setUnifiedImageData(jobject bitmap, JNIEnv *env)\n{\n\tswitch (imageInfo.format) {\n\tcase ANDROID_BITMAP_FORMAT_RGBA_8888:\n\t\tLOGE(\"set ARGB 8888\");\n\t\tsetARGB8888Data(bitmap, env);\n\t\tbreak;\n\tcase ANDROID_BITMAP_FORMAT_RGBA_4444:\n\t\tLOGE(\"set ARGB 4444\");\n\t\tsetARGB4444Data(bitmap, env);\n\t\tbreak;\n\tcase ANDROID_BITMAP_FORMAT_RGB_565:\n\t\tLOGE(\"set rgb 565\");\n\t\tsetRGB565Data(bitmap, env);\n\t\tbreak;\n\tcase ANDROID_BITMAP_FORMAT_A_8:\n\t\tLOGE(\"set alpha 8\");\n\t\tsetAlpha8Data(bitmap, env);\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n}\n\nvoid setARGB8888Data(jobject bitmap, JNIEnv *env)\n{\n\tint ret, row, col;\n\tuint32_t red, green, blue, alpha;\n\tvoid *pixelscolor;\n\tif ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixelscolor)) < 0) {\n\t\tLOGE(\"AndroidBitmap_lockPixels() failed ! error=%d\", ret);\n\t}\n\tuint8_t *pData = imageData;\n\tfor (row = 0; row < imageInfo.height; row++) {\n\t\tuint32_t *line = (uint32_t *) pixelscolor;\n\t\tfor (col = 0; col < imageInfo.width; col++) {\n\n\t\t\tblue  = *pData++;\n\t\t\tgreen = *pData++;\n\t\t\tred   = *pData++;\n\n\t\t\talpha = *pData++;\n\n\t\t\tline[col] = ((alpha<<24) & ARGB8888_MASK_ALPHA)//(line[col] & ARGB8888_MASK_ALPHA)\n\t\t\t\t | ((red << 16) & ARGB8888_MASK_RED)\n\t\t\t\t| ((green << 8) & ARGB8888_MASK_GREEN)\n\t\t\t\t| (blue & ARGB8888_MASK_BLUE);\n\t\t}\n\t\tpixelscolor = (uint8_t*)pixelscolor + imageInfo.stride;\n\t}\n\tAndroidBitmap_unlockPixels(env, bitmap);\n}\n\nvoid setARGB4444Data(jobject bitmap, JNIEnv *env)\n{}\n\nvoid setRGB565Data(jobject bitmap, JNIEnv *env)\n{\n\tint ret, row, col;\n\tuint16_t red, green, blue;\n\tvoid *pixelscolor;\n\tif ((ret = AndroidBitmap_lockPixels(env, bitmap, &pixelscolor)) < 0) {\n\t\tLOGE(\"AndroidBitmap_lockPixels() failed ! error=%d\", ret);\n\t}\n\tuint8_t *pData = imageData;\n\tfor (row = 0; row < imageInfo.height; row++) {\n\t\tuint16_t *line = (uint16_t *) pixelscolor;\n\t\tfor (col = 0; col < imageInfo.width; col++) {\n\t\t\tred = *pData++;\n\t\t\tgreen = *pData++;\n\t\t\tblue = *pData++;\n\t\t\tline[col] = ((red << 8) & RGB565_MASK_RED)\n\t\t\t\t| ((green << 3) & RGB565_MASK_GREEN)\n\t\t\t\t| (blue >> 3);\n\t\t}\n\t\tpixelscolor = (uint8_t*)pixelscolor + imageInfo.stride;\n\t}\n\tAndroidBitmap_unlockPixels(env, bitmap);\n\tLOGE(\"set RGB 565 Data finished\");\n}\n\nvoid setAlpha8Data(jobject bitmap, JNIEnv *env)\n{}\n\n\n//warp\nint AndroidBitmap_getInfo_warp(JNIEnv* env, jobject jbitmap)\n{\n\treturn AndroidBitmap_getInfo(env, jbitmap, &imageInfo);\n}\n\n\nvoid blur(){\n\tgaussBlurProcess(imageData, imageInfo.width, imageInfo.height);\n}\n\nvoid logBitmapPort(){\n\tLOGI(\"color image :: width is %d; height is %d; stride is %d; format is %d;flags is%d\",\n\t\t\t    imageInfo.width,imageInfo.height,imageInfo.stride,imageInfo.format,imageInfo.flags);\n}\n\nvoid clearColorPort(int color){\n\tclearColorProcess(imageData, imageInfo.width, imageInfo.height, color);\n}\n\nvoid boxBlurPort(int radius){\n\tboxBlurProcess(imageData, imageInfo.width, imageInfo.height, imageInfo.stride, radius);\n}\n\n\nvoid stackBlurPort(int radius){\n\tstackBlurProcess(imageData, imageInfo.width, imageInfo.height, imageInfo.stride, radius);\n}\n\n\nvoid oilPaintPort(int radius){\n\toilPaintProcess(imageData, imageInfo.width, imageInfo.height, imageInfo.stride, radius);\n}\n\n\nvoid colorWaterPaintPort(int radius){\n\tcolorWaterPaint(imageData, imageInfo.width, imageInfo.height, imageInfo.stride, radius);\n}\n"
  },
  {
    "path": "WayHoo/jni/bitmapPort.h",
    "content": "\n#include <string.h>\n#include <jni.h>\n#include <malloc.h>\n#include <android/bitmap.h>\n#include \"constants.h\"\n\n\n#ifndef _BITMAP_PORT_H_\n#define _BITMAP_PORT_H_\n\n\n//declare\nint allocateMemory();\n\nvoid getUnifiedImageData(jobject bitmap, JNIEnv *env);\n\nvoid getARGB8888Data(jobject bitmap, JNIEnv *env);\n\nvoid getARGB4444Data(jobject bitmap, JNIEnv *env);\n\nvoid getRGB565Data(jobject bitmap, JNIEnv *env);\n\nvoid getAlpha8Data(jobject bitmap, JNIEnv *env);\n\nvoid setUnifiedImageData(jobject bitmap, JNIEnv *env);\n\nvoid setARGB8888Data(jobject bitmap, JNIEnv *env);\n\nvoid setARGB4444Data(jobject bitmap, JNIEnv *env);\n\nvoid setRGB565Data(jobject bitmap, JNIEnv *env);\n\nvoid setAlpha8Data(jobject bitmap, JNIEnv *env);\n\n\nvoid blurPort();\nvoid logBitmapPort();\n\nvoid clearColorPort(int color);\nvoid boxBlurPort(int radius);\n\nvoid stackBlurPort(int radius);\n\n#endif\n"
  },
  {
    "path": "WayHoo/jni/com_way_util_blur_jni_FrostedGlassUtil.h",
    "content": "/* DO NOT EDIT THIS FILE - it is machine generated */\n#include <jni.h>\n/* Header for class com_way_util_blur_jni_FrostedGlassUtil */\n\n#ifndef _Included_com_way_util_blur_jni_FrostedGlassUtil\n#define _Included_com_way_util_blur_jni_FrostedGlassUtil\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n/*\n * Class:     com_way_util_blur_jni_FrostedGlassUtil\n * Method:    boxBlur\n * Signature: (Landroid/graphics/Bitmap;I)V\n */\nJNIEXPORT void JNICALL Java_com_way_util_blur_jni_FrostedGlassUtil_boxBlur\n  (JNIEnv *, jobject, jobject, jint);\n\n/*\n * Class:     com_way_util_blur_jni_FrostedGlassUtil\n * Method:    stackBlur\n * Signature: (Landroid/graphics/Bitmap;I)V\n */\nJNIEXPORT void JNICALL Java_com_way_util_blur_jni_FrostedGlassUtil_stackBlur\n  (JNIEnv *, jobject, jobject, jint);\n\n/*\n * Class:     com_way_util_blur_jni_FrostedGlassUtil\n * Method:    oilPaint\n * Signature: (Landroid/graphics/Bitmap;I)V\n */\nJNIEXPORT void JNICALL Java_com_way_util_blur_jni_FrostedGlassUtil_oilPaint\n  (JNIEnv *, jobject, jobject, jint);\n\n/*\n * Class:     com_way_util_blur_jni_FrostedGlassUtil\n * Method:    colorWaterPaint\n * Signature: (Landroid/graphics/Bitmap;I)V\n */\nJNIEXPORT void JNICALL Java_com_way_util_blur_jni_FrostedGlassUtil_colorWaterPaint\n  (JNIEnv *, jobject, jobject, jint);\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "WayHoo/jni/constants.h",
    "content": "#ifndef _CONSTANTS_H_\n#define _CONSTANTS_H_\n\n#include <jni.h>\n#include <android/log.h>\n#include <stdlib.h>\n#include \"com_way_util_blur_jni_FrostedGlassUtil.h\"\n\n#define RGB565_MASK_RED\t\t0xF800   \n#define RGB565_MASK_GREEN\t0x07E0   \n#define RGB565_MASK_BLUE\t0x001F  \n\n#define ARGB8888_MASK_ALPHA\t0xFF000000\n#define ARGB8888_MASK_RED\t0x00FF0000\n#define ARGB8888_MASK_GREEN\t0x0000FF00\n#define ARGB8888_MASK_BLUE\t0x000000FF\n\n#define ARGB4444_MASK_RED\t0x0F00;\n#define ARGB4444_MASK_GREEN\t0x00F0;\n#define ARGB4444_MASK_BLUE\t0x000F;\n\n\n#define ARGB8888_2_COLOR(a,r,g,b) (((a)<<24)|((r)<<16)|((g)<<8)|(b))\n\n#define LOG_TAG \"FrostedGlass\"\n\n#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)  \n#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)  \n\n\n#endif\n"
  },
  {
    "path": "WayHoo/jni/frostedGlass.c",
    "content": "/*\n * Copyright (C) 2009 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n#include <string.h>\n#include <jni.h>\n#include <malloc.h>\n#include <android/bitmap.h>\n#include \"constants.h\"\n#include \"bitmapPort.h\"\n//#include \"ImageProcessor.h\"\n//#include \"Lomo.h\"\n\n/* This is a trivial JNI example where we use a native method\n * to return a new VM String. See the corresponding Java source\n * file located at:\n *\n *   apps/samples/hello-jni/project/src/com/example/hellojni/HelloJni.java\n */\n\n\n\n\n\nJNIEXPORT void JNICALL Java_com_way_util_blur_jni_FrostedGlassUtil_boxBlur(JNIEnv *env, jclass obj, jobject srcBitmap, jint radius)\n{\n  \tvoid* pixelscolor;\n\tint ret, row, col;\n\tif ((ret = AndroidBitmap_getInfo_warp(env, srcBitmap)) < 0) {\n\t\tLOGE(\"AndroidBitmap_getInfo() failed ! error=%d\", ret);\n\t\treturn;\n\t}\n\n\tlogBitmapPort();\n\n\tif (-1 == allocateMemory()) {\n\t\tLOGE(\"allocate memory failed !\");\n\t\treturn;\n\t}\n\tgetUnifiedImageData(srcBitmap, env);\n\n\tLOGE(\"get unified image data \");\n\n\tboxBlurPort(radius);\n\n\tLOGE(\"conver image data\");\n\n\tsetUnifiedImageData(srcBitmap, env);\n\n\tLOGE(\"set unified image data\");\n\n\tfreeMemory();\n}\n\n\nJNIEXPORT void JNICALL Java_com_way_util_blur_jni_FrostedGlassUtil_stackBlur(JNIEnv *env, jclass obj, jobject srcBitmap, jint radius)\n{\n  \tvoid* pixelscolor;\n\tint ret, row, col;\n\tif ((ret = AndroidBitmap_getInfo_warp(env, srcBitmap)) < 0) {\n\t\tLOGE(\"AndroidBitmap_getInfo() failed ! error=%d\", ret);\n\t\treturn;\n\t}\n\n\tlogBitmapPort();\n\n\tif (-1 == allocateMemory()) {\n\t\tLOGE(\"allocate memory failed !\");\n\t\treturn;\n\t}\n\tgetUnifiedImageData(srcBitmap, env);\n\n\tLOGE(\"get unified image data \");\n\n\tstackBlurPort(radius);\n\n\tLOGE(\"conver image data\");\n\n\tsetUnifiedImageData(srcBitmap, env);\n\n\tLOGE(\"set unified image data\");\n\n\tfreeMemory();\n}\n\n\nJNIEXPORT void JNICALL Java_com_way_util_blur_jni_FrostedGlassUtil_oilPaint(JNIEnv *env, jclass obj, jobject srcBitmap, jint radius)\n{\n  \tvoid* pixelscolor;\n\tint ret, row, col;\n\tif ((ret = AndroidBitmap_getInfo_warp(env, srcBitmap)) < 0) {\n\t\tLOGE(\"AndroidBitmap_getInfo() failed ! error=%d\", ret);\n\t\treturn;\n\t}\n\n\tlogBitmapPort();\n\n\tif (-1 == allocateMemory()) {\n\t\tLOGE(\"allocate memory failed !\");\n\t\treturn;\n\t}\n\tgetUnifiedImageData(srcBitmap, env);\n\n\tLOGE(\"get unified image data \");\n\n\toilPaintPort(radius);\n\n\tLOGE(\"conver image data\");\n\n\tsetUnifiedImageData(srcBitmap, env);\n\n\tLOGE(\"set unified image data\");\n\n\tfreeMemory();\n}\n\nJNIEXPORT void JNICALL Java_com_way_util_blur_jni_FrostedGlassUtil_colorWaterPaint(JNIEnv *env, jclass obj, jobject srcBitmap, jint radius)\n{\n  \tvoid* pixelscolor;\n\tint ret, row, col;\n\tif ((ret = AndroidBitmap_getInfo_warp(env, srcBitmap)) < 0) {\n\t\tLOGE(\"AndroidBitmap_getInfo() failed ! error=%d\", ret);\n\t\treturn;\n\t}\n\n\tlogBitmapPort();\n\n\tif (-1 == allocateMemory()) {\n\t\tLOGE(\"allocate memory failed !\");\n\t\treturn;\n\t}\n\tgetUnifiedImageData(srcBitmap, env);\n\n\tLOGE(\"get unified image data \");\n\n\tcolorWaterPaintPort(radius);\n\n\tLOGE(\"conver image data\");\n\n\tsetUnifiedImageData(srcBitmap, env);\n\n\tLOGE(\"set unified image data\");\n\n\tfreeMemory();\n}\n"
  },
  {
    "path": "WayHoo/jni/imageProcess.c",
    "content": "#include \"stdlib.h\"\r\n#include \"stdio.h\"\r\n#include \"math.h\"\r\n#include \"imageProcess.h\"\r\n#include \"constants.h\"\r\n\r\n\r\nstatic void  iblurV(int *pDataSave, int * pData, int *divTable, int width, int height, int radius);\r\nstatic void  iblurH(int *pDataSave, int * pData, int *divTable, int width, int height, int radius);\r\nstatic void iStackBlur(int *pData, int width, int height, int radius);\r\nstatic void iQSort(uint8_t a[],int low,int high);\r\nstatic void iColorWaterFilter( int * pSrcImage, int * pDstImage, int width, int height  );\r\n\r\n\r\nvoid gaussBlurProcess(uint8_t *image, int width, int height)\r\n{\r\n\r\n\t// 高斯矩阵\r\n\tint gauss[16] = { 1, 2, 1, 2, 4, 2, 1, 2, 1 };\r\n\r\n\tint pixR = 0;\r\n\tint pixG = 0;\r\n\tint pixB = 0;\r\n\r\n\tint pixColor = 0;\r\n\r\n\tint newR = 0;\r\n\tint newG = 0;\r\n\tint newB = 0;\r\n\r\n\tint idx = 0;\r\n\tint * pixels = 0;\r\n\r\n\tint * pData = (int *)image;\r\n\r\n\tint value = 0;\r\n\r\n\tpixels = (int *)malloc(width * height*sizeof(int));\r\n\r\n\tif(pixels == NULL){\r\n\t\treturn;\r\n\t}\r\n\r\n\tmemset((unsigned char *)pixels, 0, sizeof(int)*width * height);\r\n\r\n\r\n\tint i = 0;\r\n\tint j = 0;\r\n\r\n\tint v = 0;\r\n\tint h = height - 1;\r\n\r\n\tint m;\r\n\tint n;\r\n\r\n\tfor (j = 1; j < h; j++){\r\n\r\n\t\tv = width - 1;\r\n\t\tfor (i = 1; i < v; i++){\r\n\t\t\tidx = 0;\r\n\r\n\t\t\t//initial\r\n\t\t\tnewR = 0;\r\n\t\t\tnewG = 0;\r\n\t\t\tnewB = 0;\r\n\r\n\t\t\t//add\r\n\t\t\tfor (m = -1; m <= 1; m++) {\r\n\t\t\t\tfor (n = -1; n <= 1; n++) {\r\n\t\t\t\t\tpixColor = pData[(j + m) * width + i + n];\r\n\r\n\t\t\t\t\tpixR = (pixColor>>16)&0xFF;\r\n\t\t\t\t\tpixG = (pixColor>>8)&0xFF;\r\n\t\t\t\t\tpixB = (pixColor)&0xFF;\r\n\r\n\t\t\t\t\tvalue = gauss[idx];\r\n\r\n\t\t\t\t\tnewR = newR + pixR * value;\r\n\t\t\t\t\tnewG = newG + pixG * value;\r\n\t\t\t\t\tnewB = newB + pixB * value;\r\n\t\t\t\t\tidx++;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t//divide\r\n\t\t\tnewR /= 16;\r\n\t\t\tnewG /= 16;\r\n\t\t\tnewB /= 16;\r\n\r\n\t\t\t//newR = Math.min(255, Math.max(0, newR));\r\n\t\t\t//newG = Math.min(255, Math.max(0, newG));\r\n\t\t\t//newB = Math.min(255, Math.max(0, newB));\r\n\r\n\t\t\t//setColor\r\n\t\t\tpixels[j * width + i] = 0xFF000000|(newR<<16)|(newG<<8)|(newB);//Color.argb(255, newR, newG, newB);\r\n\t\t}\r\n\t}\r\n\r\n\t//copy\r\n\tmemcpy(image, pixels, width*height);\r\n\r\n}\r\n\r\n\r\n\r\nvoid clearColorProcess(uint8_t *image, int width, int height, int color)\r\n{\r\n\tmemset(image, 0xFF, width*height*4);\r\n\r\n\t//image[0] = 0;\r\n\r\n\tint * pData = (int *)image;\r\n\r\n\tint i = 0;\r\n\tint j = 0;\r\n\r\n\tfor (j = 0; j < height; j++){\r\n\t\tfor (i = 0; i < width; i++){\r\n\t\t\tpData[j * width + i] = color;//Color.argb(255, newR, newG, newB);\r\n\t\t}\r\n\t}\r\n}\r\n\r\n\r\n\r\nvoid  blurH(uint8_t * image, int width, int height, int radius)\r\n{\r\n    int i = 0;\r\n    int j = 0;\r\n\r\n    uint32_t color;\r\n\r\n    int32_t addR;\r\n    int32_t addG;\r\n    int32_t addB;\r\n    int32_t addA;\r\n\r\n\r\n    //copy data\r\n    uint8_t * pDataSave = NULL;\r\n\r\n    int * pData = (int *)image;\r\n\r\n    int widthMinus1 = 0;\r\n\r\n    int * divTable = NULL;\r\n\r\n    //save length\r\n    j = 2*radius + 1;\r\n\r\n    divTable = (int *)malloc(256*j*sizeof(int));\r\n    if(divTable == NULL){\r\n    \treturn;\r\n    }\r\n\r\n    //clear data\r\n    memset(divTable, 0, 256*j*sizeof(int));\r\n\r\n\r\n    pDataSave = (uint8_t *)malloc(width*height*sizeof(int));\r\n    if(pDataSave == NULL){\r\n    \treturn;\r\n    }\r\n\r\n    //clear data\r\n    memset(pDataSave, 0, width*height*sizeof(int));\r\n\r\n\r\n    j = 256*(2*radius + 1);\r\n\r\n    //change div\r\n    int inIndex = 0;\r\n\r\n    widthMinus1 = (2*radius + 1);\r\n    if(j < (1<<15)){\r\n\t\t//GFIXED\r\n\t\tinIndex = (1<<15)/widthMinus1;\r\n\r\n\t\t//计算灰度均值表\r\n\t\tfor ( i = 0; i < j; i++ ){\r\n\t\t\t//divide[i] = i/tableSize;\r\n\t\t\tdivTable[i] = (i*inIndex)>>15;\r\n\t\t}\r\n    }else{\r\n\t\tfor ( i = 0; i < j; i++ ){\r\n\t\t\tdivTable[i] = i/widthMinus1;\r\n\t\t}\r\n    }\r\n\r\n\r\n    //set data\r\n    widthMinus1 = width - 1;\r\n\r\n    for (j = 0; j < height; j++ )\r\n    {\r\n        int outIndex = j;\r\n        int Plx = 0; //记录像素的灰度累加值\r\n        addR = 0;\r\n        addG = 0;\r\n        addB = 0;\r\n        addA = 0;\r\n\r\n        for ( i = - radius; i <= radius; i++ )\r\n        {\r\n            i = (i < 0)?1:0;\r\n            i = (i > width - 1)?i:(width - 1);\r\n\r\n            color = pData[inIndex+i];\r\n\r\n           // Plx += pData[inIndex+i];\r\n\r\n            addA += ((color&ARGB8888_MASK_ALPHA)>>24);\r\n            addR +=  (color&ARGB8888_MASK_RED)>>16;\r\n            addG +=  (color&ARGB8888_MASK_GREEN)>>8;\r\n            addB +=  (color&ARGB8888_MASK_BLUE);\r\n        }\r\n\r\n        for ( i = 0; i < width; i++ )\r\n        {\r\n            int i1 = i + radius + 1;\r\n            if ( i1 > widthMinus1 ){\r\n                i1 = widthMinus1;\r\n            }\r\n            int i2 = i - radius;\r\n            if ( i2 < 0 ){\r\n                i2 = 0;\r\n            }\r\n            int cTail = pData[inIndex+i1];\r\n            int cHead = pData[inIndex+i2];\r\n\r\n            //Plx += (r1 - r2);\r\n            addA += ((cTail&ARGB8888_MASK_ALPHA)>>24) - ((cHead&ARGB8888_MASK_ALPHA)>>24);\r\n            addR += ((cTail&ARGB8888_MASK_RED)>>16)   - ((cHead&ARGB8888_MASK_RED)>>16);\r\n            addG += ((cTail&ARGB8888_MASK_GREEN)>>8)   - ((cHead&ARGB8888_MASK_GREEN)>>8);\r\n            addB +=  (cTail&ARGB8888_MASK_BLUE)       -  (cHead&ARGB8888_MASK_BLUE);\r\n\r\n            //colorAdd/(radius*1+1)\r\n            color = ARGB8888_2_COLOR((divTable[addA]), divTable[addR], divTable[addG], divTable[addB]);\r\n\r\n            pDataSave[inIndex + i] = color;\r\n        }\r\n\r\n        //next line\r\n        inIndex += width;\r\n    }\r\n\r\n\r\n    memcpy(pData, pDataSave, width*height*sizeof(int));\r\n\r\n    if(pDataSave != NULL){\r\n    \tfree(pDataSave);\r\n    \tpDataSave = NULL;\r\n    }\r\n\r\n    if(divTable != NULL){\r\n    \tfree(divTable);\r\n    \tdivTable = NULL;\r\n    }\r\n\r\n}\r\n\r\n\r\nvoid boxBlurProcess(uint8_t *image, int width, int height, int stride, int radius){\r\n\tint * divTable = NULL;\r\n\r\n\tint * pDataSave;\r\n\r\n\tint * pData = (int *)image;\r\n\r\n\tint len;\r\n\r\n\r\n\tint widthMinus1 = (2*radius + 1);\r\n\r\n\t//save length\r\n\tlen = 2*radius + 1;\r\n\r\n\t//LOGI(\"00\");\r\n\r\n\r\n\r\n\tdivTable = (int *)malloc(256*len*sizeof(int));\r\n\tif(divTable == NULL){\r\n\t\treturn;\r\n\t}\r\n\r\n\t//clear data\r\n\tmemset(divTable, 0, 256*len*sizeof(int));\r\n\r\n\r\n\tlen = 256*(2*radius + 1);\r\n\r\n    //change div\r\n    int inIndex = 0;\r\n\r\n    int i;\r\n\r\n    //LOGI(\"0\");\r\n\r\n\r\n    /*\r\n    if(len < (1<<15)){\r\n\t\t//GFIXED\r\n\t\tinIndex = (1<<15)/widthMinus1;\r\n\r\n\t\t//计算灰度均值表\r\n\t\tfor ( i = 0; i < len; i++ ){\r\n\t\t\t//divide[i] = i/tableSize;\r\n\t\t\tdivTable[i] = (i*inIndex)>>15;\r\n\t\t}\r\n    }else\r\n    */\r\n    {\r\n\t\tfor ( i = 0; i < len; i++ ){\r\n\t\t\tdivTable[i] = i/widthMinus1;\r\n\t\t}\r\n    }\r\n\r\n    //LOGI(\"2\");\r\n\r\n    pDataSave = (int *)malloc(width*height*sizeof(int));\r\n    if(pDataSave == NULL){\r\n    \treturn;\r\n    }\r\n\r\n\r\n    iblurH(pDataSave, pData, divTable, width, height, radius);\r\n\r\n    //LOGI(\"3\");\r\n\r\n    iblurV(pData, pDataSave, divTable, width, height, radius);\r\n\r\n    //memcpy(pData, pDataSave, width*height*sizeof(int));\r\n\r\n    //memset(pData, 0xFF, width*height*sizeof(int));\r\n\r\n\r\n    //clearColorProcess((uint8_t *)image, width, height, 0xFFFFFF00);\r\n\r\n    //LOGI(\"4\");\r\n\r\n    if(divTable != NULL){\r\n    \tfree(divTable);\r\n    \tdivTable = NULL;\r\n    }\r\n\r\n\r\n    if(pDataSave != NULL){\r\n    \tfree(pDataSave);\r\n    \tpDataSave = NULL;\r\n    }\r\n}\r\n\r\nstatic void iblurH(int *pDataSave, int * pData, int *divTable, int width, int height, int radius)\r\n{\r\n    int i = 0;\r\n    int j = 0;\r\n\r\n    int color;\r\n\r\n    int addR;\r\n    int addG;\r\n    int addB;\r\n    int addA;\r\n\r\n    int R;\r\n    int G;\r\n    int B;\r\n    int A;\r\n\r\n    int widthMinus1 = 0;\r\n\r\n    int lineHead;\r\n\r\n    //set data\r\n    widthMinus1 = width - 1;\r\n\r\n    lineHead = 0;\r\n\r\n    int x;\r\n\r\n    for (j = 0; j < height; j++ )\r\n    {\r\n    \t//set zero\r\n    \taddA = 0;\r\n        addR = 0;\r\n        addG = 0;\r\n        addB = 0;\r\n\r\n        //add color\r\n        for ( i = - (radius + 1); i < radius; i++ )\r\n        {\r\n            x = (i < 0)?0:i;\r\n            x = (x > (width - 1))?(width - 1):x;\r\n\r\n            color = pData[lineHead + x];\r\n\r\n           // Plx += pData[inIndex+i];\r\n\r\n            A = ((color&ARGB8888_MASK_ALPHA)>>24)&0xFF;\r\n            R = (color&ARGB8888_MASK_RED)>>16&0xFF;\r\n            G = (color&ARGB8888_MASK_GREEN)>>8&0xFF;\r\n            B = (color&ARGB8888_MASK_BLUE)&0xFF;\r\n\r\n            addA += A;\r\n            addR += R;\r\n            addG += G;\r\n            addB += B;\r\n        }\r\n\r\n\r\n        for ( i = 0; i < width; i++ )\r\n        {\r\n            x = i + radius;\r\n            if ( x > widthMinus1 ){\r\n                x = widthMinus1;\r\n            }\r\n\r\n            color = pData[lineHead + x];\r\n\r\n            A = ((color&ARGB8888_MASK_ALPHA)>>24)&0xFF;\r\n            R =  (color&ARGB8888_MASK_RED)>>16&0xFF;\r\n            G =  (color&ARGB8888_MASK_GREEN)>>8&0xFF;\r\n            B =  (color&ARGB8888_MASK_BLUE)&0xFF;\r\n\r\n            addA += A;\r\n\t\t\taddR += R;\r\n\t\t\taddG += G;\r\n\t\t\taddB += B;\r\n\r\n\r\n            x = i - (radius + 1);\r\n            if ( x < 0 ){\r\n                x = 0;\r\n            }\r\n\r\n\t\t\tcolor = pData[lineHead + x];\r\n\r\n            A = ((color&ARGB8888_MASK_ALPHA)>>24)&0xFF;\r\n            R =  (color&ARGB8888_MASK_RED)>>16&0xFF;\r\n            G =  (color&ARGB8888_MASK_GREEN)>>8&0xFF;\r\n            B =  (color&ARGB8888_MASK_BLUE)&0xFF;\r\n\r\n\r\n            addA -= A;\r\n\t\t\taddR -= R;\r\n\t\t\taddG -= G;\r\n\t\t\taddB -= B;\r\n\r\n\t\t\t//div\r\n\r\n\t\t\tA = divTable[addA];//addA/(radius*2+1);\r\n\t\t\tR = divTable[addR];//addR/(radius*2+1);\r\n\t\t\tG = divTable[addG];//addG/(radius*2+1);\r\n\t\t\tB = divTable[addB];//addB/(radius*2+1);\r\n\r\n            color = ARGB8888_2_COLOR(A,R,G,B);//(((A))<<24) | (((R))<<16) | (((G))<<8)|(B);\r\n\r\n            pDataSave[lineHead + i] = color;\r\n        }\r\n\r\n        //next line\r\n        lineHead += width;\r\n    }\r\n}\r\n\r\n\r\nstatic void iblurV(int *pDataSave, int * pData, int *divTable, int width, int height, int radius)\r\n{\r\n\tint i = 0;\r\n\tint j = 0;\r\n\r\n\tint color;\r\n\r\n\tint addR;\r\n\tint addG;\r\n\tint addB;\r\n\tint addA;\r\n\r\n\tint R;\r\n\tint G;\r\n\tint B;\r\n\tint A;\r\n\r\n\tint heightMinus1 = 0;\r\n\r\n\tint lineHead;\r\n\r\n\t//set data\r\n\theightMinus1 = height - 1;\r\n\r\n\tlineHead = 0;\r\n\r\n\r\n\tint y;\r\n\r\n\tchar name[100];\r\n\r\n\tfor (i = 0; i < width; i++ )\r\n\t{\r\n\t\t//set zero\r\n\t\taddA = 0;\r\n\t\taddR = 0;\r\n\t\taddG = 0;\r\n\t\taddB = 0;\r\n\r\n\t\t//add color\r\n\t\tfor ( j = - (radius + 1); j < radius; j++ )\r\n\t\t{\r\n\t\t\ty = (j < 0)?0:j;\r\n\t\t\ty = (y > (height - 1))?(height - 1):y;\r\n\r\n\t\t\tcolor = pData[lineHead + y*width];\r\n\r\n\t\t   // Plx += pData[inIndex+i];\r\n\r\n\t\t\tA = ((color&ARGB8888_MASK_ALPHA)>>24)&0xFF;\r\n\t\t\tR = (color&ARGB8888_MASK_RED)>>16&0xFF;\r\n\t\t\tG = (color&ARGB8888_MASK_GREEN)>>8&0xFF;\r\n\t\t\tB = (color&ARGB8888_MASK_BLUE)&0xFF;\r\n\r\n\t\t\taddA += A;\r\n\t\t\taddR += R;\r\n\t\t\taddG += G;\r\n\t\t\taddB += B;\r\n\t\t}\r\n\r\n\r\n\t\tfor ( j = 0; j < height; j++ )\r\n\t\t{\r\n\t\t\ty = j + radius;\r\n\t\t\tif ( y > heightMinus1 ){\r\n\t\t\t\ty = heightMinus1;\r\n\t\t\t}\r\n\r\n\t\t\tcolor = pData[lineHead + y*width];\r\n\r\n\t\t\tA = ((color&ARGB8888_MASK_ALPHA)>>24)&0xFF;\r\n\t\t\tR =  (color&ARGB8888_MASK_RED)>>16&0xFF;\r\n\t\t\tG =  (color&ARGB8888_MASK_GREEN)>>8&0xFF;\r\n\t\t\tB =  (color&ARGB8888_MASK_BLUE)&0xFF;\r\n\r\n\t\t\taddA += A;\r\n\t\t\taddR += R;\r\n\t\t\taddG += G;\r\n\t\t\taddB += B;\r\n\r\n\r\n\t\t\ty = j - (radius + 1);\r\n\t\t\tif ( y < 0 ){\r\n\t\t\t\ty = 0;\r\n\t\t\t}\r\n\r\n\t\t\tcolor = pData[lineHead + y*width];\r\n\r\n\t\t\tsprintf(name,\"offset=%d,w=%d,h=%d,total=%d\",lineHead + y*width,width, height,width*height);\r\n\r\n\t\t\tif(lineHead + y*width<0||lineHead + y*width>=height*width){\r\n\t\t\t\t//LOGI(name);\r\n\t\t\t}\r\n\r\n\t\t\tA = ((color&ARGB8888_MASK_ALPHA)>>24)&0xFF;\r\n\t\t\tR =  (color&ARGB8888_MASK_RED)>>16&0xFF;\r\n\t\t\tG =  (color&ARGB8888_MASK_GREEN)>>8&0xFF;\r\n\t\t\tB =  (color&ARGB8888_MASK_BLUE)&0xFF;\r\n\r\n\r\n\t\t\taddA -= A;\r\n\t\t\taddR -= R;\r\n\t\t\taddG -= G;\r\n\t\t\taddB -= B;\r\n\r\n\t\t\t//div\r\n\r\n\t\t\tA = divTable[addA];//addA/(radius*2+1);\r\n\t\t\tR = divTable[addR];//addR/(radius*2+1);\r\n\t\t\tG = divTable[addG];//addG/(radius*2+1);\r\n\t\t\tB = divTable[addB];//addB/(radius*2+1);\r\n\r\n\t\t\tcolor = ARGB8888_2_COLOR(A,R,G,B);//(((A))<<24) | (((R))<<16) | (((G))<<8)|(B);\r\n\r\n\t\t\tpDataSave[lineHead + j*width] = color;\r\n\t\t}\r\n\r\n\t\t//next line\r\n\t\tlineHead ++;\r\n\t}\r\n}\r\n\r\n\r\n//////////////////////////////////////////////////////////////////////////////////////////////\r\n\r\nvoid stackBlurProcess(uint8_t *image, int width, int height, int stride, int radius){\r\n\tint * pData = (int *)image;\r\n\r\n\tint len;\r\n    //LOGI(\"2\");\r\n    //LOGI(\"3\");\r\n\r\n    iStackBlur(pData, width, height, radius);\r\n\r\n\r\n    //LOGI(\"4\");\r\n}\r\n\r\nint Math_max(int x, int y){\r\n\treturn (x>y)?x:y;\r\n}\r\n\r\nint Math_min(int x, int y){\r\n\treturn (x<y)?x:y;\r\n}\r\n\r\nint Math_abs(int x){\r\n\treturn (x>0)?x:(-x);\r\n}\r\n\r\nstatic void iStackBlur(int *pData, int width, int height, int radius){\r\n\r\n    if (radius < 1) {\r\n        return;\r\n    }\r\n\r\n    int w = width;\r\n    int h = height;\r\n\r\n    int* pix = pData;\r\n\r\n    //Log.e(\"pix\", w + \" \" + h + \" \" + pix.length);\r\n\r\n    //bitmap.getPixels(pix, 0, w, 0, 0, w, h);\r\n\r\n    int wm = w - 1;\r\n    int hm = h - 1;\r\n    int wh = w * h;\r\n    int div = radius + radius + 1;\r\n\r\n\r\n    int *r = (int*)malloc(wh*sizeof(int));\r\n    int *g = (int*)malloc(wh*sizeof(int));\r\n    int *b = (int*)malloc(wh*sizeof(int));\r\n\r\n\r\n    int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;\r\n    int * vmin = (int*)malloc(Math_max(w, h)*sizeof(int));\r\n\r\n    int divsum = (div + 1) >> 1;\r\n    divsum *= divsum;\r\n    int *dv = (int*)malloc(256 * divsum*sizeof(int));\r\n    for (i = 0; i < 256 * divsum; i++) {\r\n        dv[i] = (i / divsum);\r\n    }\r\n\r\n    yw = yi = 0;\r\n\r\n    //int[][] stack = new int[div][3];\r\n    int **stack = (int **)malloc(div*sizeof(int*));\r\n    for (x = 0; x < div; ++x){\r\n    \tstack[x] = (int *)malloc(3*sizeof(int));\r\n    }\r\n\r\n\r\n    int stackpointer;\r\n    int stackstart;\r\n    int* sir;\r\n\r\n    int rbs;\r\n    int r1 = radius + 1;\r\n    int routsum, goutsum, boutsum;\r\n    int rinsum, ginsum, binsum;\r\n\r\n    //char name[128];\r\n    //sprintf(name,\"start%d\", radius);\r\n\r\n    //LOGI(name);\r\n\r\n    for (y = 0; y < h; y++) {\r\n        rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;\r\n\r\n        for (i = -radius; i <= radius; i++) {\r\n            p = pix[yi + Math_min(wm, Math_max(i, 0))];\r\n            sir = stack[i + radius];\r\n\r\n\r\n            sir[0] = (p & 0xff0000) >> 16;\r\n            sir[1] = (p & 0x00ff00) >> 8;\r\n            sir[2] = (p & 0x0000ff);\r\n\r\n\r\n            rbs = r1 - Math_abs(i);\r\n            rsum += sir[0] * rbs;\r\n            gsum += sir[1] * rbs;\r\n            bsum += sir[2] * rbs;\r\n            if (i > 0) {\r\n                rinsum += sir[0];\r\n                ginsum += sir[1];\r\n                binsum += sir[2];\r\n            } else {\r\n                routsum += sir[0];\r\n                goutsum += sir[1];\r\n                boutsum += sir[2];\r\n            }\r\n\r\n        }\r\n\r\n\r\n        stackpointer = radius;\r\n\r\n\r\n        for (x = 0; x < w; x++) {\r\n\r\n            r[yi] = dv[rsum];\r\n            g[yi] = dv[gsum];\r\n            b[yi] = dv[bsum];\r\n\r\n            rsum -= routsum;\r\n            gsum -= goutsum;\r\n            bsum -= boutsum;\r\n\r\n            stackstart = stackpointer - radius + div;\r\n            sir = stack[stackstart % div];\r\n\r\n            routsum -= sir[0];\r\n            goutsum -= sir[1];\r\n            boutsum -= sir[2];\r\n\r\n            if (y == 0) {\r\n                vmin[x] = Math_min(x + radius + 1, wm);\r\n            }\r\n            p = pix[yw + vmin[x]];\r\n\r\n            sir[0] = (p & 0xff0000) >> 16;\r\n            sir[1] = (p & 0x00ff00) >> 8;\r\n            sir[2] = (p & 0x0000ff);\r\n\r\n            rinsum += sir[0];\r\n            ginsum += sir[1];\r\n            binsum += sir[2];\r\n\r\n            rsum += rinsum;\r\n            gsum += ginsum;\r\n            bsum += binsum;\r\n\r\n            stackpointer = (stackpointer + 1) % div;\r\n            sir = stack[(stackpointer) % div];\r\n\r\n            routsum += sir[0];\r\n            goutsum += sir[1];\r\n            boutsum += sir[2];\r\n\r\n            rinsum -= sir[0];\r\n            ginsum -= sir[1];\r\n            binsum -= sir[2];\r\n\r\n            yi++;\r\n        }\r\n\r\n        yw += w;\r\n    }\r\n\r\n    //LOGI(\"Next\");\r\n\r\n\r\n\r\n    for (x = 0; x < w; x++) {\r\n        rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;\r\n        yp = -radius * w;\r\n        for (i = -radius; i <= radius; i++) {\r\n            yi = Math_max(0, yp) + x;\r\n\r\n            sir = stack[i + radius];\r\n\r\n            sir[0] = r[yi];\r\n            sir[1] = g[yi];\r\n            sir[2] = b[yi];\r\n\r\n            rbs = r1 - Math_abs(i);\r\n\r\n            rsum += r[yi] * rbs;\r\n            gsum += g[yi] * rbs;\r\n            bsum += b[yi] * rbs;\r\n\r\n            if (i > 0) {\r\n                rinsum += sir[0];\r\n                ginsum += sir[1];\r\n                binsum += sir[2];\r\n            } else {\r\n                routsum += sir[0];\r\n                goutsum += sir[1];\r\n                boutsum += sir[2];\r\n            }\r\n\r\n            if (i < hm) {\r\n                yp += w;\r\n            }\r\n        }\r\n        yi = x;\r\n        stackpointer = radius;\r\n        for (y = 0; y < h; y++) {\r\n            // Preserve alpha channel: ( 0xff000000 & pix[yi] )\r\n        \t//force to set alpha 255 modified by chao.lc\r\n            pix[yi] = ( 0xff000000 /*& pix[yi]*/ ) | ( dv[rsum] << 16 ) | ( dv[gsum] << 8 ) | dv[bsum];\r\n\r\n            rsum -= routsum;\r\n            gsum -= goutsum;\r\n            bsum -= boutsum;\r\n\r\n            stackstart = stackpointer - radius + div;\r\n            sir = stack[stackstart % div];\r\n\r\n            routsum -= sir[0];\r\n            goutsum -= sir[1];\r\n            boutsum -= sir[2];\r\n\r\n            if (x == 0) {\r\n                vmin[y] = Math_min(y + r1, hm) * w;\r\n            }\r\n            p = x + vmin[y];\r\n\r\n            sir[0] = r[p];\r\n            sir[1] = g[p];\r\n            sir[2] = b[p];\r\n\r\n            rinsum += sir[0];\r\n            ginsum += sir[1];\r\n            binsum += sir[2];\r\n\r\n            rsum += rinsum;\r\n            gsum += ginsum;\r\n            bsum += binsum;\r\n\r\n            stackpointer = (stackpointer + 1) % div;\r\n            sir = stack[stackpointer];\r\n\r\n            routsum += sir[0];\r\n            goutsum += sir[1];\r\n            boutsum += sir[2];\r\n\r\n            rinsum -= sir[0];\r\n            ginsum -= sir[1];\r\n            binsum -= sir[2];\r\n\r\n            yi += w;\r\n        }\r\n    }\r\n\r\n\r\n\r\n    if(vmin != NULL){\r\n    \tfree(vmin);\r\n    \tvmin = NULL;\r\n    }\r\n\r\n\r\n    if(dv != NULL){\r\n    \tfree(dv);\r\n    \tdv = NULL;\r\n    }\r\n\r\n    for (x = 0; x < div; ++x){\r\n        if(stack[x] != NULL){\r\n        \tfree(stack[x]);\r\n        \tstack[x] = NULL;\r\n        }\r\n    }\r\n    if(stack != NULL){\r\n    \tfree( stack );\r\n    }\r\n\r\n    if(r!=NULL){\r\n    \tfree(r);\r\n    \tr = NULL;\r\n    }\r\n\r\n    if(g != NULL){\r\n    \tfree(g);\r\n    \tg = NULL;\r\n    }\r\n\r\n    if(b != NULL){\r\n    \tfree(b);\r\n    \tb = NULL;\r\n    }\r\n}\r\n\r\n\r\n\r\nstatic int iOilPaint( int * pDstData, int * pSrcData, int width, int height, int radius)\r\n{\r\n\tint dx;\r\n\tint dy;\r\n\r\n\tint i = 0;\r\n\tint j = 0;\r\n\r\n\tint r = 0;\r\n\tint g = 0;\r\n\tint b = 0;\r\n\r\n\tint rTemp = 0;\r\n\tint gTemp = 0;\r\n\tint bTemp = 0;\r\n\r\n\tint rIndex = 0;\r\n\tint gIndex = 0;\r\n\tint bIndex = 0;\r\n\r\n\tint histo[256+256+256];\r\n\tint * histoR = histo;\r\n\tint * histoG = histo + 256;\r\n\tint * histoB = histo + 256 + 256;\r\n\r\n\tint color;\r\n\tint N = 2;\r\n\tint offset = width - (2*N+1);\r\n\r\n\tint * pSrcTempData;\r\n\r\n\tif( 2*N+1 > width || 2*N + 1 > height )\r\n\t{\r\n\t\treturn -1;\r\n\t}\r\n\r\n\t//copy pix\r\n\tmemcpy( pDstData, pSrcData, sizeof(int)*width*height);\r\n\r\n\tint *pDstTempData = pDstData + N*width;\r\n\tfor( j = N; j < height - N; j++ )\r\n\t{\r\n\t\tfor( i = 0; i < width; i++ )\r\n\t\t{\r\n\t\t\tpSrcTempData = pSrcData;\r\n\r\n\t\t\tmemset( histo, 0, sizeof( int )*768 );//\r\n\r\n\t\t\trTemp = 0;\r\n\t\t\tgTemp = 0;\r\n\t\t\tbTemp = 0;\r\n\r\n\t\t\tfor( dy = - N; dy <= N; dy++ )\r\n\t\t\t{\r\n\t\t\t\tfor( dx = - N; dx <= N; dx++)\r\n\t\t\t\t{\r\n\t\t\t\t\t//color = *(pSrcImage->p + dx + dy*pSrcImage->w);\r\n\t\t\t\t\tcolor = *pSrcTempData;\r\n\r\n\t\t\t\t\t//r = (color&0xF800)>>11;\r\n\t\t\t\t\t//g = (color&0x7E0)>>5;\r\n\t\t\t\t\t//b = (color&0x1F);\r\n\r\n\t\t\t\t\t//a = ((color&ARGB8888_MASK_ALPHA)>>24)&0xFF;\r\n\t\t\t\t\tr =  (color&ARGB8888_MASK_RED)>>16&0xFF;\r\n\t\t\t\t\tg =  (color&ARGB8888_MASK_GREEN)>>8&0xFF;\r\n\t\t\t\t\tb =  (color&ARGB8888_MASK_BLUE)&0xFF;\r\n\r\n\r\n\t\t\t\t\thistoR[r]++;\r\n\t\t\t\t\thistoG[g]++;\r\n\t\t\t\t\thistoB[b]++;\r\n\t\t\t\t\tif ( histoR[r] > rTemp )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\trTemp = histoR[r];\r\n\t\t\t\t\t\trIndex = r;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif ( histoG[g] > gTemp )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tgTemp = histoG[g];\r\n\t\t\t\t\t\tgIndex = g;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tif ( histoB[b] > bTemp )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tbTemp = histoB[b];\r\n\t\t\t\t\t\tbIndex = b;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tpSrcTempData++;\r\n\t\t\t\t}\r\n\t\t\t\tpSrcTempData += offset;\r\n\t\t\t}\r\n\r\n\t\t\t*(pDstTempData++) = ARGB8888_2_COLOR(255,rIndex,gIndex,bIndex);//(rIndex<<11) | (gIndex<<5) | (bIndex);\r\n\t\t\tpSrcData++;\r\n\t\t}\r\n\t}\r\n\r\n\treturn 0;\r\n}\r\n\r\n\r\n\r\n\r\nvoid oilPaintProcess(uint8_t *image, int width, int height, int stride, int radius){\r\n\r\n    //LOGI(\"2\");\r\n\r\n    int *pDataSave = (int *)malloc(width*height*sizeof(int));\r\n    if(pDataSave == NULL){\r\n    \treturn;\r\n    }\r\n\r\n\r\n    iOilPaint(pDataSave, (int *)image, width, height, radius);\r\n\r\n\r\n    memcpy( image, pDataSave, sizeof(int)*width*height);\r\n\r\n\r\n    //LOGI(\"4\");\r\n\r\n    if(pDataSave != NULL){\r\n    \tfree(pDataSave);\r\n    \tpDataSave = NULL;\r\n    }\r\n}\r\n\r\n\r\n////////////////////\r\n\r\nstatic int iPartions(uint8_t a[],int low,int high)\r\n{\r\n\tint pivotkey=a[low];\r\n\ta[0]=a[low];\r\n\twhile(low<high)\r\n\t{\r\n\t\twhile(low<high && a[high]>=pivotkey)\r\n\t\t\t--high;\r\n\t\ta[low]=a[high];\r\n\t\twhile(low<high && a[low]<=pivotkey)\r\n\t\t\t++low;\r\n\t\ta[high]=a[low];\r\n\t}\r\n\ta[low]=a[0];\r\n\treturn low;\r\n}\r\n\r\nstatic void iQSort(uint8_t a[],int low,int high)\r\n{\r\n\tint pivottag;\r\n\tif(low<high)\r\n\t{ //µÝ¹éµ÷ÓÃ\r\n\t\tpivottag=iPartions(a,low,high);\r\n\t\tiQSort(a,low,pivottag-1);\r\n\t\tiQSort(a,pivottag+1,high);\r\n\t}\r\n}\r\n\r\nstatic void iQuickSort(uint8_t a[],int n)\r\n{\r\n\tiQSort(a,1,n);\r\n}\r\n\r\n//ÖÐÖµÂË²š//numÎªÆæÊý\r\nstatic void imMedian( int * pDstImage, int * pSrcImage, int width, int height, int num )\r\n{\r\n\tint i = 0;\r\n\tint j = 0;\r\n\r\n\tint k = 0;\r\n\tint l = 0;\r\n\r\n\tuint8_t r[256];\r\n\tuint8_t g[256];\r\n\tuint8_t b[256];\r\n\r\n\tuint8_t SortArray[32];\r\n\r\n\tuint8_t rColor;\r\n\tuint8_t gColor;\r\n\tuint8_t bColor;\r\n\r\n\tint color;\r\n\r\n\tint * pSrcData = pSrcImage;\r\n\tint * pDstData = pDstImage;\r\n\r\n\tint * pSrcDataTemp = pSrcData;\r\n\r\n\tint m = 0;\r\n\tint num2 = num*num;\r\n\tint num22 = num*num/2;\r\n\tint offset = (width - num);\r\n\r\n\t//copy pix\r\n\t//memcpy( pDstData, pSrcData, sizeof(int)*width*height );\r\n\r\n\tpDstData = pDstImage + (num/2 - 1)*width;\r\n\r\n\t//char name[128];\r\n\r\n\t//sprintf(name,\"n=%d,%d,%d\",num,width,height);\r\n\r\n\t//LOGI(name);\r\n\t//LOGI(\"mike\");\r\n\r\n\tfor ( j = num/2; j < height - num/2; j++ )\r\n\t{\r\n\t\tfor ( i = 0; i < width; i++ )\r\n\t\t{\r\n\t\t\t//\r\n\t\t\tpSrcDataTemp = pSrcData;\r\n\r\n\t\t\tm = 0;\r\n\t\t\tfor ( k = 0 ; k < num; k++ )\r\n\t\t\t{\r\n\t\t\t\tfor ( l = 0; l < num; l++ )\r\n\t\t\t\t{\r\n\t\t\t\t\t//\r\n\t\t\t\t\tcolor = *pSrcDataTemp;\r\n\r\n\t\t\t\t\tr[m] = (color&ARGB8888_MASK_RED)>>16&0xFF;\r\n\t\t\t\t\tg[m] = (color&ARGB8888_MASK_GREEN)>>8&0xFF;\r\n\t\t\t\t\tb[m] = (color&ARGB8888_MASK_BLUE)&0xFF;\r\n\r\n\t\t\t\t\tpSrcDataTemp++;\r\n\t\t\t\t\tm++;\r\n\t\t\t\t}\r\n\t\t\t\tpSrcDataTemp += offset;\r\n\t\t\t}\r\n\t\t\t/*\r\n\t\t\t//Sort( r, SortArray, num2 );\r\n\t\t\tqsort( r, num2 );\r\n\t\t\trColor = SortArray[num22];\r\n\t\t\tqsort( g, num2 );\r\n\t\t\t//Sort( g, SortArray, num2 );\r\n\t\t\tgColor = SortArray[num22];\r\n\t\t\t//Sort( b, SortArray, num2 );\r\n\t\t\tqsort( b, num2 );\r\n\t\t\tbColor = SortArray[num22];\r\n\t\t\t*/\r\n\t\t\tiQuickSort( r, num2 );\r\n\t\t\trColor = r[num22];\r\n\r\n\t\t\tiQuickSort( g, num2 );\r\n\t\t\tgColor = g[num22];\r\n\r\n\t\t\tiQuickSort( b, num2 );\r\n\t\t\tbColor = b[num22];\r\n\r\n\t\t\t*pDstData = ARGB8888_2_COLOR( 255, rColor, gColor, bColor );\r\n\r\n\t\t\tpDstData++;\r\n\t\t\tpSrcData++;\r\n\t\t\t//LOGI(\"SSSS\");\r\n\t\t}\r\n\t}\r\n\r\n\r\n\t//LOGI(\"SSSS ssussusuu\");\r\n\treturn;\r\n}\r\n\r\n\r\n\r\nstatic void iColorWaterFilter( int * pDstImage, int * pSrcImage, int width, int height  )\r\n{\r\n\tint i = 0;\r\n\tint j = 0;\r\n\r\n\tint rColor;\r\n\tint gColor;\r\n\tint bColor;\r\n\r\n\tint color;\r\n\tint * pDesData = pDstImage;\r\n\tint * pSrcData = pSrcImage;\r\n\tint * pSrcDataTemp = pSrcData;\r\n\r\n\t//int colorAdd;\r\n\t//int colorSub;\r\n\r\n\tint colorSubLow;\r\n\tint colorSubHigh;\r\n\r\n\tint colorAddLow;\r\n\tint colorAddHigh;\r\n\r\n\r\n//\tfilterCoef[0]=0;   filterCoef[1]=-(1<<15);  filterCoef[2]=0;\r\n//\tfilterCoef[3]=-(1<<15);  filterCoef[4]=5<<15;   filterCoef[5]=-(1<<15);\r\n//\tfilterCoef[6]=0;   filterCoef[7]=-(1<<15);  filterCoef[8]=0;\r\n\r\n\r\n\tmemcpy( pDesData, pSrcData, sizeof(int)*width*height );\r\n\r\n\tpDesData += width;\r\n\r\n\tchar name[128];\r\n\r\n\tfor ( j = 1; j < height - 1; j++ )\r\n\t{\r\n\t\tfor ( i = 0; i < width; i++ )\r\n\t\t{\r\n\t\t\t//\r\n\t\t\tpSrcDataTemp = pSrcData;\r\n\r\n\r\n\t\t\tcolor = *(pSrcDataTemp+1);\r\n\t\t\t//colorSub = (( color | (color << 16) ) & 0x07E0F81F);\r\n\r\n\t\t\tcolorSubLow  =  color&0x00FF00FF;\r\n\t\t\tcolorSubHigh = (color&0xFF00FF00)>>8;\r\n\r\n\t\t\tpSrcDataTemp += width;\r\n\t\t\tcolor = *(pSrcDataTemp);\r\n\t\t\t//colorSub += (( color | (color << 16) ) & 0x07E0F81F);\r\n\r\n\t\t\tcolorSubLow  +=  color&0x00FF00FF;\r\n\t\t\tcolorSubHigh += (color&0xFF00FF00)>>8;\r\n\r\n\t\t\t//x5\r\n\t\t\tcolor = *(pSrcDataTemp+1);\r\n\t\t\t//colorAdd = (( color | (color << 16) ) & 0x07E0F81F);\r\n\t\t\t//colorAdd = (colorAdd<<2) + colorAdd;\r\n\r\n\t\t\t//sprintf(name, \"color =%x\",color);\r\n\t\t\t//LOGI(name);\r\n\r\n\t\t\tcolorAddLow  =      ((color&0x00FF00FF)<<2) +     (color&0x00FF00FF);\r\n\t\t\tcolorAddHigh = (((color&0xFF00FF00)>>8)<<2) + ((color&0xFF00FF00)>>8);\r\n\r\n\t\t\t//sprintf(name, \"spit:%x,%x\",colorAddLow,colorAddHigh);\r\n\t\t\t//LOGI(name);\r\n\r\n\t\t\t//colorAddLow  = ((color&0x00FF00FF))*5;\r\n\t\t\t//colorAddHigh = (((color>>8)&0x00FF00FF))*5;\r\n\r\n\r\n\t\t\tcolor = *(pSrcDataTemp+2);\r\n\t\t\t//colorSub += (( color | (color << 16) ) & 0x07E0F81F);\r\n\r\n\t\t\tcolorSubLow  +=  color&0x00FF00FF;\r\n\t\t\tcolorSubHigh += (color&0xFF00FF00)>>8;\r\n\r\n\t\t\tpSrcDataTemp += width;\r\n\t\t\tcolor = *(pSrcDataTemp+1);\r\n\t\t\t//colorSub += (( color | (color << 16) ) & 0x07E0F81F);\r\n\t\t\tcolorSubLow  +=  color&0x00FF00FF;\r\n\t\t\tcolorSubHigh += (color&0xFF00FF00)>>8;\r\n\r\n\r\n\r\n\t\t\trColor = ((colorAddLow)>>16);\r\n\t\t\tgColor = (colorAddHigh&0xFFFF);\r\n\t\t\tbColor =  (colorAddLow&0xFFFF);\r\n\r\n\t\t\t//sprintf(name, \"rcolor =%x,%x,%x\",rColor,gColor,bColor);\r\n\t\t\t//LOGI(name);\r\n\r\n\t\t\trColor -= (colorSubLow>>16);\r\n\t\t\tgColor -= (colorSubHigh&0xFFFF);\r\n\t\t\tbColor -= (colorSubLow)&0xFFFF;\r\n\r\n\t\t\tif ( rColor > 255 )\r\n\t\t\t{\r\n\t\t\t\trColor = 255;\r\n\t\t\t}\r\n\t\t\telse if ( rColor < 0 )\r\n\t\t\t{\r\n\t\t\t\trColor = 0;\r\n\t\t\t}\r\n\r\n\t\t\tif ( gColor > 255 )\r\n\t\t\t{\r\n\t\t\t\tgColor = 255;\r\n\t\t\t}\r\n\t\t\telse if ( gColor < 0 )\r\n\t\t\t{\r\n\t\t\t\tgColor = 0;\r\n\t\t\t}\r\n\r\n\t\t\tif ( bColor > 255 )\r\n\t\t\t{\r\n\t\t\t\tbColor = 255;\r\n\t\t\t}\r\n\t\t\telse if ( bColor < 0 )\r\n\t\t\t{\r\n\t\t\t\tbColor = 0;\r\n\t\t\t}\r\n\t\t\tcolor = ARGB8888_2_COLOR(255, rColor, gColor, bColor);//*(pSrcDataTemp+1);//ARGB8888_2_COLOR(255, rColor, gColor, bColor);//ARGB8888_2_COLOR(255, rColor, gColor, bColor );\r\n\r\n\t\t\t//sprintf(name, \"last color =%x\",color);\r\n\t\t\t//LOGI(name);\r\n\r\n\t\t\t*pDesData = color;\r\n\t\t\tpDesData++;\r\n\t\t\tpSrcData++;\r\n\t\t}\r\n\t}\r\n\treturn;\r\n}\r\n\r\nvoid colorWaterPaint( int * pSrcImage, int width, int height, int stride, int radius ){\r\n//\r\n\t//LOGI(\"start colorWaterPaint  mmmmm\");\r\n\r\n\tint *pDstData = (int *)malloc( sizeof(int)*width*height );\r\n\tif ( pDstData == NULL )\r\n\t{\r\n\t\treturn;\r\n\t}\r\n\r\n\timMedian( pDstData, pSrcImage, width, height, radius );\r\n\r\n\t//memcpy(pSrcImage,pDstData,sizeof(int)*width*height);\r\n\t//CommonFilter( &DesImageCpy, pDesImage, 3, filterCoef );\r\n\tiColorWaterFilter( pSrcImage, pDstData,  width, height );\r\n\r\n\r\n\tif ( pDstData != NULL ){\r\n\t\tfree( pDstData );\r\n\t\tpDstData = 0;\r\n\t}\r\n}\r\n\r\n"
  },
  {
    "path": "WayHoo/jni/imageProcess.h",
    "content": "#ifndef _IMAGE_PROCESS_H_\n#define _IMAGE_PROCESS_H_\n\nvoid gaussBlurProcess( uint8_t *colorImg, int width, int height );\nvoid clearColorProcess(uint8_t *image, int width, int height, int color);\nvoid boxBlurProcess(uint8_t *image, int width, int height, int stride, int radius);\n\nvoid stackBlurProcess(uint8_t *image, int width, int height, int stride, int radius);\nvoid oilPaintProcess(uint8_t *image, int width, int height, int stride, int radius);\nvoid colorWaterPaint( int * pSrcImage, int width, int heiht, int stride, int radius );\n\n\n#endif\n"
  },
  {
    "path": "WayHoo/proguard-project.txt",
    "content": "# To enable ProGuard in your project, edit project.properties\n# to define the proguard.config property as described in that file.\n#\n# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in ${sdk.dir}/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the ProGuard\n# include property in project.properties.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "WayHoo/project.properties",
    "content": "# This file is automatically generated by Android Tools.\n# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n#\n# This file must be checked in Version Control Systems.\n#\n# To customize properties used by the Ant build system edit\n# \"ant.properties\", and override values to adapt the script to your\n# project structure.\n#\n# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):\n#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt\n\n# Project target.\ntarget=android-21\n"
  },
  {
    "path": "WayHoo/res/anim/count_down_exit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (c) 2013, The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\" >\n\n    <alpha\n        android:duration=\"1000\"\n        android:fromAlpha=\"1.0\"\n        android:toAlpha=\"0.0\" />\n\n    <scale\n        android:duration=\"800\"\n        android:fromXScale=\"1.0\"\n        android:fromYScale=\"1.0\"\n        android:pivotX=\"50%\"\n        android:pivotY=\"50%\"\n        android:toXScale=\"3.0\"\n        android:toYScale=\"3.0\" />\n\n</set>"
  },
  {
    "path": "WayHoo/res/anim/fade_out.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<alpha xmlns:android=\"http://schemas.android.com/apk/res/android\" \n    android:interpolator=\"@android:interpolator/accelerate_quad\" \n    android:fromAlpha=\"1.0\"\n    android:toAlpha=\"0.0\"\n    android:duration=\"@integer/dragged_item_fade_out_duration\" \n    android:fillEnabled=\"true\"\n    android:fillAfter=\"true\"\n/>\n"
  },
  {
    "path": "WayHoo/res/anim/in_from_bottom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n\r\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\r\n    <translate \r\n        android:fromYDelta=\"100%\"\r\n        android:toYDelta=\"0\"\r\n        android:fillAfter=\"false\"\r\n        android:duration=\"300\"   \r\n        /> \r\n    <alpha \r\n        android:fromAlpha=\"0.0\"\r\n        android:toAlpha=\"1.0\"\r\n        android:duration=\"300\"\r\n        android:fillAfter=\"true\"/>\r\n    </set>"
  },
  {
    "path": "WayHoo/res/anim/in_from_top.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n\r\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\r\n    <translate \r\n        android:fromYDelta=\"-100%\"\r\n        android:toYDelta=\"0\"\r\n        android:fillAfter=\"false\"\r\n        android:duration=\"300\"   \r\n        /> \r\n    <alpha \r\n        android:fromAlpha=\"0.0\"\r\n        android:toAlpha=\"1.0\"\r\n        android:duration=\"300\"\r\n        android:fillAfter=\"true\"/>\r\n    </set>"
  },
  {
    "path": "WayHoo/res/anim/out_from_bottom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n\r\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\r\n   <translate \r\n        android:fromYDelta=\"0\"\r\n        android:toYDelta=\"100%\"\r\n        android:fillAfter=\"false\"\r\n        android:duration=\"300\"   \r\n        /> \r\n    <alpha \r\n        android:fromAlpha=\"1.0\"\r\n        android:toAlpha=\"0.0\"\r\n        android:duration=\"300\"\r\n        android:fillAfter=\"true\"/>\r\n    </set>"
  },
  {
    "path": "WayHoo/res/anim/out_from_top.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n\r\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\r\n   <translate \r\n        android:fromYDelta=\"0\"\r\n        android:toYDelta=\"-100%\"\r\n        android:fillAfter=\"false\"\r\n        android:duration=\"300\"   \r\n        /> \r\n    <alpha \r\n        android:fromAlpha=\"1.0\"\r\n        android:toAlpha=\"0.0\"\r\n        android:duration=\"300\"\r\n        android:fillAfter=\"true\"/>\r\n    </set>"
  },
  {
    "path": "WayHoo/res/anim/push_right_out.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\" >\r\n\r\n    <translate\r\n        android:duration=\"800\"\r\n        android:fromXDelta=\"0.0\"\r\n        android:toXDelta=\"100.0%p\" />\r\n\r\n  <!--   <alpha\r\n        android:duration=\"1000\"\r\n        android:fromAlpha=\"1.0\"\r\n        android:toAlpha=\"0.8\" /> -->\r\n\r\n</set>"
  },
  {
    "path": "WayHoo/res/anim/slide_in_from_bottom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2009 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n\n<translate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"@android:integer/config_longAnimTime\"\n    android:fromYDelta=\"100%p\"\n    android:toYDelta=\"0\" />\n"
  },
  {
    "path": "WayHoo/res/anim/slide_in_from_top.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2009 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n\n<translate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"@android:integer/config_longAnimTime\"\n    android:fromYDelta=\"-100%p\"\n    android:toYDelta=\"0\" />\n"
  },
  {
    "path": "WayHoo/res/anim/slide_out_to_bottom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2009 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n\n<translate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"@android:integer/config_longAnimTime\"\n    android:fromYDelta=\"0\"\n    android:toYDelta=\"100%p\" />\n"
  },
  {
    "path": "WayHoo/res/anim/slide_out_to_top.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2009 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n\n<translate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"@android:integer/config_longAnimTime\"\n    android:fromYDelta=\"0\"\n    android:toYDelta=\"-100%p\" />\n"
  },
  {
    "path": "WayHoo/res/color/vpi__dark_theme.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2010 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n  \n          http://www.apache.org/licenses/LICENSE-2.0\n  \n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_enabled=\"false\" android:color=\"@color/vpi__bright_foreground_disabled_holo_dark\"/>\n    <item android:state_window_focused=\"false\" android:color=\"@color/vpi__bright_foreground_holo_dark\"/>\n    <item android:state_pressed=\"true\" android:color=\"@color/vpi__bright_foreground_holo_dark\"/>\n    <item android:state_selected=\"true\" android:color=\"@color/vpi__bright_foreground_holo_dark\"/>\n    <!--item android:state_activated=\"true\" android:color=\"@color/vpi__bright_foreground_holo_dark\"/-->\n    <item android:color=\"@color/vpi__bright_foreground_holo_dark\"/> <!-- not selected -->\n</selector>\n"
  },
  {
    "path": "WayHoo/res/color/vpi__light_theme.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2010 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n  \n          http://www.apache.org/licenses/LICENSE-2.0\n  \n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_enabled=\"false\" android:color=\"@color/vpi__bright_foreground_disabled_holo_light\"/>\n    <item android:state_window_focused=\"false\" android:color=\"@color/vpi__bright_foreground_holo_light\"/>\n    <item android:state_pressed=\"true\" android:color=\"@color/vpi__bright_foreground_holo_light\"/>\n    <item android:state_selected=\"true\" android:color=\"@color/vpi__bright_foreground_holo_light\"/>\n    <!--item android:state_activated=\"true\" android:color=\"@color/vpi__bright_foreground_holo_light\"/-->\n    <item android:color=\"@color/vpi__bright_foreground_holo_light\"/> <!-- not selected -->\n    \n</selector>\n\n"
  },
  {
    "path": "WayHoo/res/drawable/city_edit_prs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector\n  xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/city_delete\" />\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/btn_citylist_delete_pressed\" />\n</selector>\n"
  },
  {
    "path": "WayHoo/res/drawable/city_query_delete.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector\n  xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/btn_search_clear_pressed1\" />\n    <item android:drawable=\"@drawable/btn_search_clear_normal1\" />\n</selector>\n"
  },
  {
    "path": "WayHoo/res/drawable/cityselector_locate_btn_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:drawable=\"@drawable/cityselector_locate_btn_bg_pressed\" android:state_pressed=\"true\"/>\n    <item android:drawable=\"@drawable/cityselector_locate_btn_bg_normal\"/>\n\n</selector>"
  },
  {
    "path": "WayHoo/res/drawable/home_setting_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:drawable=\"@drawable/home_setting_background\" android:state_pressed=\"true\"/>\n\n</selector>"
  },
  {
    "path": "WayHoo/res/drawable/indicator_bg_bottom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\" >\n\n    <solid android:color=\"#40000000\" />\n\n    <!--\n    I know the android:radius is useless here but it's needed to fix an old bug:\n    http://code.google.com/p/android/issues/detail?id=939\n    -->\n    <corners\n        android:bottomLeftRadius=\"0dp\"\n        android:bottomRightRadius=\"0dp\"\n        android:radius=\"1dp\"\n        android:topLeftRadius=\"@dimen/indicator_corner_radius\"\n        android:topRightRadius=\"@dimen/indicator_corner_radius\" />\n\n</shape>"
  },
  {
    "path": "WayHoo/res/drawable/indicator_bg_top.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\" >\n\n    <solid android:color=\"#40000000\" />\n\n    <!--\n    I know the android:radius is useless here but it's needed to fix an old bug:\n    http://code.google.com/p/android/issues/detail?id=939\n    -->\n    <corners\n        android:bottomLeftRadius=\"@dimen/indicator_corner_radius\"\n        android:bottomRightRadius=\"@dimen/indicator_corner_radius\"\n        android:radius=\"1dp\"\n        android:topLeftRadius=\"0dp\"\n        android:topRightRadius=\"0dp\" />\n\n</shape>"
  },
  {
    "path": "WayHoo/res/drawable/item_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:drawable=\"@drawable/pressed_application_background\" android:state_pressed=\"true\"/>\n\n</selector>"
  },
  {
    "path": "WayHoo/res/drawable/item_prs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:drawable=\"@drawable/settings_bg_city_item_down\" android:state_pressed=\"true\"/>\n    <item android:drawable=\"@drawable/settings_bg_city_item_up\"/>\n</selector>"
  },
  {
    "path": "WayHoo/res/drawable/listview_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:drawable=\"@drawable/settings_bg_city_item_up\" android:state_pressed=\"true\"/>\n    <item android:drawable=\"@android:color/transparent\"/>\n\n</selector>"
  },
  {
    "path": "WayHoo/res/drawable/main_city_name_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:drawable=\"@drawable/main_city_name_bg_selected\" android:state_pressed=\"true\"/>\n    <item android:drawable=\"@color/transparent\"/>\n\n</selector>"
  },
  {
    "path": "WayHoo/res/drawable/main_life_complete_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:drawable=\"@drawable/main_life_complete\" android:state_focused=\"false\" android:state_pressed=\"false\"/>\n    <item android:drawable=\"@drawable/main_life_complete_pressed\" android:state_pressed=\"true\"/>\n\n</selector>"
  },
  {
    "path": "WayHoo/res/drawable/main_life_edit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:drawable=\"@drawable/main_life_edit_default\" android:state_enabled=\"true\" android:state_focused=\"false\" android:state_pressed=\"false\"/>\n    <item android:drawable=\"@drawable/main_life_edit_press\" android:state_pressed=\"true\"/>\n    <item android:drawable=\"@drawable/main_life_edit_press\" android:state_enabled=\"false\"/>\n\n</selector>"
  },
  {
    "path": "WayHoo/res/drawable/module_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    android:padding=\"0.0dip\"\r\n    android:shape=\"rectangle\" >\r\n\r\n    <solid android:color=\"#33000000\" />\r\n\r\n    <corners\r\n        android:bottomLeftRadius=\"2.0dip\"\r\n        android:bottomRightRadius=\"2.0dip\"\r\n        android:topLeftRadius=\"2.0dip\"\r\n        android:topRightRadius=\"2.0dip\" />\r\n\r\n</shape>"
  },
  {
    "path": "WayHoo/res/drawable/refresh_icon_anim.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<animation-list xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    android:id=\"@+id/selected\"\r\n    android:oneshot=\"false\" >\r\n\r\n    <item\r\n        android:drawable=\"@drawable/refresh_27x27_1\"\r\n        android:duration=\"25\"/>\r\n    <item\r\n        android:drawable=\"@drawable/refresh_27x27_2\"\r\n        android:duration=\"25\"/>\r\n    <item\r\n        android:drawable=\"@drawable/refresh_27x27_3\"\r\n        android:duration=\"25\"/>\r\n    <item\r\n        android:drawable=\"@drawable/refresh_27x27_4\"\r\n        android:duration=\"25\"/>\r\n    <item\r\n        android:drawable=\"@drawable/refresh_27x27_5\"\r\n        android:duration=\"25\"/>\r\n    <item\r\n        android:drawable=\"@drawable/refresh_27x27_6\"\r\n        android:duration=\"25\"/>\r\n    <item\r\n        android:drawable=\"@drawable/refresh_27x27_7\"\r\n        android:duration=\"25\"/>\r\n    <item\r\n        android:drawable=\"@drawable/refresh_27x27_8\"\r\n        android:duration=\"25\"/>\r\n    <item\r\n        android:drawable=\"@drawable/refresh_27x27_9\"\r\n        android:duration=\"25\"/>\r\n    <item\r\n        android:drawable=\"@drawable/refresh_27x27_10\"\r\n        android:duration=\"25\"/>\r\n    <item\r\n        android:drawable=\"@drawable/refresh_27x27_11\"\r\n        android:duration=\"25\"/>\r\n    <item\r\n        android:drawable=\"@drawable/refresh_27x27_12\"\r\n        android:duration=\"25\"/>\r\n    <item\r\n        android:drawable=\"@drawable/refresh_27x27_13\"\r\n        android:duration=\"25\"/>\r\n    <item\r\n        android:drawable=\"@drawable/refresh_27x27_14\"\r\n        android:duration=\"25\"/>\r\n    <item\r\n        android:drawable=\"@drawable/refresh_27x27_15\"\r\n        android:duration=\"25\"/>\r\n    <item\r\n        android:drawable=\"@drawable/refresh_27x27_16\"\r\n        android:duration=\"25\"/>\r\n    <item\r\n        android:drawable=\"@drawable/refresh_27x27_17\"\r\n        android:duration=\"25\"/>\r\n    <item\r\n        android:drawable=\"@drawable/refresh_27x27_18\"\r\n        android:duration=\"25\"/>\r\n\r\n</animation-list>"
  },
  {
    "path": "WayHoo/res/drawable/resource_circle_progressbar_indeterminate.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<rotate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:drawable=\"@drawable/update_forecast_selector\"\n    android:fromDegrees=\"0.0\"\n    android:pivotX=\"50.0%\"\n    android:pivotY=\"50.0%\"\n    android:toDegrees=\"1080.0\" />\n"
  },
  {
    "path": "WayHoo/res/drawable/setting_top_back.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector\n  xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_focused=\"false\" android:state_pressed=\"false\" android:drawable=\"@drawable/setting_top_back_default\" />\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/setting_top_back_selected\" />\n</selector>\n"
  },
  {
    "path": "WayHoo/res/drawable/settings_bg_city_item_down.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\" >\n\n    <solid android:color=\"#60ffffff\" />\n\n    <stroke\n        android:width=\"1.0dip\"\n        android:color=\"#65ffffff\" />\n\n    <corners android:radius=\"4.0dip\" />\n\n</shape>"
  },
  {
    "path": "WayHoo/res/drawable/settings_bg_city_item_up.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\" >\n\n    <solid android:color=\"#30ffffff\" />\n\n    <stroke\n        android:width=\"1.0dip\"\n        android:color=\"#35ffffff\" />\n\n    <corners android:radius=\"4.0dip\" />\n\n</shape>"
  },
  {
    "path": "WayHoo/res/drawable/shape_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\" >\n\n    <solid android:color=\"#80000000\" />\n\n    <stroke\n        android:width=\"1.0dip\"\n        android:color=\"#e0000000\" />\n\n    <corners android:radius=\"4.0dip\" />\n\n</shape>"
  },
  {
    "path": "WayHoo/res/drawable/sidebar_item_background_dark_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:drawable=\"@color/sidebar_item_background_activated_dark\" android:state_pressed=\"true\"/>\n    <item android:drawable=\"@color/sidebar_item_background_activated_dark\" android:state_checked=\"true\"/>\n    <item android:drawable=\"@drawable/sidebar_row_dark\"/>\n    <item android:drawable=\"@drawable/sidebar_item_selected_layerlist_dark\" android:state_middle=\"true\"/>\n\n</selector>"
  },
  {
    "path": "WayHoo/res/drawable/sidebar_item_background_light_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:drawable=\"@color/sidebar_item_background_activated_dark\" android:state_pressed=\"true\"/>\n    <item android:drawable=\"@color/sidebar_item_background_activated_dark\" android:state_checked=\"true\"/>\n    <item android:drawable=\"@drawable/sidemenu_item_normal\"/>\n\n</selector>"
  },
  {
    "path": "WayHoo/res/drawable/sidebar_item_selected_layerlist_dark.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list\n  xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@color/sidebar_item_background_dark\" />\n    <item android:drawable=\"@color/sidebar_item_background_activated_dark\" />\n</layer-list>"
  },
  {
    "path": "WayHoo/res/drawable/slidingmenu_shadow.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\" >\n\n    <gradient\n        android:centerColor=\"#33000000\"\n        android:endColor=\"#73000000\"\n        android:startColor=\"#00000000\" />\n\n</shape>"
  },
  {
    "path": "WayHoo/res/drawable/topbar_icon_share_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <!--\n    <item android:drawable=\"@drawable/topbar_icon_share_normal\" android:state_focused=\"false\" android:state_pressed=\"false\"/>\n    <item android:drawable=\"@drawable/topbar_icon_share_pressed\" android:state_pressed=\"true\"/>\n    -->\n    <item android:drawable=\"@drawable/sidebar_icon_share_dark\" android:state_enabled=\"true\" android:state_focused=\"false\" android:state_pressed=\"false\"/>\n    <item android:drawable=\"@drawable/sidebar_icon_share_light\" android:state_enabled=\"false\"/>\n    <item android:drawable=\"@drawable/sidebar_icon_share_light\" android:state_pressed=\"true\"/>\n\n</selector>"
  },
  {
    "path": "WayHoo/res/drawable/topbar_icon_side_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:drawable=\"@drawable/topbar_icon_side_pressed\" android:state_pressed=\"true\"/>\n    <item android:drawable=\"@drawable/topbar_icon_side\"/>\n\n</selector>"
  },
  {
    "path": "WayHoo/res/drawable/update_forecast_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:drawable=\"@drawable/main_update_icon\" android:state_enabled=\"true\" android:state_focused=\"false\" android:state_pressed=\"false\"/>\n    <item android:drawable=\"@drawable/main_update_icon_pressed\" android:state_pressed=\"true\"/>\n    <item android:drawable=\"@drawable/main_update_icon_pressed\" android:state_enabled=\"false\"/>\n\n</selector>"
  },
  {
    "path": "WayHoo/res/drawable/vpi__tab_indicator.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2008 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n  \n          http://www.apache.org/licenses/LICENSE-2.0\n  \n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <!-- Non focused states -->\n    <item android:state_focused=\"false\" android:state_selected=\"false\" android:state_pressed=\"false\" android:drawable=\"@drawable/vpi__tab_unselected_holo\" />\n    <item android:state_focused=\"false\" android:state_selected=\"true\"  android:state_pressed=\"false\" android:drawable=\"@drawable/vpi__tab_selected_holo\" />\n\n    <!-- Focused states -->\n    <item android:state_focused=\"true\" android:state_selected=\"false\" android:state_pressed=\"false\" android:drawable=\"@drawable/vpi__tab_unselected_focused_holo\" />\n    <item android:state_focused=\"true\" android:state_selected=\"true\"  android:state_pressed=\"false\" android:drawable=\"@drawable/vpi__tab_selected_focused_holo\" />\n\n    <!-- Pressed -->\n    <!--    Non focused states -->\n    <item android:state_focused=\"false\" android:state_selected=\"false\" android:state_pressed=\"true\" android:drawable=\"@drawable/vpi__tab_unselected_pressed_holo\" />\n    <item android:state_focused=\"false\" android:state_selected=\"true\"  android:state_pressed=\"true\" android:drawable=\"@drawable/vpi__tab_selected_pressed_holo\" />\n\n    <!--    Focused states -->\n    <item android:state_focused=\"true\" android:state_selected=\"false\" android:state_pressed=\"true\" android:drawable=\"@drawable/vpi__tab_unselected_pressed_holo\" />\n    <item android:state_focused=\"true\" android:state_selected=\"true\"  android:state_pressed=\"true\" android:drawable=\"@drawable/vpi__tab_selected_pressed_holo\" />\n</selector>\n"
  },
  {
    "path": "WayHoo/res/drawable/weather_detail_activity_btn_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:drawable=\"@drawable/weather_detail_activity_btn\" android:state_pressed=\"true\"/>\n\n</selector>"
  },
  {
    "path": "WayHoo/res/drawable/white_border.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\" >\r\n\r\n    <solid android:color=\"@android:color/transparent\" />\r\n\r\n    <corners\r\n        android:bottomLeftRadius=\"2.0dip\"\r\n        android:bottomRightRadius=\"2.0dip\"\r\n        android:topLeftRadius=\"2.0dip\"\r\n        android:topRightRadius=\"2.0dip\" />\r\n\r\n    <stroke\r\n        android:width=\"0.5dip\"\r\n        android:color=\"#4dffffff\" />\r\n\r\n</shape>"
  },
  {
    "path": "WayHoo/res/layout/about.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:weather=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:background=\"@drawable/bg_addcity\"\n    android:orientation=\"vertical\" >\n\n    <include\n        android:id=\"@+id/top_bar\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        layout=\"@layout/city_query_actionbar\" />\n\n    <ScrollView\n        android:id=\"@+id/scrollView\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\"\n        android:layout_marginTop=\"@dimen/abs__action_bar_default_height\"\n        android:fadingEdge=\"none\"\n        android:fillViewport=\"true\" >\n\n        <RelativeLayout\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"fill_parent\"\n            android:paddingLeft=\"10.0dip\"\n            android:paddingRight=\"10.0dip\" >\n\n            <ImageView\n                android:id=\"@+id/brand\"\n                android:layout_width=\"70.0dip\"\n                android:layout_height=\"70.0dip\"\n                android:layout_centerHorizontal=\"true\"\n                android:layout_marginTop=\"30.0dip\"\n                android:contentDescription=\"@null\"\n                android:onClick=\"showChangeLog\"\n                android:src=\"@drawable/ic_launcher\" />\n\n            <TextView\n                android:id=\"@+id/subVersion\"\n                style=\"@style/Label.Shadow\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_below=\"@id/brand\"\n                android:layout_centerHorizontal=\"true\"\n                android:layout_marginTop=\"3.0dip\"\n                android:includeFontPadding=\"false\" />\n\n            <TextView\n                android:id=\"@+id/app_information\"\n                style=\"@style/Label.Shadow.Medium\"\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_below=\"@id/subVersion\"\n                android:layout_gravity=\"center\"\n                android:layout_marginTop=\"20.0dip\"\n                android:duplicateParentState=\"true\"\n                android:linksClickable=\"true\"\n                android:paddingBottom=\"5dip\"\n                android:paddingLeft=\"10.0dip\"\n                android:paddingRight=\"10.0dip\"\n                android:paddingTop=\"5dip\"\n                android:singleLine=\"false\"\n                android:text=\"@string/app_information\"\n                android:textSize=\"15sp\" />\n\n            <LinearLayout\n                android:id=\"@+id/upgradeLayout\"\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"44.0dip\"\n                android:layout_below=\"@id/app_information\"\n                android:layout_marginTop=\"20.0dip\"\n                android:clickable=\"true\"\n                android:focusable=\"true\"\n                android:orientation=\"horizontal\" >\n\n                <TextView\n                    android:id=\"@+id/textView1\"\n                    style=\"@style/Label.Medium\"\n                    android:layout_width=\"0dip\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_gravity=\"center\"\n                    android:layout_weight=\"1\"\n                    android:duplicateParentState=\"true\"\n                    android:gravity=\"center\"\n                    android:paddingLeft=\"10.0dip\"\n                    android:paddingRight=\"10.0dip\"\n                    android:singleLine=\"true\"\n                    android:text=\"作者:\"\n                    android:textSize=\"15sp\" />\n\n                <TextView\n                    style=\"@style/Label.Medium\"\n                    android:layout_width=\"0dip\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_gravity=\"center\"\n                    android:layout_weight=\"3\"\n                    android:duplicateParentState=\"true\"\n                    android:gravity=\"center\"\n                    android:paddingLeft=\"10.0dip\"\n                    android:paddingRight=\"10.0dip\"\n                    android:singleLine=\"true\"\n                    android:text=\"way\"\n                    android:textSize=\"15sp\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"44.0dip\"\n                android:layout_below=\"@id/upgradeLayout\"\n                android:clickable=\"true\"\n                android:focusable=\"true\"\n                android:orientation=\"horizontal\" >\n\n                <TextView\n                    android:id=\"@+id/textView1\"\n                    style=\"@style/Label.Medium\"\n                    android:layout_width=\"0dip\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_gravity=\"center\"\n                    android:layout_weight=\"1\"\n                    android:duplicateParentState=\"true\"\n                    android:gravity=\"center\"\n                    android:paddingLeft=\"10.0dip\"\n                    android:paddingRight=\"10.0dip\"\n                    android:singleLine=\"true\"\n                    android:text=\"邮箱:\"\n                    android:textSize=\"15sp\" />\n\n                <TextView\n                    style=\"@style/Label.Medium\"\n                    android:layout_width=\"0dip\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_gravity=\"center\"\n                    android:layout_weight=\"3\"\n                    android:duplicateParentState=\"true\"\n                    android:gravity=\"center\"\n                    android:linksClickable=\"true\"\n                    android:paddingLeft=\"10.0dip\"\n                    android:paddingRight=\"10.0dip\"\n                    android:singleLine=\"true\"\n                    android:text=\"way.ping.li@gmail.com\"\n                    android:textSize=\"15sp\" />\n            </LinearLayout>\n\n            <TextView\n                android:id=\"@+id/copyright\"\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentBottom=\"true\"\n                android:layout_marginBottom=\"20.0dip\"\n                android:gravity=\"bottom|center\"\n                android:includeFontPadding=\"false\"\n                android:singleLine=\"true\"\n                android:text=\"@string/all_rights_reserved\"\n                android:textColor=\"#ffbbbbbb\"\n                android:textSize=\"12.0sp\" />\n        </RelativeLayout>\n    </ScrollView>\n\n</RelativeLayout>"
  },
  {
    "path": "WayHoo/res/layout/activity_main.xml",
    "content": "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:weather=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/root_view\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n     >\n\n    <Button\n        android:id=\"@+id/add_city_btn\"\n        style=\"@style/Label.Shadow\"\n        android:layout_gravity=\"center\"\n        android:background=\"@drawable/cityselector_locate_btn_bg\"\n        android:gravity=\"center\"\n        android:text=\"添加城市\"\n        android:visibility=\"gone\" />\n\n    <android.support.v4.view.ViewPager\n        android:id=\"@+id/main_viewpager\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n    <include layout=\"@layout/activity_main_actionbar\" />\n\n    <ImageView\n        android:id=\"@+id/blur_overlay_img\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:alpha=\"0\"\n        android:visibility=\"gone\" />\n\n</FrameLayout>"
  },
  {
    "path": "WayHoo/res/layout/activity_main_actionbar.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:weather=\"http://schemas.android.com/apk/res-auto\" >\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/abs__action_bar_default_height\"\n        android:background=\"@android:color/transparent\" >\n\n        <ImageView\n            android:id=\"@+id/sidebarButton\"\n            android:layout_width=\"48dip\"\n            android:layout_height=\"48dip\"\n            android:layout_alignParentLeft=\"true\"\n            android:layout_centerInParent=\"true\"\n            android:layout_marginLeft=\"8dip\"\n            android:minHeight=\"@dimen/topbar_icon_size\"\n            android:minWidth=\"@dimen/topbar_icon_size\"\n            android:padding=\"12dip\"\n            android:src=\"@drawable/topbar_icon_side_selector\" />\n            android:scaleType=\"centerCrop\"\n\n        <ImageView\n            android:id=\"@+id/shareButton\"\n            android:layout_width=\"48dip\"\n            android:layout_height=\"48dip\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_centerInParent=\"true\"\n            android:layout_marginRight=\"8dip\"\n            android:minHeight=\"@dimen/topbar_icon_size\"\n            android:minWidth=\"@dimen/topbar_icon_size\"\n            android:padding=\"12dip\"\n            android:src=\"@drawable/topbar_icon_share_selector\" />\n            android:scaleType=\"centerCrop\"\n\n        <!--\n             <com.viewpagerindicator.TitlePageIndicator\n            android:id=\"@+id/titles\"\n            style=\"@style/Label.Shadow\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"match_parent\"\n            android:layout_centerInParent=\"true\"\n            android:layout_toLeftOf=\"@id/addLocationButton\"\n            android:layout_toRightOf=\"@id/sidebarButton\"\n            android:background=\"@color/transparent\"\n            android:textColor=\"@android:color/transparent\"\n            weather:footerIndicatorStyle=\"none\"\n            weather:footerLineHeight=\"0dp\"\n            weather:selectedColor=\"@android:color/white\"\n            weather:titlePadding=\"0dp\" />\n        -->\n\n        <com.way.ui.view.WeatherTypefacedTextView\n            android:id=\"@+id/location_city_textview\"\n            style=\"@style/Label.Shadow\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            android:background=\"@drawable/main_city_name_bg\"\n            android:gravity=\"center\"\n            android:text=\"--\"\n            weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n        <com.viewpagerindicator.CirclePageIndicator\n            android:id=\"@+id/indicator\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@id/location_city_textview\"\n            android:layout_centerHorizontal=\"true\"\n            android:padding=\"2dip\"\n            weather:radius=\"2.5dp\"\n            weather:strokeWidth=\"0.8dp\" />\n\n        <ImageView\n            android:id=\"@+id/curr_loc_icon\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignBottom=\"@id/location_city_textview\"\n            android:layout_alignTop=\"@id/location_city_textview\"\n            android:layout_marginTop=\"-2.0dip\"\n            android:layout_toLeftOf=\"@id/location_city_textview\"\n            android:layout_toStartOf=\"@id/location_city_textview\"\n            android:paddingRight=\"3.0dip\"\n            android:paddingTop=\"3.0dip\"\n            android:src=\"@drawable/current_loc_active_26x26\"\n            android:visibility=\"gone\" />\n    </RelativeLayout>\n\n</merge>"
  },
  {
    "path": "WayHoo/res/layout/base_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\" >\n\n    <include\n        android:id=\"@+id/layoutContent\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        layout=\"@layout/weather_fragment\"\n        android:gravity=\"center\"\n        android:orientation=\"vertical\"\n        android:visibility=\"gone\" />\n\n    <include\n        android:id=\"@+id/layoutLoading\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        layout=\"@layout/comm_lay_loading\" />\n\n    <include\n        android:id=\"@+id/layoutLoadFailed\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        layout=\"@layout/comm_lay_loadfailed\"\n        android:visibility=\"gone\" />\n\n    <include\n        android:id=\"@+id/layoutEmpty\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        layout=\"@layout/comm_lay_emptyview\"\n        android:visibility=\"gone\" />\n\n</FrameLayout>"
  },
  {
    "path": "WayHoo/res/layout/city_manager_actionbar.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:weather=\"http://schemas.android.com/apk/res-auto\" >\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/abs__action_bar_default_height\"\n        android:background=\"@android:color/transparent\" >\n\n        <ImageView\n            android:id=\"@+id/back_image\"\n            android:layout_width=\"48dip\"\n            android:layout_height=\"48dip\"\n            android:layout_alignParentLeft=\"true\"\n            android:layout_centerInParent=\"true\"\n            android:layout_marginLeft=\"8dip\"\n            android:clickable=\"true\"\n            android:focusable=\"true\"\n            android:minHeight=\"@dimen/topbar_icon_size\"\n            android:minWidth=\"@dimen/topbar_icon_size\"\n            android:padding=\"12dip\"\n            android:src=\"@drawable/setting_top_back\" />\n\n        <TextView\n            android:id=\"@+id/city_title\"\n            style=\"@style/Label.Shadow\"\n            android:layout_height=\"match_parent\"\n            android:layout_centerInParent=\"true\"\n            android:gravity=\"center\"\n            android:text=\"城市管理\"\n            android:textColor=\"@android:color/white\"\n            android:textSize=\"20.0sp\" />\n\n        <ImageView\n            android:id=\"@+id/edit_city\"\n            android:layout_width=\"48dip\"\n            android:layout_height=\"48dip\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_centerInParent=\"true\"\n            android:layout_marginRight=\"8dip\"\n            android:clickable=\"true\"\n            android:focusable=\"true\"\n            android:minHeight=\"@dimen/topbar_icon_size\"\n            android:minWidth=\"@dimen/topbar_icon_size\"\n            android:padding=\"12dip\"\n            android:src=\"@drawable/main_life_edit\"\n            android:visibility=\"visible\" />\n\n        <ImageView\n            android:id=\"@+id/confirm_city\"\n            android:layout_width=\"48dip\"\n            android:layout_height=\"48dip\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_centerInParent=\"true\"\n            android:layout_marginRight=\"8dip\"\n            android:clickable=\"true\"\n            android:focusable=\"true\"\n            android:minHeight=\"@dimen/topbar_icon_size\"\n            android:minWidth=\"@dimen/topbar_icon_size\"\n            android:padding=\"12dip\"\n            android:src=\"@drawable/main_life_complete_selector\"\n            android:visibility=\"invisible\" />\n\n        <ImageView\n            android:id=\"@+id/divider_line\"\n            android:layout_width=\"1.0dip\"\n            android:layout_height=\"20.0dip\"\n            android:layout_centerVertical=\"true\"\n            android:layout_toLeftOf=\"@id/confirm_city\"\n            android:background=\"@drawable/line_parting\"\n            android:visibility=\"visible\" />\n\n        <ImageView\n            android:id=\"@+id/refresh_city\"\n            android:layout_width=\"48dip\"\n            android:layout_height=\"48dip\"\n            android:layout_centerVertical=\"true\"\n            android:layout_toLeftOf=\"@id/confirm_city\"\n            android:clickable=\"true\"\n            android:focusable=\"true\"\n            android:minHeight=\"@dimen/topbar_icon_size\"\n            android:minWidth=\"@dimen/topbar_icon_size\"\n            android:padding=\"12dip\"\n            android:src=\"@drawable/update_forecast_selector\"\n            android:visibility=\"visible\" />\n\n        <ProgressBar\n            android:id=\"@+id/refresh_progress\"\n            android:layout_width=\"48dip\"\n            android:layout_height=\"48dip\"\n            android:layout_centerVertical=\"true\"\n            android:layout_gravity=\"center\"\n            android:layout_toLeftOf=\"@id/confirm_city\"\n            android:clickable=\"true\"\n            android:focusable=\"true\"\n            android:indeterminate=\"true\"\n            android:minHeight=\"@dimen/topbar_icon_size\"\n            android:minWidth=\"@dimen/topbar_icon_size\"\n            android:padding=\"12dip\"\n            android:visibility=\"invisible\" />\n    </RelativeLayout>\n\n</merge>"
  },
  {
    "path": "WayHoo/res/layout/city_manager_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/city_manager_bg\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:background=\"@drawable/bg_addcity\"\n    android:descendantFocusability=\"blocksDescendants\" >\n\n    <LinearLayout\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center_horizontal\"\n        android:orientation=\"vertical\" >\n\n        <include\n            android:id=\"@+id/top_bar\"\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            layout=\"@layout/city_manager_actionbar\" />\n\n        <com.way.ui.view.DragSortGridView\n            android:id=\"@+id/my_city\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center_vertical\"\n            android:layout_marginBottom=\"5.0pt\"\n            android:layout_marginLeft=\"5.0pt\"\n            android:layout_marginRight=\"5.0pt\"\n            android:layout_marginTop=\"5.0pt\"\n            android:cacheColorHint=\"#00000000\"\n            android:columnWidth=\"104.0dip\"\n            android:fadingEdge=\"none\"\n            android:fadingEdgeLength=\"0.0dip\"\n            android:horizontalSpacing=\"5.0dip\"\n            android:listSelector=\"#00000000\"\n            android:numColumns=\"3\"\n            android:paddingBottom=\"5.0sp\"\n            android:paddingTop=\"5.0sp\"\n            android:verticalSpacing=\"5.0dip\" />\n    </LinearLayout>\n\n    <TextView\n        android:id=\"@+id/tip_city\"\n        style=\"@style/Label.Shadow.Medium\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_marginBottom=\"14.0dip\"\n        android:visibility=\"gone\"\n        android:text=\"长按可进入编辑模式\" />\n\n</RelativeLayout>"
  },
  {
    "path": "WayHoo/res/layout/city_manger_grid_item_add.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:weather=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"wrap_content\" >\n\n    <LinearLayout\n        android:id=\"@+id/city_add_btn\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"@dimen/city_manager_tiny_height\"\n        android:layout_marginBottom=\"5.0dip\"\n        android:layout_marginLeft=\"5.0dip\"\n        android:layout_marginRight=\"5.0dip\"\n        android:layout_marginTop=\"12.0dip\"\n        android:background=\"@drawable/item_prs\"\n        android:gravity=\"center\"\n        android:orientation=\"vertical\" >\n\n        <ImageView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:layout_marginBottom=\"0.0dip\"\n            android:layout_marginTop=\"0.0dip\"\n            android:src=\"@drawable/plus_icon\" />\n    </LinearLayout>\n\n</FrameLayout>"
  },
  {
    "path": "WayHoo/res/layout/city_manger_grid_item_normal.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:weather=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\" >\n\n    <RelativeLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"@dimen/city_manager_tiny_height\"\n        android:layout_marginBottom=\"5.0dip\"\n        android:layout_marginLeft=\"5.0dip\"\n        android:layout_marginRight=\"5.0dip\"\n        android:layout_marginTop=\"12.0dip\"\n        android:background=\"@drawable/item_prs\"\n        android:orientation=\"vertical\" >\n\n        <TextView\n            android:id=\"@+id/city_manager_name_tv\"\n            style=\"@style/Label.Shadow\"\n            android:layout_height=\"25.0dip\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginTop=\"6.0dip\"\n            android:drawableLeft=\"@drawable/current_loc_active_26x26\"\n            android:gravity=\"center\"\n            android:singleLine=\"true\"\n            android:text=\"深圳\"\n            android:textColor=\"@android:color/white\" />\n\n        <ImageView\n            android:id=\"@+id/city_manager_icon_iv\"\n            android:layout_width=\"56.0dip\"\n            android:layout_height=\"56.0dip\"\n            android:layout_centerInParent=\"true\"\n            android:layout_marginBottom=\"8.0dip\"\n            android:layout_marginTop=\"12.0dip\"\n            android:src=\"@drawable/xy_weather_ic_default\"\n            android:contentDescription=\"weather_default_weather_desc\" />\n\n        <ProgressBar\n            android:id=\"@+id/city_manager_progressbar\"\n            android:layout_width=\"56.0dip\"\n            android:layout_height=\"56.0dip\"\n            android:layout_centerInParent=\"true\"\n            android:layout_marginBottom=\"8.0dip\"\n            android:layout_marginTop=\"12.0dip\"\n            android:visibility=\"gone\" />\n\n        <com.way.ui.view.WeatherTypefacedTextView\n            android:id=\"@+id/city_manager_temp_tv\"\n            style=\"@style/Label\"\n            android:layout_alignParentBottom=\"true\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginBottom=\"10.0dip\"\n            android:text=\"23~33°\"\n            android:textColor=\"@android:color/white\"\n            weather:typeface=\"fonts/Roboto-Light.ttf\" />\n    </RelativeLayout>\n\n    <ImageView\n        android:id=\"@+id/city_delete_btn\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_alignParentTop=\"true\"\n        android:layout_marginBottom=\"0.0dip\"\n        android:layout_marginTop=\"0.0dip\"\n        android:src=\"@drawable/city_edit_prs\"\n        android:visibility=\"gone\" />\n\n</RelativeLayout>"
  },
  {
    "path": "WayHoo/res/layout/city_query_actionbar.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:weather=\"http://schemas.android.com/apk/res-auto\" >\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/abs__action_bar_default_height\"\n        android:background=\"@android:color/transparent\" >\n\n       <ImageView\n            android:id=\"@+id/back_image\"\n            android:layout_width=\"48dip\"\n            android:layout_height=\"48dip\"\n            android:layout_alignParentLeft=\"true\"\n            android:layout_centerInParent=\"true\"\n            android:layout_marginLeft=\"8dip\"\n            android:clickable=\"true\"\n            android:focusable=\"true\"\n            android:minHeight=\"@dimen/topbar_icon_size\"\n            android:minWidth=\"@dimen/topbar_icon_size\"\n            android:padding=\"12dip\"\n            android:src=\"@drawable/setting_top_back\" />\n\n        <TextView\n            android:id=\"@+id/city_title\"\n            android:layout_height=\"match_parent\"\n            android:layout_centerInParent=\"true\"\n            android:gravity=\"center\"\n            style=\"@style/Label.Shadow\"\n            android:text=\"选择城市\"\n            android:textSize=\"20.0sp\" />\n\n    </RelativeLayout>\n\n</merge>"
  },
  {
    "path": "WayHoo/res/layout/city_query_hotcity_grid_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\" >\n\n    <TextView\n        android:id=\"@+id/grid_city_name\"\n        style=\"@style/Label.Shadow\"\n        android:layout_centerInParent=\"true\"\n        android:ellipsize=\"end\"\n        android:gravity=\"center\"\n        android:paddingBottom=\"1dip\"\n        android:paddingTop=\"1dip\"\n        android:text=\"深圳\"\n        android:singleLine=\"true\" />\n\n    <ImageView\n        android:id=\"@+id/grid_city_selected_iv\"\n        style=\"@style/Label.Shadow\"\n        android:layout_toRightOf=\"@id/grid_city_name\"\n        android:layout_centerVertical=\"true\"\n        android:src=\"@drawable/ic_addcity_selected\" />\n\n</RelativeLayout>"
  },
  {
    "path": "WayHoo/res/layout/city_query_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/city_add_bg\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:layout_gravity=\"top|center\"\n    android:background=\"@drawable/bg_addcity\"\n    android:gravity=\"center\" >\n\n    <include\n        android:id=\"@id/top_bar\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        layout=\"@layout/city_query_actionbar\" />\n\n    <LinearLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\"\n        android:layout_below=\"@id/top_bar\"\n        android:layout_marginTop=\"@dimen/abs__action_bar_default_height\"\n        android:orientation=\"vertical\" >\n\n        <RelativeLayout\n            android:id=\"@+id/queryCitySearch\"\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginBottom=\"1.0dip\"\n            android:layout_marginLeft=\"10.0dip\"\n            android:layout_marginRight=\"10.0dip\"\n            android:layout_marginTop=\"1.0dip\"\n            android:addStatesFromChildren=\"true\" >\n\n            <RelativeLayout\n                android:id=\"@+id/queryCitySearchlayout\"\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"38.0dip\" >\n\n                <EditText\n                    android:id=\"@+id/queryCityText\"\n                    android:layout_width=\"fill_parent\"\n                    android:layout_height=\"38.0dip\"\n                    android:layout_alignParentLeft=\"true\"\n                    android:layout_alignParentTop=\"true\"\n                    android:layout_centerVertical=\"true\"\n                    android:background=\"@drawable/bg_search1\"\n                    android:completionThreshold=\"1\"\n                    android:dropDownHorizontalOffset=\"34.0dip\"\n                    android:dropDownVerticalOffset=\"0.0dip\"\n                    android:dropDownWidth=\"240.0dip\"\n                    android:gravity=\"left|center\"\n                    android:hint=\"@string/enter_city_name\"\n                    android:paddingRight=\"35.0dip\"\n                    android:singleLine=\"true\"\n                    android:textColor=\"@android:color/white\"\n                    android:textColorHint=\"#ffaeb8c2\"\n                    android:textSize=\"15.0sp\" />\n\n                <ImageButton\n                    android:id=\"@+id/queryCityExit\"\n                    android:layout_width=\"18.0dip\"\n                    android:layout_height=\"18.0dip\"\n                    android:layout_alignParentRight=\"true\"\n                    android:layout_centerVertical=\"true\"\n                    android:layout_marginBottom=\"2.0dip\"\n                    android:layout_marginRight=\"8.0dip\"\n                    android:background=\"@drawable/city_query_delete\"\n                    android:gravity=\"center_vertical\"\n                    android:visibility=\"invisible\" \n                    />\n                    android:layout_marginLeft=\"-30.0dip\"\n                    android:layout_toRightOf=\"@id/queryCityText\"\n                    android:layout_alignLeft=\"@id/queryCityText\"\n            </RelativeLayout>\n\n            <TextView\n                android:id=\"@+id/location\"\n                style=\"@style/Label.Shadow.Medium\"\n                android:layout_width=\"80.0dip\"\n                android:layout_height=\"20.0dip\"\n                android:layout_below=\"@id/queryCitySearchlayout\"\n                android:layout_marginLeft=\"8.0dip\"\n                android:layout_marginTop=\"16.0dip\"\n                android:drawableLeft=\"@drawable/current_loc_active_26x26\"\n                android:gravity=\"bottom\"\n                android:text=\"当前位置:\"\n                android:textSize=\"14.0sp\" />\n\n            <TextView\n                android:id=\"@+id/location_text\"\n                style=\"@style/Label\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_below=\"@id/queryCitySearchlayout\"\n                android:layout_marginLeft=\"8.0dip\"\n                android:layout_marginRight=\"8.0dip\"\n                android:layout_marginTop=\"14.0dip\"\n                android:layout_toRightOf=\"@id/location\"\n                android:background=\"@drawable/item_background\"\n                android:clickable=\"true\"\n                android:focusable=\"true\"\n                android:gravity=\"center_vertical\"\n                android:paddingLeft=\"8.0dip\"\n                android:paddingRight=\"8.0dip\"\n                android:singleLine=\"true\"\n                android:text=\"点击开始定位\"\n                android:textColor=\"@android:color/white\" />\n        </RelativeLayout>\n\n        <ListView\n            android:id=\"@+id/cityList\"\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"fill_parent\"\n            android:layout_marginLeft=\"14.0dip\"\n            android:layout_marginRight=\"14.0dip\"\n            android:layout_marginTop=\"0.0dip\"\n            android:cacheColorHint=\"@null\"\n            android:divider=\"@drawable/dotted_line_624\"\n            android:dividerHeight=\"1.0dip\"\n            android:footerDividersEnabled=\"true\"\n            android:listSelector=\"@drawable/listview_background\"\n            android:visibility=\"gone\" />\n\n        <TextView\n            android:id=\"@+id/noCityText\"\n            style=\"@style/Label.Shadow\"\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"fill_parent\"\n            android:layout_marginLeft=\"12.0dip\"\n            android:layout_marginRight=\"10.0dip\"\n            android:gravity=\"top|center\"\n            android:singleLine=\"true\"\n            android:text=\"未找到该城市\"\n            android:textColor=\"#ffffffff\"\n            android:visibility=\"gone\" />\n\n        <TextView\n            android:id=\"@+id/hotCityText\"\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"8.0dip\"\n            android:gravity=\"left|center\"\n            android:paddingLeft=\"14.0dip\"\n            android:paddingRight=\"10.0dip\"\n            android:paddingTop=\"18.0dip\"\n            android:text=\"热门城市\"\n            android:textColor=\"#ffffffff\"\n            android:textSize=\"14.0sp\" />\n\n        <View\n            android:id=\"@+id/queryCityDivider\"\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"1.0dip\"\n            android:layout_marginBottom=\"10.0dip\"\n            android:layout_marginLeft=\"10.0dip\"\n            android:layout_marginRight=\"10.0dip\"\n            android:layout_marginTop=\"6.0dip\"\n            android:background=\"@drawable/line_parting\" />\n\n        <GridView\n            android:id=\"@+id/hotCityGrid\"\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"fill_parent\"\n            android:layout_gravity=\"right\"\n            android:layout_marginLeft=\"1.0dip\"\n            android:layout_marginRight=\"1.0dip\"\n            android:drawSelectorOnTop=\"false\"\n            android:fadingEdge=\"none\"\n            android:gravity=\"center\"\n            android:horizontalSpacing=\"-10.0dip\"\n            android:listSelector=\"@drawable/item_background\"\n            android:numColumns=\"4\"\n            android:paddingBottom=\"5.0dip\"\n            android:paddingLeft=\"0.0dip\"\n            android:paddingRight=\"0.0dip\"\n            android:paddingTop=\"2.0dip\"\n            android:scrollbarAlwaysDrawVerticalTrack=\"true\"\n            android:scrollbarStyle=\"insideInset\"\n            android:verticalSpacing=\"20.0dip\" />\n    </LinearLayout>\n\n</RelativeLayout>"
  },
  {
    "path": "WayHoo/res/layout/city_query_list_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"44.0dip\"\n    android:orientation=\"horizontal\"\n    android:paddingLeft=\"30.0sp\" >\n\n    <TextView\n        android:id=\"@+id/citylst_name\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"40.0dip\"\n        android:layout_gravity=\"center_vertical\"\n        android:layout_marginBottom=\"4.0dip\"\n        android:layout_marginTop=\"4.0dip\"\n        android:gravity=\"center_vertical\"\n        android:paddingLeft=\"0.0sp\"\n        android:paddingRight=\"0.0sp\"\n        android:text=\"城市\"\n        android:textColor=\"@android:color/white\"\n        android:textSize=\"18.0sp\" />\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"40.0dip\"\n        android:layout_gravity=\"center_vertical\"\n        android:gravity=\"center_vertical\"\n        android:paddingLeft=\"4.0sp\"\n        android:paddingRight=\"4.0sp\"\n        android:text=\"-\"\n        android:textColor=\"#ffaeb8c2\"\n        android:textSize=\"18.0sp\" />\n\n    <TextView\n        android:id=\"@+id/citylst_city\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"40.0dip\"\n        android:layout_gravity=\"center_vertical\"\n        android:layout_marginBottom=\"4.0dip\"\n        android:layout_marginTop=\"4.0dip\"\n        android:gravity=\"center_vertical\"\n        android:paddingLeft=\"4.0sp\"\n        android:paddingRight=\"4.0sp\"\n        android:singleLine=\"true\"\n        android:text=\"市\"\n        android:textColor=\"#ffaeb8c2\"\n        android:textSize=\"16.0sp\" />\n\n    <TextView\n        android:id=\"@+id/citylst_separator\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"40.0dip\"\n        android:layout_gravity=\"center_vertical\"\n        android:gravity=\"center_vertical\"\n        android:paddingLeft=\"4.0sp\"\n        android:paddingRight=\"4.0sp\"\n        android:text=\"-\"\n        android:textColor=\"#ffaeb8c2\"\n        android:textSize=\"18.0sp\" />\n\n    <TextView\n        android:id=\"@+id/citylst_province\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"40.0dip\"\n        android:layout_gravity=\"center_vertical\"\n        android:layout_marginBottom=\"4.0dip\"\n        android:layout_marginTop=\"4.0dip\"\n        android:gravity=\"center_vertical\"\n        android:paddingLeft=\"4.0sp\"\n        android:paddingRight=\"4.0sp\"\n        android:singleLine=\"true\"\n        android:text=\"省\"\n        android:textColor=\"#ffaeb8c2\"\n        android:textSize=\"16.0sp\" />\n\n</LinearLayout>"
  },
  {
    "path": "WayHoo/res/layout/comm_lay_emptyview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:gravity=\"center\"\n    android:orientation=\"vertical\" >\n\n    <TextView\n        android:id=\"@+id/txtLoadEmpty\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"数据为空\"\n        android:textSize=\"16sp\" />\n\n    <TextView\n        android:id=\"@+id/layoutReload\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"16dip\"\n        android:gravity=\"center\"\n        android:paddingBottom=\"3.0dip\"\n        android:paddingLeft=\"10.0dip\"\n        android:paddingRight=\"10.0dip\"\n        android:paddingTop=\"3.0dip\"\n        android:text=\"刷新看看\"\n        android:textSize=\"18sp\"\n        android:textStyle=\"italic\" />\n\n</LinearLayout>"
  },
  {
    "path": "WayHoo/res/layout/comm_lay_loadfailed.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:gravity=\"center\"\n    android:orientation=\"vertical\" >\n\n    <TextView\n        android:id=\"@+id/txtLoadFailed\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:textSize=\"16sp\" />\n\n    <TextView\n        android:id=\"@+id/layoutReload\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"16dip\"\n        android:gravity=\"center\"\n        android:paddingBottom=\"3.0dip\"\n        android:paddingLeft=\"10.0dip\"\n        android:paddingRight=\"10.0dip\"\n        android:paddingTop=\"3.0dip\"\n        android:text=\"刷新看看\"\n        android:textSize=\"18sp\"\n        android:textStyle=\"italic\" />\n\n</LinearLayout>"
  },
  {
    "path": "WayHoo/res/layout/comm_lay_loading.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:gravity=\"center\"\n    android:orientation=\"vertical\" >\n\n    <ProgressBar\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\" />\n\n</LinearLayout>"
  },
  {
    "path": "WayHoo/res/layout/count_down_to_location.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.way.ui.view.CountDownView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/count_down_to_locate\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"#b0000000\"\n    android:visibility=\"invisible\" >\n\n    <TextView\n        android:id=\"@+id/remaining_seconds\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:gravity=\"center\"\n        android:textColor=\"@android:color/white\"\n        android:textSize=\"60sp\" />\n\n    <TextView\n        android:id=\"@+id/count_down_title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:paddingLeft=\"80dp\"\n        android:paddingTop=\"120dp\"\n        android:text=\"正在努力搜寻您的位置...\"\n        android:textColor=\"@android:color/white\"\n        android:textSize=\"20sp\" />\n\n    <Button\n        android:id=\"@+id/cancel_locate_city_btn\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"50.0dip\"\n        android:layout_gravity=\"bottom\"\n        android:layout_marginBottom=\"40.0dip\"\n        android:layout_marginLeft=\"15.0dip\"\n        android:layout_marginRight=\"15.0dip\"\n        android:background=\"@drawable/cityselector_locate_btn_bg\"\n        android:text=\"取  消\"\n        android:textColor=\"@android:color/white\"\n        android:textSize=\"20.0sp\" />\n\n</com.way.ui.view.CountDownView>"
  },
  {
    "path": "WayHoo/res/layout/custom_progress_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:gravity=\"center\" >\n\n    <RelativeLayout\n        android:id=\"@+id/dialogRoot\"\n        android:layout_width=\"200.0dip\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:background=\"@drawable/shape_bg\" >\n\n        <ProgressBar\n            android:id=\"@+id/dialogMainIcon\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginBottom=\"8.0dip\"\n            android:layout_marginTop=\"14.0dip\" />\n\n        <TextView\n            android:id=\"@+id/dialogText\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@id/dialogMainIcon\"\n            android:layout_centerHorizontal=\"true\"\n            android:paddingBottom=\"16.0dip\"\n            android:paddingLeft=\"14.0dip\"\n            android:paddingRight=\"14.0dip\"\n            android:textColor=\"@android:color/white\"\n            android:textSize=\"16.0sp\" />\n    </RelativeLayout>\n\n</FrameLayout>"
  },
  {
    "path": "WayHoo/res/layout/feed_back_view.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    xmlns:weather=\"http://schemas.android.com/apk/res-auto\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"match_parent\"\r\n    android:background=\"@drawable/bg_addcity\"\r\n    android:orientation=\"vertical\" >\r\n\r\n    <include\r\n        android:id=\"@id/top_bar\"\r\n        android:layout_width=\"fill_parent\"\r\n        android:layout_height=\"wrap_content\"\r\n        layout=\"@layout/city_query_actionbar\" />\r\n\r\n    <ScrollView\r\n        android:id=\"@+id/scrollview\"\r\n        android:layout_width=\"fill_parent\"\r\n        android:layout_height=\"0dp\"\r\n        android:layout_margin=\"10.0dip\"\r\n        android:layout_marginTop=\"@dimen/abs__action_bar_default_height\"\r\n        android:layout_weight=\"1\"\r\n        android:background=\"@drawable/settings_bg_city_item_up\"\r\n        android:fillViewport=\"true\" >\r\n\r\n        <EditText\r\n            android:id=\"@+id/fee_back_edit\"\r\n            android:layout_width=\"match_parent\"\r\n            android:layout_height=\"200dip\"\r\n            android:background=\"@null\"\r\n            android:gravity=\"left|top\"\r\n            android:hint=\"亲，给点建议吧！么么哒！\"\r\n            android:inputType=\"textMultiLine\"\r\n            android:paddingBottom=\"4.0dip\"\r\n            android:paddingLeft=\"4.0dip\"\r\n            android:paddingRight=\"4.0dip\"\r\n            android:paddingTop=\"4.0dip\"\r\n            android:textSize=\"16.0sp\" />\r\n    </ScrollView>\r\n\r\n    <Button\r\n        android:id=\"@+id/feed_back_btn\"\r\n        android:layout_width=\"fill_parent\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:layout_gravity=\"center\"\r\n        android:layout_marginBottom=\"10.0dip\"\r\n        android:layout_marginLeft=\"10.0dip\"\r\n        android:layout_marginRight=\"10.0dip\"\r\n        android:background=\"@drawable/cityselector_locate_btn_bg\"\r\n        android:text=\"提  交\" />\r\n\r\n</LinearLayout>"
  },
  {
    "path": "WayHoo/res/layout/loading_empty_container.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.way.ui.view.LoadingEmptyContainer xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/loading_empty_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:layout_centerInParent=\"true\"\n    android:layout_gravity=\"center\"\n    android:gravity=\"center\" >\n\n    <ProgressBar\n        android:id=\"@+id/progressbar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\" />\n\n    <include layout=\"@layout/no_results_message\" />\n\n</com.way.ui.view.LoadingEmptyContainer>"
  },
  {
    "path": "WayHoo/res/layout/no_results_message.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.way.ui.view.NoResultsContainer xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/no_results_container\"\n    android:layout_width=\"260dip\"\n    android:layout_height=\"wrap_content\"\n    android:layout_centerInParent=\"true\"\n    android:layout_gravity=\"center\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:visibility=\"gone\" >\n\n    <TextView\n        android:id=\"@+id/no_results_main_text\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal\"\n        android:gravity=\"center_horizontal\"\n        android:maxLines=\"2\"\n        android:text=\"\"\n        android:textAllCaps=\"true\"\n        android:textColor=\"@android:color/white\"\n        android:textSize=\"20sp\"\n        android:textStyle=\"bold\" />\n\n</com.way.ui.view.NoResultsContainer>"
  },
  {
    "path": "WayHoo/res/layout/notification_weather_multi.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"64.0dip\"\n    android:paddingLeft=\"2.0dip\"\n    android:paddingRight=\"8.0dip\" >\n\n    <RelativeLayout\n        android:id=\"@+id/first_line_layout\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerVertical=\"true\" >\n\n        <LinearLayout\n            android:id=\"@+id/weather_notification_left_layout\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:orientation=\"vertical\" >\n\n            <ImageView\n                android:id=\"@+id/notification_weather_image\"\n                android:layout_width=\"60.0dip\"\n                android:layout_height=\"60.0dip\"\n                android:layout_marginRight=\"2.0dip\"\n                android:src=\"@drawable/ic_default_big\" />\n        </LinearLayout>\n\n        <ImageView\n            android:id=\"@+id/divider\"\n            android:layout_width=\"1.0px\"\n            android:layout_height=\"fill_parent\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginBottom=\"4.0dip\"\n            android:layout_marginRight=\"9.0dip\"\n            android:layout_marginTop=\"8.0dip\"\n            android:layout_toRightOf=\"@id/weather_notification_left_layout\"\n            android:src=\"#33ffffff\" />\n\n        <RelativeLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_toRightOf=\"@id/divider\" >\n\n            <ImageView\n                android:id=\"@+id/notification_weather_temp\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\" />\n\n            <TextView\n                android:id=\"@+id/notification_weather_desc\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"6.0dip\"\n                android:layout_marginTop=\"7.0dip\"\n                android:layout_toRightOf=\"@id/notification_weather_temp\"\n                android:gravity=\"center_horizontal\"\n                android:singleLine=\"true\"\n                android:text=\"晴\"\n                android:textColor=\"#e5ffffff\"\n                android:textSize=\"14.0dip\" />\n\n            <TextView\n                android:id=\"@+id/notification_weather_low_high\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_below=\"@id/notification_weather_desc\"\n                android:layout_marginLeft=\"6.0dip\"\n                android:layout_marginTop=\"2.0dip\"\n                android:layout_toRightOf=\"@id/notification_weather_temp\"\n                android:singleLine=\"true\"\n                android:text=\"0°\"\n                android:textColor=\"#e5ffffff\"\n                android:textSize=\"10.0dip\" />\n\n            <TextView\n                android:id=\"@+id/notification_weather_time\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_below=\"@id/notification_weather_temp\"\n                android:layout_marginTop=\"2.0dip\"\n                android:text=\"\"\n                android:textColor=\"#7fffffff\"\n                android:textSize=\"10.0dip\" />\n\n            <ImageView\n                android:layout_width=\"10.0dip\"\n                android:layout_height=\"10.0dip\"\n                android:layout_alignBottom=\"@id/notification_weather_time\"\n                android:layout_marginLeft=\"2.0dip\"\n                android:layout_toRightOf=\"@id/notification_weather_time\"\n                android:src=\"@drawable/notify_log_view\" />\n\n            <RelativeLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\" >\n\n                <RelativeLayout\n                    android:id=\"@+id/notification_city_layout\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\" >\n\n                    <TextView\n                        android:id=\"@+id/notification_weather_city\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_alignParentRight=\"true\"\n                        android:layout_marginTop=\"4.0dip\"\n                        android:maxLength=\"6\"\n                        android:singleLine=\"true\"\n                        android:text=\"深圳\"\n                        android:textColor=\"#e5ffffff\"\n                        android:textSize=\"16.0dip\" />\n\n                    <ImageView\n                        android:id=\"@+id/notifiy_location_icon_view\"\n                        android:layout_width=\"10.0dip\"\n                        android:layout_height=\"10.0dip\"\n                        android:layout_marginRight=\"4.0dip\"\n                        android:layout_marginTop=\"10.0dip\"\n                        android:layout_toLeftOf=\"@id/notification_weather_city\"\n                        android:src=\"@drawable/current_loc_active_26x26\"\n                        android:visibility=\"invisible\" />\n                </RelativeLayout>\n\n                <RelativeLayout\n                    android:id=\"@+id/notification_aqi_bg_layout\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"16.0dip\"\n                    android:layout_alignParentRight=\"true\"\n                    android:layout_below=\"@id/notification_city_layout\"\n                    android:layout_marginTop=\"6.0dip\"\n                    android:background=\"@drawable/bg_pm25_01\"\n                    android:minWidth=\"44.0dip\" >\n\n                    <TextView\n                        android:id=\"@+id/notification_weather_pm\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"16.0dip\"\n                        android:layout_marginLeft=\"2.0dip\"\n                        android:singleLine=\"true\"\n                        android:text=\"60\"\n                        android:textColor=\"@android:color/white\"\n                        android:textSize=\"10.0dip\" />\n\n                    <ImageView\n                        android:id=\"@+id/notification_weather_aqi_divider\"\n                        android:layout_width=\"1.0px\"\n                        android:layout_height=\"12.0dip\"\n                        android:layout_centerVertical=\"true\"\n                        android:layout_marginLeft=\"2.0dip\"\n                        android:layout_marginRight=\"2.0dip\"\n                        android:layout_toRightOf=\"@id/notification_weather_pm\"\n                        android:background=\"#4cffffff\" />\n\n                    <TextView\n                        android:id=\"@+id/notification_weather_pm_desc\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"16.0dip\"\n                        android:layout_toRightOf=\"@id/notification_weather_aqi_divider\"\n                        android:minWidth=\"14.0dip\"\n                        android:singleLine=\"true\"\n                        android:text=\"优\"\n                        android:textColor=\"@android:color/white\"\n                        android:textSize=\"10.0dip\" />\n                </RelativeLayout>\n            </RelativeLayout>\n        </RelativeLayout>\n    </RelativeLayout>\n\n    <LinearLayout\n        android:id=\"@+id/second_line_layout\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/first_line_layout\"\n        android:layout_marginTop=\"5.0dip\"\n        android:gravity=\"center_vertical\"\n        android:orientation=\"horizontal\"\n        android:visibility=\"gone\" >\n\n        <RelativeLayout\n            android:id=\"@+id/one_layout\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1.0\"\n            android:orientation=\"horizontal\" >\n\n            <LinearLayout\n                android:id=\"@+id/one_layout_weather\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1.0\"\n                android:gravity=\"center_horizontal\"\n                android:orientation=\"vertical\" >\n\n                <ImageView\n                    android:id=\"@+id/one_weather_image\"\n                    android:layout_width=\"24.0dip\"\n                    android:layout_height=\"24.0dip\"\n                    android:src=\"@drawable/ic_default_big\" />\n\n                <TextView\n                    android:id=\"@+id/one_weather_desc\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginTop=\"4.0dip\"\n                    android:gravity=\"center\"\n                    android:singleLine=\"true\"\n                    android:text=\"晴\"\n                    android:textColor=\"#ffffffff\"\n                    android:textSize=\"10.0dip\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"5.0dip\"\n                android:layout_toRightOf=\"@id/one_layout_weather\"\n                android:layout_weight=\"1.0\"\n                android:gravity=\"center_horizontal\"\n                android:orientation=\"vertical\" >\n\n                <TextView\n                    android:id=\"@+id/one_date\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:gravity=\"center\"\n                    android:singleLine=\"true\"\n                    android:text=\"晴\"\n                    android:textColor=\"#7fffffff\"\n                    android:textSize=\"14.0dip\" />\n\n                <TextView\n                    android:id=\"@+id/one_weather_temp\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginTop=\"6.0dip\"\n                    android:gravity=\"center\"\n                    android:singleLine=\"true\"\n                    android:text=\"晴\"\n                    android:textColor=\"#ffffffff\"\n                    android:textSize=\"14.0dip\" />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <RelativeLayout\n            android:id=\"@+id/two_layout\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1.0\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\" >\n\n            <LinearLayout\n                android:id=\"@+id/two_layout_weather\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1.0\"\n                android:gravity=\"center_horizontal\"\n                android:orientation=\"vertical\" >\n\n                <ImageView\n                    android:id=\"@+id/two_weather_image\"\n                    android:layout_width=\"24.0dip\"\n                    android:layout_height=\"24.0dip\"\n                    android:src=\"@drawable/ic_default_big\" />\n\n                <TextView\n                    android:id=\"@+id/two_weather_desc\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginTop=\"4.0dip\"\n                    android:gravity=\"center\"\n                    android:singleLine=\"true\"\n                    android:text=\"晴\"\n                    android:textColor=\"#ffffffff\"\n                    android:textSize=\"10.0dip\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"5.0dip\"\n                android:layout_toRightOf=\"@id/two_layout_weather\"\n                android:layout_weight=\"1.0\"\n                android:gravity=\"center_horizontal\"\n                android:orientation=\"vertical\" >\n\n                <TextView\n                    android:id=\"@+id/two_date\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:gravity=\"center\"\n                    android:singleLine=\"true\"\n                    android:text=\"晴\"\n                    android:textColor=\"#7fffffff\"\n                    android:textSize=\"14.0dip\" />\n\n                <TextView\n                    android:id=\"@+id/two_weather_temp\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginTop=\"6.0dip\"\n                    android:gravity=\"center\"\n                    android:singleLine=\"true\"\n                    android:text=\"晴\"\n                    android:textColor=\"#ffffffff\"\n                    android:textSize=\"14.0dip\" />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <RelativeLayout\n            android:id=\"@+id/three_layout\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1.0\"\n            android:gravity=\"right\" >\n\n            <LinearLayout\n                android:id=\"@+id/three_layout_weather\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"center_horizontal\"\n                android:orientation=\"vertical\" >\n\n                <ImageView\n                    android:id=\"@+id/three_weather_image\"\n                    android:layout_width=\"24.0dip\"\n                    android:layout_height=\"24.0dip\"\n                    android:src=\"@drawable/ic_default_big\" />\n\n                <TextView\n                    android:id=\"@+id/three_weather_desc\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginTop=\"4.0dip\"\n                    android:gravity=\"center\"\n                    android:singleLine=\"true\"\n                    android:text=\"晴\"\n                    android:textColor=\"#ffffffff\"\n                    android:textSize=\"10.0dip\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"5.0dip\"\n                android:layout_toRightOf=\"@id/three_layout_weather\"\n                android:gravity=\"center_horizontal\"\n                android:orientation=\"vertical\" >\n\n                <TextView\n                    android:id=\"@+id/three_date\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:gravity=\"center\"\n                    android:singleLine=\"true\"\n                    android:text=\"晴\"\n                    android:textColor=\"#7fffffff\"\n                    android:textSize=\"14.0dip\" />\n\n                <TextView\n                    android:id=\"@+id/three_weather_temp\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_marginTop=\"6.0dip\"\n                    android:gravity=\"center\"\n                    android:singleLine=\"true\"\n                    android:text=\"晴\"\n                    android:textColor=\"#ffffffff\"\n                    android:textSize=\"14.0dip\" />\n            </LinearLayout>\n        </RelativeLayout>\n    </LinearLayout>\n\n</RelativeLayout>"
  },
  {
    "path": "WayHoo/res/layout/sidemenu_list_item_category.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<TextView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/MenuDrawer.Widget.Category\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\" />\n\n<!--     android:background=\"@drawable/sidemenu_category\" -->"
  },
  {
    "path": "WayHoo/res/layout/sidemenu_list_item_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<TextView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    style=\"@style/MenuDrawer.Widget.Title\"\n    android:layout_width=\"match_parent\"\nandroid:background=\"@drawable/sidebar_item_background_light_selector\" \n    android:layout_height=\"wrap_content\" />\n\n<!-- \n-->\n"
  },
  {
    "path": "WayHoo/res/layout/sidemenu_listview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ListView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/menu_list\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:cacheColorHint=\"@android:color/transparent\"\n    android:background=\"#c0000000\"\n    android:choiceMode=\"singleChoice\"\n    android:divider=\"@color/sidebar_item_separator_dark\" \n    android:dividerHeight=\"1.0dip\"\n    />\n<!--\n    android:divider=\"@drawable/dotted_line_624\"\n    android:background=\"@drawable/biz_pc_main_bg_transformers_left\"\n    android:background=\"@color/sidebar_footer_background_dark\"\n-->\n"
  },
  {
    "path": "WayHoo/res/layout/splash_activity_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:background=\"@drawable/start_bg\" >\n\n    <TextView\n        android:id=\"@+id/copy_right\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_marginBottom=\"16dip\"\n        android:padding=\"12dip\"\n        android:text=\"@string/all_rights_reserved\"\n        android:textColor=\"@android:color/white\"\n        android:textSize=\"12sp\" />\n\n    <RelativeLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\" >\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginTop=\"80.0dip\"\n            android:orientation=\"vertical\" >\n\n            <ImageView\n                android:layout_width=\"72.0dip\"\n                android:layout_height=\"72.0dip\"\n                android:layout_gravity=\"center_horizontal\"\n                android:src=\"@drawable/ic_launcher\" />\n\n            <TextView\n                android:id=\"@+id/app_name\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center_horizontal\"\n                android:layout_marginTop=\"5.0dip\"\n                android:text=\"@string/app_name\"\n                android:textColor=\"@android:color/white\"\n                android:textSize=\"16sp\" />\n        </LinearLayout>\n    </RelativeLayout>\n\n</RelativeLayout>"
  },
  {
    "path": "WayHoo/res/layout/swipeback_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.way.ui.swipeback.SwipeBackLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/swipe\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />\n    \n"
  },
  {
    "path": "WayHoo/res/layout/weather_aqi.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.way.ui.view.WeatherAqiView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:weather=\"http://schemas.android.com/apk/res-auto\"\n    style=\"@style/WeatherModule\" >\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/details_label\"\n        style=\"@style/Label\"\n        android:layout_marginTop=\"@dimen/negative_margin_md\"\n        android:text=\"空气质量\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n    <ImageView\n        android:id=\"@+id/divider\"\n        style=\"@style/Line.Solid\"\n        android:layout_below=\"@id/details_label\"\n        android:scaleType=\"fitXY\" />\n\n    <ImageView\n        android:id=\"@+id/aqi_icon\"\n        android:layout_width=\"@dimen/details_icon_size\"\n        android:layout_height=\"@dimen/details_icon_size\"\n        android:layout_below=\"@id/divider\"\n        android:layout_marginLeft=\"@dimen/margin_xs\"\n        android:layout_marginTop=\"25.0dip\"\n        android:src=\"@drawable/na\" />\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/aqi_level\"\n        style=\"@style/Label\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"60.0dip\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_below=\"@id/divider\"\n        android:layout_marginLeft=\"@dimen/margin_md\"\n        android:layout_marginRight=\"5.0dip\"\n        android:layout_toRightOf=\"@id/aqi_icon\"\n        android:gravity=\"center\"\n        android:paddingBottom=\"@dimen/padding_xs\"\n        android:paddingTop=\"@dimen/padding_xs\"\n        android:text=\"----\"\n        android:textSize=\"30sp\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n    <ImageView\n        android:id=\"@+id/dotline1\"\n        style=\"@style/Line.Dotted\"\n        android:layout_alignLeft=\"@id/aqi_level\"\n        android:layout_below=\"@id/aqi_level\" />\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/aqi\"\n        style=\"@style/Label\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"42dip\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_below=\"@id/dotline1\"\n        android:layout_marginLeft=\"5.0dip\"\n        android:gravity=\"center\"\n        android:paddingBottom=\"@dimen/padding_xs\"\n        android:paddingTop=\"@dimen/padding_xs\"\n        android:text=\"--\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/aqi_label\"\n        style=\"@style/Label.Medium\"\n        android:layout_height=\"42dip\"\n        android:layout_alignLeft=\"@id/aqi_level\"\n        android:layout_below=\"@id/dotline1\"\n        android:layout_marginRight=\"5.0dip\"\n        android:gravity=\"center\"\n        android:text=\"AQI\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n    <ImageView\n        android:id=\"@+id/dotline2\"\n        style=\"@style/Line.Dotted\"\n        android:layout_alignLeft=\"@id/aqi_label\"\n        android:layout_below=\"@id/aqi_label\" />\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/pm25\"\n        style=\"@style/Label\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"42dip\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_below=\"@id/dotline2\"\n        android:layout_marginLeft=\"5.0dip\"\n        android:gravity=\"center\"\n        android:paddingBottom=\"@dimen/padding_xs\"\n        android:paddingTop=\"@dimen/padding_xs\"\n        android:text=\"--\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/pm25_label\"\n        style=\"@style/Label.Medium\"\n        android:layout_height=\"42dip\"\n        android:layout_alignLeft=\"@id/aqi_level\"\n        android:layout_below=\"@id/dotline2\"\n        android:layout_marginRight=\"5.0dip\"\n        android:gravity=\"center\"\n        android:text=\"PM2.5\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n    <ImageView\n        android:id=\"@+id/dotline3\"\n        style=\"@style/Line.Dotted\"\n        android:layout_alignParentLeft=\"true\"\n        android:layout_below=\"@id/pm25_label\" />\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/aqi_desc\"\n        style=\"@style/Label.Medium\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/dotline3\"\n        android:layout_marginLeft=\"5.0dip\"\n        android:layout_marginRight=\"5.0dip\"\n        android:maxLines=\"2\"\n        android:singleLine=\"false\"\n         android:gravity=\"center_vertical\"\n        android:paddingBottom=\"@dimen/padding_xs\"\n        android:paddingTop=\"@dimen/padding_xs\"\n        android:text=\"--\"\n        android:textSize=\"15sp\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n    <ImageView\n        android:id=\"@+id/dotline4\"\n        style=\"@style/Line.Dotted\"\n        android:layout_alignParentLeft=\"true\"\n        android:layout_below=\"@id/aqi_desc\" />\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/weather_aqi_foot_tv\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"30dip\"\n        android:layout_alignParentLeft=\"true\"\n        android:layout_below=\"@id/dotline4\"\n        android:paddingTop=\"4dip\"\n        android:gravity=\"bottom|right\"\n        android:text=\"中国环境检测总站\"\n        android:textColor=\"@android:color/white\"\n        android:textSize=\"@dimen/text_md\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n</com.way.ui.view.WeatherAqiView>"
  },
  {
    "path": "WayHoo/res/layout/weather_current_condition.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    xmlns:weather=\"http://schemas.android.com/apk/res-auto\"\r\n    android:layout_width=\"fill_parent\"\r\n    android:layout_height=\"fill_parent\" >\r\n\r\n    <com.way.ui.view.WeatherTypefacedTextView\r\n        android:id=\"@+id/temperature\"\r\n        style=\"@style/Label.Shadow.ExtraLarge\"\r\n        android:layout_alignParentBottom=\"true\"\r\n        android:layout_alignParentLeft=\"true\"\r\n        android:layout_marginBottom=\"@dimen/negative_margin_sm\"\r\n        android:layout_marginRight=\"0.0dip\"\r\n        android:layout_marginTop=\"-30.0dip\"\r\n        android:text=\"--\"\r\n        weather:typeface=\"fonts/Roboto-Thin.ttf\" />\r\n\r\n    <ImageView\r\n        android:id=\"@+id/flickr_icon\"\r\n        android:layout_width=\"wrap_content\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:layout_alignParentBottom=\"true\"\r\n        android:layout_alignParentRight=\"true\"\r\n        android:layout_marginBottom=\"@dimen/margin_md\"\r\n        android:layout_marginLeft=\"2.0dip\"\r\n        android:layout_marginRight=\"3.0dip\"\r\n        android:contentDescription=\"@string/speak_launch_project_weather\"\r\n        android:paddingBottom=\"@dimen/flickr_icon_padding_bottom\"\r\n        android:visibility=\"invisible\"\r\n        android:src=\"@drawable/flickr\" />\r\n\r\n    <com.way.ui.view.WeatherTypefacedTextView\r\n        android:id=\"@+id/copyright\"\r\n        style=\"@style/Label.Shadow.Small\"\r\n        android:layout_alignBottom=\"@id/flickr_icon\"\r\n        android:layout_marginBottom=\"-2.0dip\"\r\n        android:layout_toLeftOf=\"@id/flickr_icon\"\r\n        android:ellipsize=\"middle\"\r\n        android:gravity=\"center\"\r\n        android:maxWidth=\"120.0dip\"\r\n        android:paddingBottom=\"0.0dip\"\r\n        android:text=\"未发布\"\r\n        weather:typeface=\"fonts/Roboto-Regular.ttf\" />\r\n        android:textColor=\"#ffdcdcdc\"\r\n\r\n    <ImageView\r\n        android:id=\"@+id/copyright_icon\"\r\n        android:layout_width=\"wrap_content\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:layout_alignBottom=\"@id/flickr_icon\"\r\n        android:layout_marginBottom=\"-1.0dip\"\r\n        android:layout_marginRight=\"1.0dip\"\r\n        android:layout_toLeftOf=\"@id/copyright\"\r\n        android:paddingBottom=\"@dimen/flickr_icon_padding_bottom\"\r\n        android:visibility=\"invisible\"\r\n        android:src=\"@drawable/copywrite\" />\r\n\r\n    <ImageView\r\n        android:id=\"@+id/temp_high_icon\"\r\n        android:layout_width=\"wrap_content\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:layout_above=\"@id/temperature\"\r\n        android:layout_alignParentLeft=\"true\"\r\n        android:layout_marginLeft=\"@dimen/temp_high_icon_margin_left\"\r\n        android:paddingBottom=\"@dimen/temp_high_icon_padding_bottom\"\r\n        android:src=\"@drawable/temp_high\" />\r\n\r\n    <ImageView\r\n        android:id=\"@+id/main_icon\"\r\n        android:layout_width=\"@dimen/current_condition_icon_size\"\r\n        android:layout_height=\"@dimen/current_condition_icon_size\"\r\n        android:layout_above=\"@id/temp_high_icon\"\r\n        android:layout_alignParentLeft=\"true\"\r\n        android:layout_marginBottom=\"@dimen/current_condition_icon_margin_bottom\"\r\n        android:layout_marginLeft=\"2.0dip\"\r\n        android:layout_marginRight=\"@dimen/current_condition_icon_right_margin\"\r\n        android:adjustViewBounds=\"true\"\r\n        android:scaleType=\"centerCrop\"\r\n        android:src=\"@drawable/ic_default_big\" />\r\n\r\n    <com.way.ui.view.WeatherTypefacedTextView\r\n        android:id=\"@+id/weather_description\"\r\n        style=\"@style/Label.Shadow\"\r\n        android:layout_alignBottom=\"@id/main_icon\"\r\n        android:layout_alignTop=\"@id/main_icon\"\r\n        android:layout_marginLeft=\"4.0dip\"\r\n        android:layout_toRightOf=\"@id/main_icon\"\r\n        android:gravity=\"center\"\r\n        android:paddingBottom=\"0.0dip\"\r\n        android:text=\"--\"\r\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\r\n\r\n    <com.way.ui.view.WeatherTypefacedTextView\r\n        android:id=\"@+id/temp_high\"\r\n        style=\"@style/Label.Shadow\"\r\n        android:layout_above=\"@id/temperature\"\r\n        android:layout_alignLeft=\"@id/weather_description\"\r\n        android:layout_marginBottom=\"@dimen/high_low_text_margin_bottom\"\r\n        android:layout_marginRight=\"30.0dip\"\r\n        android:layout_toRightOf=\"@id/temp_high_icon\"\r\n        android:text=\"--°\"\r\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\r\n\r\n    <ImageView\r\n        android:id=\"@+id/temp_low_icon\"\r\n        android:layout_width=\"wrap_content\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:layout_above=\"@id/temperature\"\r\n        android:layout_marginRight=\"@dimen/temp_icon_padding\"\r\n        android:layout_toRightOf=\"@id/temp_high\"\r\n        android:paddingBottom=\"@dimen/temp_high_icon_padding_bottom\"\r\n        android:src=\"@drawable/temp_low\" />\r\n\r\n    <com.way.ui.view.WeatherTypefacedTextView\r\n        android:id=\"@+id/temp_low\"\r\n        style=\"@style/Label.Shadow\"\r\n        android:layout_above=\"@id/temperature\"\r\n        android:layout_marginBottom=\"@dimen/high_low_text_margin_bottom\"\r\n        android:layout_toRightOf=\"@id/temp_low_icon\"\r\n        android:text=\"--°\"\r\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\r\n\r\n    <ImageView\r\n        android:id=\"@+id/degree_icon\"\r\n        android:layout_width=\"@dimen/degree_icon_size\"\r\n        android:layout_height=\"@dimen/degree_icon_size\"\r\n        android:layout_alignTop=\"@id/temperature\"\r\n        android:layout_marginBottom=\"10.0dip\"\r\n        android:layout_marginLeft=\"5.0dip\"\r\n        android:layout_marginRight=\"14.0dip\"\r\n        android:layout_marginTop=\"40.0dip\"\r\n        android:layout_toRightOf=\"@id/temperature\"\r\n        android:src=\"@drawable/big_temp_degree\" />\r\n\r\n</RelativeLayout>"
  },
  {
    "path": "WayHoo/res/layout/weather_details.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.way.ui.view.WeatherDetailsView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:weather=\"http://schemas.android.com/apk/res-auto\"\n    style=\"@style/WeatherModule\" >\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/details_label\"\n        style=\"@style/Label\"\n        android:layout_marginTop=\"@dimen/negative_margin_md\"\n        android:text=\"详细信息\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n    <ImageView\n        android:id=\"@+id/divider\"\n        style=\"@style/Line.Solid\"\n        android:layout_below=\"@id/details_label\"\n        android:scaleType=\"fitXY\" />\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/weather_name_tv\"\n        style=\"@style/Label\"\n        android:layout_height=\"42dip\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_below=\"@id/divider\"\n        android:gravity=\"center\"\n        android:text=\"--\"\n        android:paddingBottom=\"@dimen/padding_xs\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/feelslike_label\"\n        style=\"@style/Label.Medium\"\n        android:layout_height=\"42dip\"\n        android:layout_alignBottom=\"@id/weather_name_tv\"\n        android:layout_alignTop=\"@id/weather_name_tv\"\n        android:layout_below=\"@id/divider\"\n        android:layout_marginLeft=\"@dimen/margin_md\"\n        android:layout_marginRight=\"5.0dip\"\n        android:layout_toRightOf=\"@id/details_icon\"\n        android:gravity=\"center\"\n        android:text=\"天气\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n    <ImageView\n        android:id=\"@+id/dotline1\"\n        style=\"@style/Line.Dotted\"\n        android:layout_alignLeft=\"@id/feelslike_label\"\n        android:layout_below=\"@id/feelslike_label\" />\n\n    <ImageView\n        android:id=\"@+id/details_icon\"\n        android:layout_width=\"@dimen/details_icon_size\"\n        android:layout_height=\"@dimen/details_icon_size\"\n        android:layout_alignParentLeft=\"true\"\n        android:layout_alignTop=\"@id/dotline1\"\n        android:layout_below=\"@id/divider\"\n        android:layout_centerVertical=\"true\"\n        android:layout_marginLeft=\"@dimen/margin_xs\"\n        android:layout_marginTop=\"0.0dip\"\n        android:src=\"@drawable/ic_default_big\" />\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/feelsTemp_tv\"\n        style=\"@style/Label\"\n        android:layout_height=\"42dip\"\n        android:layout_width=\"wrap_content\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_below=\"@id/dotline1\"\n        android:layout_marginLeft=\"5.0dip\"\n        android:gravity=\"center\"\n         android:text=\"--°\"\n        android:paddingBottom=\"@dimen/padding_xs\"\n        android:paddingTop=\"@dimen/padding_xs\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/humidity_label\"\n        style=\"@style/Label.Medium\"\n        android:layout_height=\"42dip\"\n        android:layout_alignBottom=\"@id/feelsTemp_tv\"\n        android:layout_alignLeft=\"@id/feelslike_label\"\n        android:layout_alignTop=\"@id/feelsTemp_tv\"\n        android:layout_below=\"@id/dotline1\"\n        android:layout_marginRight=\"5.0dip\"\n        android:gravity=\"center\"\n        android:text=\"体感温度\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n    <ImageView\n        android:id=\"@+id/dotline2\"\n        style=\"@style/Line.Dotted\"\n        android:layout_alignLeft=\"@id/feelslike_label\"\n        android:layout_below=\"@id/feelsTemp_tv\" />\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/humidity_tv\"\n        style=\"@style/Label\"\n        android:layout_height=\"42dip\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_below=\"@id/dotline2\"\n        android:layout_marginLeft=\"5.0dip\"\n        android:gravity=\"center\"\n        android:text=\"--%\"\n        android:paddingBottom=\"@dimen/padding_xs\"\n        android:paddingTop=\"@dimen/padding_xs\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/visibility_label\"\n        style=\"@style/Label.Medium\"\n        android:layout_height=\"42dip\"\n        android:layout_alignBottom=\"@id/humidity_tv\"\n        android:layout_alignLeft=\"@id/feelslike_label\"\n        android:layout_alignTop=\"@id/humidity_tv\"\n        android:layout_below=\"@id/dotline2\"\n        android:layout_marginRight=\"5.0dip\"\n        android:gravity=\"center\"\n        android:text=\"湿度\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n    <ImageView\n        android:id=\"@+id/dotline3\"\n        style=\"@style/Line.Dotted\"\n        android:layout_alignLeft=\"@id/feelslike_label\"\n        android:layout_below=\"@id/humidity_tv\" />\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/wind_tv\"\n        style=\"@style/Label\"\n        android:layout_height=\"42dip\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_below=\"@id/dotline3\"\n        android:layout_marginLeft=\"5.0dip\"\n        android:gravity=\"center\"\n        android:paddingBottom=\"@dimen/padding_xs\"\n        android:paddingTop=\"@dimen/padding_xs\"\n        android:text=\"--\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/uv_label\"\n        style=\"@style/Label.Medium\"\n        android:layout_height=\"42dip\"\n        android:layout_alignBottom=\"@id/wind_tv\"\n        android:layout_alignLeft=\"@id/feelslike_label\"\n        android:layout_alignTop=\"@id/wind_tv\"\n        android:layout_below=\"@id/dotline3\"\n        android:layout_marginRight=\"5.0dip\"\n        android:gravity=\"center\"\n        android:text=\"风力\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n     <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/wind_desc\"\n        style=\"@style/Label.Medium\"\n        android:layout_height=\"42dip\"\n        android:layout_width=\"wrap_content\"\n        android:layout_alignBottom=\"@id/wind_tv\"\n        android:layout_alignTop=\"@id/wind_tv\"\n        android:layout_below=\"@id/dotline3\"\n        android:layout_marginRight=\"5.0dip\"\n        android:layout_toLeftOf=\"@id/wind_tv\"\n        android:gravity=\"center\"\n        android:text=\"--\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" /> \n\n    <ImageView\n        android:id=\"@+id/dotline4\"\n        style=\"@style/Line.Dotted\"\n        android:layout_alignParentLeft=\"true\"\n        android:layout_below=\"@id/wind_tv\" />\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/weather_details_foot_tv\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"30dip\"\n        android:layout_alignParentLeft=\"true\"\n        android:layout_below=\"@id/dotline4\"\n        android:paddingTop=\"4dip\"\n        android:gravity=\"bottom|right\"\n        android:text=\"中国天气网\"\n        android:textColor=\"@android:color/white\"\n        android:textSize=\"@dimen/text_md\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n</com.way.ui.view.WeatherDetailsView>"
  },
  {
    "path": "WayHoo/res/layout/weather_forecast.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.way.ui.view.WeatherForecastView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:weather=\"http://schemas.android.com/apk/res-auto\"\n    style=\"@style/WeatherModule\"\n    android:orientation=\"vertical\" >\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/forecast_label\"\n        style=\"@style/Label\"\n        android:layout_marginTop=\"@dimen/negative_margin_md\"\n        android:text=\"预报\"\n        weather:typeface=\"fonts/Roboto-Thin.ttf\" />\n\n    <ImageView\n        android:id=\"@+id/divider\"\n        style=\"@style/Line.Solid\"\n        android:layout_below=\"@id/forecast_label\" />\n\n    <include\n        android:id=\"@+id/day1\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/divider\"\n        android:layout_marginBottom=\"@dimen/margin_xxs\"\n        android:layout_marginTop=\"@dimen/margin_xxs\"\n        layout=\"@layout/weather_forecast_item\" />\n\n    <ImageView\n        android:id=\"@+id/divider3\"\n        style=\"@style/Line.Dotted\"\n        android:layout_width=\"fill_parent\"\n        android:layout_below=\"@id/day1\"\n        android:scaleType=\"fitXY\" />\n\n    <include\n        android:id=\"@+id/day2\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/divider3\"\n        android:layout_marginBottom=\"@dimen/margin_xxs\"\n        android:layout_marginTop=\"@dimen/margin_xxs\"\n        layout=\"@layout/weather_forecast_item\" />\n\n    <ImageView\n        android:id=\"@+id/divider4\"\n        style=\"@style/Line.Dotted\"\n        android:layout_width=\"fill_parent\"\n        android:layout_below=\"@id/day2\"\n        android:scaleType=\"fitXY\" />\n\n    <include\n        android:id=\"@+id/day3\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/divider4\"\n        android:layout_marginBottom=\"@dimen/margin_xxs\"\n        android:layout_marginTop=\"@dimen/margin_xxs\"\n        layout=\"@layout/weather_forecast_item\" />\n\n    <ImageView\n        android:id=\"@+id/divider5\"\n        style=\"@style/Line.Dotted\"\n        android:layout_width=\"fill_parent\"\n        android:layout_below=\"@id/day3\"\n        android:scaleType=\"fitXY\" />\n\n    <include\n        android:id=\"@+id/day4\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/divider5\"\n        android:layout_marginBottom=\"@dimen/margin_xxs\"\n        android:layout_marginTop=\"@dimen/margin_xxs\"\n        layout=\"@layout/weather_forecast_item\" />\n\n    <ImageView\n        android:id=\"@+id/divider6\"\n        style=\"@style/Line.Dotted\"\n        android:layout_width=\"fill_parent\"\n        android:layout_below=\"@id/day4\"\n        android:scaleType=\"fitXY\" />\n\n    <include\n        android:id=\"@+id/day5\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/divider6\"\n        android:layout_marginBottom=\"@dimen/margin_xxs\"\n        android:layout_marginTop=\"@dimen/margin_xxs\"\n        layout=\"@layout/weather_forecast_item\" />\n\n    <ImageView\n        android:id=\"@+id/divider7\"\n        style=\"@style/Line.Dotted\"\n        android:layout_width=\"fill_parent\"\n        android:layout_below=\"@id/day5\"\n        android:scaleType=\"fitXY\" />\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/forecast_foot\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"30dip\"\n        android:layout_below=\"@id/divider7\"\n        android:drawableRight=\"@drawable/wunderground_logo\"\n        android:gravity=\"bottom|right\"\n        android:paddingTop=\"4dip\"\n        android:textColor=\"@android:color/white\"\n        android:textSize=\"@dimen/text_md\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n</com.way.ui.view.WeatherForecastView>"
  },
  {
    "path": "WayHoo/res/layout/weather_forecast_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n    xmlns:weather=\"http://schemas.android.com/apk/res-auto\"\r\n    android:layout_width=\"fill_parent\"\r\n    android:layout_height=\"@dimen/daily_forecast_item_height\"\r\n    android:gravity=\"center_vertical\"\r\n    android:orientation=\"horizontal\"\r\n    android:padding=\"@dimen/padding_xs\" >\r\n\r\n    <com.way.ui.view.WeatherTypefacedTextView\r\n        android:id=\"@+id/forecast_week_tv\"\r\n        style=\"@style/Label.Shadow\"\r\n        android:layout_width=\"0dip\"\r\n        android:layout_height=\"match_parent\"\r\n        android:layout_weight=\"2\"\r\n        android:gravity=\"center_vertical\"\r\n        android:text=\"---\"\r\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\r\n\r\n    <ImageView\r\n        android:id=\"@+id/forecast_icon\"\r\n        android:layout_width=\"0dip\"\r\n        android:layout_height=\"32.0dip\"\r\n        android:layout_weight=\"1\"\r\n        android:src=\"@drawable/ic_default_big\" />\r\n\r\n    <com.way.ui.view.WeatherTypefacedTextView\r\n        android:id=\"@+id/forecast_low_temp_tv\"\r\n        style=\"@style/Label.Shadow\"\r\n        android:layout_width=\"0dip\"\r\n        android:layout_height=\"match_parent\"\r\n        android:layout_weight=\"1\"\r\n        android:gravity=\"center|right\"\r\n        android:textColor=\"#00aacc\"\r\n        android:text=\"--°\"\r\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\r\n\r\n    <com.way.ui.view.WeatherTypefacedTextView\r\n        android:id=\"@+id/forecast_high_temp_tv\"\r\n        style=\"@style/Label.Shadow\"\r\n        android:layout_width=\"0dip\"\r\n        android:layout_height=\"match_parent\"\r\n        android:layout_weight=\"1\"\r\n        android:gravity=\"center|right\"\r\n        android:text=\"--°\"\r\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\r\n\r\n</LinearLayout>"
  },
  {
    "path": "WayHoo/res/layout/weather_fragment.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" >\n\n    <FrameLayout\n        android:id=\"@+id/top_part\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_alignWithParentIfMissing=\"true\"\n        android:layout_centerInParent=\"true\" >\n\n        <ImageView\n            android:id=\"@+id/weather_background\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:adjustViewBounds=\"true\"\n            android:scaleType=\"centerCrop\"\n            android:src=\"@drawable/bg_na_blur\" />\n\n        <ImageView\n            android:id=\"@+id/weather_background_blurred\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:adjustViewBounds=\"true\"\n            android:scaleType=\"centerCrop\" />\n    </FrameLayout>\n\n    <android.support.v4.widget.SwipeRefreshLayout\n        android:id=\"@+id/swiperefresh\"\n        android:layout_marginTop=\"@dimen/weather_topbar_padding\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" >\n\n        <ListView\n            android:id=\"@+id/drag_list\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:paddingLeft=\"@dimen/margin_sm\"\n            android:paddingRight=\"@dimen/margin_sm\"\n            android:background=\"@android:color/transparent\"\n            android:cacheColorHint=\"@android:color/transparent\"\n            android:divider=\"@android:color/transparent\"\n            android:dividerHeight=\"10.0dip\"\n            android:drawSelectorOnTop=\"false\"\n            android:focusable=\"true\"\n            android:focusableInTouchMode=\"true\"\n            android:listSelector=\"@android:color/transparent\"\n            android:overScrollMode=\"never\"\n            android:scrollbars=\"none\" />\n    </android.support.v4.widget.SwipeRefreshLayout>\n\n</RelativeLayout>"
  },
  {
    "path": "WayHoo/res/layout/weather_index.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.way.ui.view.WeatherIndexView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:weather=\"http://schemas.android.com/apk/res-auto\"\n    style=\"@style/WeatherModule\"\n    android:orientation=\"vertical\" >\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/index_label\"\n        style=\"@style/Label\"\n        android:layout_marginTop=\"@dimen/negative_margin_md\"\n        android:text=\"指数\"\n        weather:typeface=\"fonts/Roboto-Thin.ttf\" />\n\n    <ImageView\n        android:id=\"@+id/divider\"\n        style=\"@style/Line.Solid\"\n        android:layout_below=\"@id/index_label\" />\n\n    <include\n        android:id=\"@+id/wind_index\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/divider\"\n        android:layout_marginBottom=\"@dimen/margin_xxs\"\n        android:layout_marginTop=\"@dimen/margin_xxs\"\n        layout=\"@layout/weather_index_item\" />\n\n    <ImageView\n        android:id=\"@+id/divider3\"\n        style=\"@style/Line.Dotted\"\n        android:layout_width=\"fill_parent\"\n        android:layout_below=\"@id/wind_index\"\n        android:scaleType=\"fitXY\" />\n\n    <include\n        android:id=\"@+id/ua_index\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/divider3\"\n        android:layout_marginBottom=\"@dimen/margin_xxs\"\n        android:layout_marginTop=\"@dimen/margin_xxs\"\n        layout=\"@layout/weather_index_item\" />\n\n    <ImageView\n        android:id=\"@+id/divider4\"\n        style=\"@style/Line.Dotted\"\n        android:layout_width=\"fill_parent\"\n        android:layout_below=\"@id/ua_index\"\n        android:scaleType=\"fitXY\" />\n\n    <include\n        android:id=\"@+id/clothe_index\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/divider4\"\n        android:layout_marginBottom=\"@dimen/margin_xxs\"\n        android:layout_marginTop=\"@dimen/margin_xxs\"\n        layout=\"@layout/weather_index_item\" />\n\n    <ImageView\n        android:id=\"@+id/divider5\"\n        style=\"@style/Line.Dotted\"\n        android:layout_width=\"fill_parent\"\n        android:layout_below=\"@id/clothe_index\"\n        android:scaleType=\"fitXY\" />\n\n    <include\n        android:id=\"@+id/comfort_index\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/divider5\"\n        android:layout_marginBottom=\"@dimen/margin_xxs\"\n        android:layout_marginTop=\"@dimen/margin_xxs\"\n        layout=\"@layout/weather_index_item\" />\n\n    <ImageView\n        android:id=\"@+id/divider6\"\n        style=\"@style/Line.Dotted\"\n        android:layout_width=\"fill_parent\"\n        android:layout_below=\"@id/comfort_index\"\n        android:scaleType=\"fitXY\" />\n\n    <include\n        android:id=\"@+id/car_index\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/divider6\"\n        android:layout_marginBottom=\"@dimen/margin_xxs\"\n        android:layout_marginTop=\"@dimen/margin_xxs\"\n        layout=\"@layout/weather_index_item\" />\n\n    <ImageView\n        android:id=\"@+id/divider7\"\n        style=\"@style/Line.Dotted\"\n        android:layout_width=\"fill_parent\"\n        android:layout_below=\"@id/car_index\"\n        android:scaleType=\"fitXY\" />\n\n    <include\n        android:id=\"@+id/insolation_index\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/divider7\"\n        android:layout_marginBottom=\"@dimen/margin_xxs\"\n        android:layout_marginTop=\"@dimen/margin_xxs\"\n        layout=\"@layout/weather_index_item\" />\n\n    <ImageView\n        android:id=\"@+id/divider8\"\n        style=\"@style/Line.Dotted\"\n        android:layout_width=\"fill_parent\"\n        android:layout_below=\"@id/insolation_index\"\n        android:scaleType=\"fitXY\" />\n\n    <include\n        android:id=\"@+id/sport_index\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/divider8\"\n        android:layout_marginBottom=\"@dimen/margin_xxs\"\n        android:layout_marginTop=\"@dimen/margin_xxs\"\n        layout=\"@layout/weather_index_item\" />\n\n    <ImageView\n        android:id=\"@+id/travel_divider\"\n        style=\"@style/Line.Dotted\"\n        android:layout_width=\"fill_parent\"\n        android:layout_below=\"@id/sport_index\"\n        android:scaleType=\"fitXY\" />\n\n    <include\n        android:id=\"@+id/travel_index\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/travel_divider\"\n        android:layout_marginBottom=\"@dimen/margin_xxs\"\n        android:layout_marginTop=\"@dimen/margin_xxs\"\n        layout=\"@layout/weather_index_item\" />\n\n    <ImageView\n        android:id=\"@+id/divider10\"\n        style=\"@style/Line.Dotted\"\n        android:layout_width=\"fill_parent\"\n        android:layout_below=\"@id/travel_index\"\n        android:scaleType=\"fitXY\" />\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/forecast_foot\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"30dip\"\n        android:layout_below=\"@id/divider10\"\n        android:drawableRight=\"@drawable/wunderground_logo\"\n        android:gravity=\"bottom|right\"\n        android:paddingTop=\"4dip\"\n        android:textColor=\"@android:color/white\"\n        android:textSize=\"@dimen/text_md\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n</com.way.ui.view.WeatherIndexView>"
  },
  {
    "path": "WayHoo/res/layout/weather_index_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:weather=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingBottom=\"@dimen/negative_margin_sm\"\n    android:paddingTop=\"@dimen/margin_sm\"\n    android:orientation=\"vertical\" >\n\n    <ImageView\n        android:id=\"@+id/index_icon_iv\"\n        android:layout_width=\"48dip\"\n        android:layout_height=\"48dip\"\n        android:layout_marginLeft=\"@dimen/margin_xs\"\n        android:layout_marginTop=\"12.0dip\"\n        android:src=\"@drawable/na\" />\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/index_title_tv\"\n        style=\"@style/Label.Shadow.Medium\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"36dip\"\n        android:textSize=\"14sp\"\n        android:layout_marginLeft=\"@dimen/margin_md\"\n        android:layout_marginRight=\"5.0dip\"\n        android:layout_toRightOf=\"@id/index_icon_iv\"\n        android:gravity=\"center|left\"\n        android:paddingBottom=\"@dimen/padding_xxs\"\n        android:paddingTop=\"@dimen/padding_xxs\"\n        android:text=\"----\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n    <ImageView\n        android:id=\"@+id/dotline1\"\n        style=\"@style/Line.Dotted\"\n        android:layout_alignLeft=\"@id/index_title_tv\"\n        android:layout_below=\"@id/index_title_tv\" />\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/index_desc_tv\"\n        style=\"@style/Label.Shadow.Medium\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"36dip\"\n        android:layout_below=\"@id/dotline1\"\n        android:layout_marginLeft=\"@dimen/margin_md\"\n        android:layout_marginRight=\"5.0dip\"\n        android:layout_toRightOf=\"@id/index_icon_iv\"\n        android:gravity=\"center_vertical\"\n        android:paddingBottom=\"@dimen/padding_xxs\"\n        android:paddingTop=\"@dimen/padding_xxs\"\n        android:text=\"--\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n    <ImageView\n        android:id=\"@+id/dotline2\"\n        style=\"@style/Line.Dotted\"\n        android:layout_below=\"@id/index_desc_tv\" />\n\n    <com.way.ui.view.WeatherTypefacedTextView\n        android:id=\"@+id/index_detail_tv\"\n        style=\"@style/Label.Shadow.Medium\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"40.0dip\"\n        android:layout_below=\"@id/dotline2\"\n        android:layout_marginLeft=\"@dimen/margin_xs\"\n        android:layout_marginRight=\"@dimen/margin_xs\"\n        android:gravity=\"center_vertical\"\n        android:singleLine=\"false\"\n        android:maxLines=\"2\"\n        android:paddingBottom=\"@dimen/padding_xxs\"\n        android:paddingTop=\"@dimen/padding_xxs\"\n        android:text=\"--\"\n        weather:typeface=\"fonts/Roboto-Light.ttf\" />\n\n</RelativeLayout>"
  },
  {
    "path": "WayHoo/res/layout/weather_info_error.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"80dip\"\n    android:layout_alignParentBottom=\"true\"\n    android:layout_marginBottom=\"20dip\"\n    android:background=\"#80000000\"\n    android:orientation=\"vertical\" >\n\n    <ImageView\n        android:id=\"@+id/sadcloud\"\n        android:layout_width=\"50dip\"\n        android:layout_height=\"50dip\"\n        android:layout_centerVertical=\"true\"\n        android:layout_marginLeft=\"20dip\"\n        android:src=\"@drawable/sadcloud\" />\n\n    <ImageView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignBottom=\"@id/sadcloud\"\n        android:layout_alignRight=\"@id/sadcloud\"\n        android:layout_marginBottom=\"10dip\"\n        android:layout_marginRight=\"10dip\"\n        android:src=\"@drawable/tear\" />\n\n    <TextView\n        android:id=\"@+id/sad_title\"\n        style=\"@style/Label.Shadow\"\n        android:layout_marginLeft=\"20dip\"\n        android:layout_marginTop=\"25dip\"\n        android:layout_toRightOf=\"@id/sadcloud\"\n        android:text=\"抱歉\" />\n\n    <TextView\n        style=\"@style/Label.Shadow.Medium\"\n        android:layout_below=\"@id/sad_title\"\n        android:layout_marginLeft=\"20dip\"\n        android:layout_toRightOf=\"@id/sadcloud\"\n        android:text=\"天气信息不可用\" />\n\n</RelativeLayout>"
  },
  {
    "path": "WayHoo/res/layout/weather_provider_layout_rigo_2x2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/weather_rigo_widget_2x2\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\" >\n\n    <ImageView\n        android:id=\"@+id/widget_backgroud\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\"\n        android:layout_margin=\"1.0dip\"\n        android:scaleType=\"centerCrop\" />\n\n    <ImageView\n        android:id=\"@+id/transparent_bg\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\"\n        android:clickable=\"false\"\n        android:focusable=\"false\"\n        android:scaleType=\"fitXY\"\n        android:src=\"@drawable/bg_widget_v\"\n        android:visibility=\"gone\" />\n\n    <ImageView\n        android:id=\"@+id/loading_backgroud\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\"\n        android:scaleType=\"fitXY\"\n        android:visibility=\"gone\" />\n\n    <ImageView\n        android:id=\"@+id/loading_logo\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_centerHorizontal=\"true\"\n        android:visibility=\"gone\" />\n\n    <TextView\n        android:id=\"@+id/loading\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\"\n        android:layout_centerInParent=\"true\"\n        android:gravity=\"center\"\n        android:text=\"正在加载,请稍等...\"\n        android:textSize=\"14.0dip\"\n        android:visibility=\"gone\" />\n\n    <RelativeLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\"\n        android:layout_marginBottom=\"3.0dip\"\n        android:layout_marginLeft=\"3.0dip\" >\n\n        <LinearLayout\n            android:id=\"@+id/weather_location_layout\"\n            android:layout_width=\"110.0dip\"\n            android:layout_height=\"40.0dip\"\n            android:layout_alignParentLeft=\"true\"\n            android:layout_alignParentTop=\"true\"\n            android:layout_marginLeft=\"10.0dip\"\n            android:orientation=\"horizontal\"\n            android:paddingTop=\"5.0dip\" >\n\n            <TextView\n                android:id=\"@+id/location_view\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginRight=\"1.0dip\"\n                android:ellipsize=\"end\"\n                android:gravity=\"center\"\n                android:lines=\"1\"\n                android:shadowColor=\"#bf000000\"\n                android:shadowDx=\"1.0\"\n                android:shadowDy=\"1.0\"\n                android:shadowRadius=\"3.0\"\n                android:textColor=\"@android:color/white\"\n                android:textSize=\"18.0dip\" />\n\n            <ImageView\n                android:id=\"@+id/location_icon_view\"\n                android:layout_width=\"18.0dip\"\n                android:layout_height=\"18.0dip\"\n                android:layout_marginTop=\"2.0dip\"\n                android:src=\"@drawable/current_loc_active_26x26\"\n                android:visibility=\"invisible\" />\n        </LinearLayout>\n\n        <RelativeLayout\n            android:id=\"@+id/refresh_layout_view\"\n            android:layout_width=\"50.0dip\"\n            android:layout_height=\"50.0dip\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_alignParentTop=\"true\"\n            android:paddingLeft=\"18.0dip\"\n            android:paddingTop=\"2.0dip\" >\n\n            <FrameLayout\n                android:id=\"@+id/widget_fresh_progressbar\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:visibility=\"gone\" >\n\n                <ProgressBar\n                    style=\"@style/weather_widget_refresh_style\"\n                    android:layout_width=\"26.0dip\"\n                    android:layout_height=\"26.0dip\" />\n            </FrameLayout>\n\n            <ImageView\n                android:id=\"@+id/refresh_view\"\n                android:layout_width=\"26.0dip\"\n                android:layout_height=\"26.0dip\"\n                android:src=\"@drawable/refresh_27x27_1\"\n                android:visibility=\"invisible\" />\n        </RelativeLayout>\n\n        <LinearLayout\n            android:id=\"@+id/weather_temperature_layout\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentBottom=\"true\"\n            android:layout_alignParentLeft=\"true\"\n            android:layout_marginBottom=\"5.0dip\"\n            android:layout_marginLeft=\"5.0dip\"\n            android:orientation=\"horizontal\" >\n\n            <RelativeLayout\n                android:id=\"@+id/weather_view\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"fill_parent\" >\n\n                <TextView\n                    android:id=\"@+id/weather_text_view\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_alignParentBottom=\"true\"\n                    android:layout_centerHorizontal=\"true\"\n                    android:gravity=\"center\"\n                    android:paddingLeft=\"1.0dip\"\n                    android:paddingRight=\"1.0dip\"\n                    android:shadowColor=\"#bf000000\"\n                    android:shadowDx=\"1.0\"\n                    android:shadowDy=\"1.0\"\n                    android:shadowRadius=\"3.0\"\n                    android:textColor=\"@android:color/white\"\n                    android:textSize=\"14.0dip\" />\n\n                <ImageView\n                    android:id=\"@+id/weather_icon_view\"\n                    android:layout_width=\"40.0dip\"\n                    android:layout_height=\"40.0dip\"\n                    android:layout_above=\"@id/weather_text_view\"\n                    android:layout_centerInParent=\"true\"\n                    android:layout_marginBottom=\"6.0dip\" />\n            </RelativeLayout>\n\n            <ImageView\n                android:id=\"@+id/weather_divider_view\"\n                android:layout_width=\"2.0px\"\n                android:layout_height=\"60.0dip\"\n                android:layout_marginLeft=\"6.0dip\"\n                android:layout_marginTop=\"13.0dip\"\n                android:background=\"#99ffffff\"\n                android:visibility=\"invisible\" />\n\n            <RelativeLayout\n                android:id=\"@+id/temperature_pm_view\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"fill_parent\"\n                android:layout_marginLeft=\"6.0dip\"\n                android:gravity=\"bottom\" >\n\n                <RelativeLayout\n                    android:id=\"@+id/pm_view\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"22.0dip\"\n                    android:layout_alignParentBottom=\"true\"\n                    android:layout_marginLeft=\"6.0dip\"\n                    android:background=\"@drawable/bg_pm25_01\"\n                    android:minWidth=\"44.0dip\"\n                    android:orientation=\"horizontal\"\n                    android:visibility=\"gone\" >\n\n                    <TextView\n                        android:id=\"@+id/widget_pm_value_view\"\n                        style=\"@style/Shade\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_marginLeft=\"2.0dip\"\n                        android:layout_marginTop=\"5.0dip\"\n                        android:gravity=\"center_vertical\"\n                        android:singleLine=\"true\"\n                        android:text=\"60\"\n                        android:textColor=\"@android:color/white\"\n                        android:textSize=\"12.0dip\" />\n\n                    <ImageView\n                        android:id=\"@+id/widget_aqi_divider\"\n                        android:layout_width=\"1.0px\"\n                        android:layout_height=\"18.0dip\"\n                        android:layout_gravity=\"center_horizontal\"\n                        android:layout_marginLeft=\"2.0dip\"\n                        android:layout_marginRight=\"2.0dip\"\n                        android:layout_marginTop=\"6.0dip\"\n                        android:layout_toRightOf=\"@id/widget_pm_value_view\"\n                        android:background=\"#7bffffff\" />\n\n                    <TextView\n                        android:id=\"@+id/widget_pm_level_view\"\n                        style=\"@style/Shade\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"fill_parent\"\n                        android:layout_marginTop=\"5.0dip\"\n                        android:layout_toRightOf=\"@id/widget_aqi_divider\"\n                        android:gravity=\"center_vertical\"\n                        android:minWidth=\"14.0dip\"\n                        android:singleLine=\"true\"\n                        android:text=\"优\"\n                        android:textColor=\"@android:color/white\"\n                        android:textSize=\"12.0dip\" />\n                </RelativeLayout>\n\n                <RelativeLayout\n                    android:id=\"@+id/temperature_view\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_above=\"@id/pm_view\"\n                    android:layout_gravity=\"bottom|left|center\"\n                    android:layout_marginBottom=\"2.0dip\" >\n\n                    <ImageView\n                        android:id=\"@+id/current_temperature_view\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_alignParentLeft=\"true\" />\n\n                    <ImageView\n                        android:id=\"@id/degree_icon\"\n                        android:layout_width=\"8.0dip\"\n                        android:layout_height=\"8.0dip\"\n                        android:layout_alignParentRight=\"true\"\n                        android:layout_alignParentTop=\"true\"\n                        android:layout_marginLeft=\"2.0dip\"\n                        android:layout_marginTop=\"10.0dip\"\n                        android:visibility=\"gone\" />\n                </RelativeLayout>\n            </RelativeLayout>\n        </LinearLayout>\n    </RelativeLayout>\n\n</RelativeLayout>"
  },
  {
    "path": "WayHoo/res/layout/weather_provider_layout_rigo_4x1.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/weather_rigo_widget_4x1\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\" >\n\n    <ImageView\n        android:id=\"@+id/widget_backgroud\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\"\n        android:layout_margin=\"1.0dip\"\n        android:scaleType=\"centerCrop\" />\n\n    <ImageView\n        android:id=\"@+id/transparent_bg\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\"\n        android:clickable=\"false\"\n        android:focusable=\"false\"\n        android:scaleType=\"fitXY\"\n        android:src=\"@drawable/bg_widget_v\" />\n\n    <ImageView\n        android:id=\"@+id/loading_backgroud\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\"\n        android:scaleType=\"fitXY\"\n        android:visibility=\"gone\" />\n\n    <ImageView\n        android:id=\"@+id/loading_logo\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_centerHorizontal=\"true\"\n        android:visibility=\"gone\" />\n\n    <TextView\n        android:id=\"@+id/loading\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\"\n        android:layout_centerInParent=\"true\"\n        android:gravity=\"center\"\n        android:text=\"@string/widget_loading_tips\"\n        android:textSize=\"14.0dip\"\n        android:visibility=\"gone\" />\n\n    <RelativeLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\" >\n\n        <LinearLayout\n            android:id=\"@id/weather_location_layout\"\n            android:layout_width=\"110.0dip\"\n            android:layout_height=\"40.0dip\"\n            android:layout_alignParentLeft=\"true\"\n            android:layout_alignParentTop=\"true\"\n            android:layout_marginLeft=\"10.0dip\"\n            android:orientation=\"horizontal\"\n            android:paddingTop=\"5.0dip\" >\n\n            <TextView\n                android:id=\"@id/location_view\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginRight=\"1.0dip\"\n                android:ellipsize=\"end\"\n                android:gravity=\"center\"\n                android:lines=\"1\"\n                android:shadowColor=\"#bf000000\"\n                android:shadowDx=\"1.0\"\n                android:shadowDy=\"1.0\"\n                android:shadowRadius=\"3.0\"\n                android:textColor=\"@android:color/white\"\n                android:textSize=\"18.0dip\" />\n\n            <ImageView\n                android:id=\"@+id/location_icon_view\"\n                android:layout_width=\"18.0dip\"\n                android:layout_height=\"18.0dip\"\n                android:layout_marginTop=\"2.0dip\"\n                android:src=\"@drawable/current_loc_active_26x26\"\n                android:visibility=\"invisible\" />\n        </LinearLayout>\n\n        <RelativeLayout\n            android:id=\"@id/refresh_layout_view\"\n            android:layout_width=\"50.0dip\"\n            android:layout_height=\"50.0dip\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_alignParentTop=\"true\"\n            android:layout_marginRight=\"5.0dip\"\n            android:gravity=\"right\"\n            android:paddingTop=\"5.0dip\" >\n\n            <FrameLayout\n                android:id=\"@id/widget_fresh_progressbar\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:visibility=\"gone\" >\n\n                <ProgressBar\n                    style=\"@style/weather_widget_refresh_style\"\n                    android:layout_width=\"26.0dip\"\n                    android:layout_height=\"26.0dip\" />\n            </FrameLayout>\n\n            <ImageView\n                android:id=\"@+id/refresh_view\"\n                android:layout_width=\"26.0dip\"\n                android:layout_height=\"26.0dip\"\n                android:src=\"@drawable/refresh_27x27_1\"\n                android:visibility=\"invisible\" />\n        </RelativeLayout>\n\n        <LinearLayout\n            android:id=\"@id/weather_temperature_layout\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"50.0dip\"\n            android:layout_alignParentBottom=\"true\"\n            android:layout_alignParentLeft=\"true\"\n            android:layout_marginBottom=\"6.0dip\"\n            android:layout_marginLeft=\"5.0dip\"\n            android:gravity=\"bottom\"\n            android:orientation=\"horizontal\" >\n\n            <RelativeLayout\n                android:id=\"@id/weather_view\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"fill_parent\" >\n\n                <TextView\n                    android:id=\"@id/weather_text_view\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_alignParentBottom=\"true\"\n                    android:layout_centerHorizontal=\"true\"\n                    android:gravity=\"center\"\n                    android:paddingLeft=\"1.0dip\"\n                    android:paddingRight=\"1.0dip\"\n                    android:shadowColor=\"#bf000000\"\n                    android:shadowDx=\"1.0\"\n                    android:shadowDy=\"1.0\"\n                    android:shadowRadius=\"3.0\"\n                    android:textColor=\"@android:color/white\"\n                    android:textSize=\"10.0dip\" />\n\n                <ImageView\n                    android:id=\"@+id/weather_icon_view\"\n                    android:layout_width=\"24.0dip\"\n                    android:layout_height=\"24.0dip\"\n                    android:layout_above=\"@id/weather_text_view\"\n                    android:layout_centerInParent=\"true\" />\n            </RelativeLayout>\n\n            <ImageView\n                android:id=\"@+id/weather_divider_view\"\n                android:layout_width=\"2.0px\"\n                android:layout_height=\"35.0dip\"\n                android:layout_marginLeft=\"4.0dip\"\n                android:background=\"#99ffffff\"\n                android:visibility=\"invisible\" />\n\n            <LinearLayout\n                android:id=\"@id/temperature_view\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"5.0dip\"\n                android:orientation=\"horizontal\" >\n\n                <RelativeLayout\n                    android:id=\"@+id/current_temperature_layout\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_gravity=\"bottom|left|center\"\n                    android:gravity=\"bottom\" >\n\n                    <ImageView\n                        android:id=\"@+id/current_temperature_view\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_alignParentLeft=\"true\" />\n                </RelativeLayout>\n\n                <LinearLayout\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_gravity=\"bottom|center\"\n                    android:orientation=\"vertical\" >\n\n                    <RelativeLayout\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_gravity=\"bottom|right|center\"\n                        android:gravity=\"bottom\" >\n\n                        <TextView\n                            android:id=\"@+id/high_temperature_view\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:layout_alignParentLeft=\"true\"\n                            android:shadowColor=\"#bf000000\"\n                            android:shadowDx=\"1.0\"\n                            android:shadowDy=\"1.0\"\n                            android:shadowRadius=\"3.0\"\n                            android:textColor=\"@android:color/white\"\n                            android:textSize=\"12.0dip\" />\n                    </RelativeLayout>\n\n                    <RelativeLayout\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_gravity=\"bottom|right|center\"\n                        android:gravity=\"bottom\" >\n\n                        <TextView\n                            android:id=\"@+id/low_temperature_view\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:layout_alignParentLeft=\"true\"\n                            android:shadowColor=\"#bf000000\"\n                            android:shadowDx=\"1.0\"\n                            android:shadowDy=\"1.0\"\n                            android:shadowRadius=\"3.0\"\n                            android:textColor=\"@android:color/white\"\n                            android:textSize=\"12.0dip\" />\n                    </RelativeLayout>\n                </LinearLayout>\n            </LinearLayout>\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentBottom=\"true\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_marginBottom=\"6.0dip\"\n            android:layout_marginLeft=\"6.0dip\"\n            android:layout_marginRight=\"6.0dip\"\n            android:orientation=\"horizontal\" >\n\n            <LinearLayout\n                android:id=\"@+id/day1_weather_layout\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"6.0dip\"\n                android:orientation=\"vertical\" >\n\n                <ImageView\n                    android:id=\"@+id/day1_weather_icon_view\"\n                    android:layout_width=\"32.0dip\"\n                    android:layout_height=\"32.0dip\" />\n\n                <TextView\n                    android:id=\"@+id/day1_date_view\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:ellipsize=\"end\"\n                    android:gravity=\"center\"\n                    android:lines=\"1\"\n                    android:shadowColor=\"#bf000000\"\n                    android:shadowDx=\"1.0\"\n                    android:shadowDy=\"1.0\"\n                    android:shadowRadius=\"3.0\"\n                    android:textColor=\"@android:color/white\"\n                    android:textSize=\"12.0dip\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:id=\"@+id/day2_weather_layout\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"6.0dip\"\n                android:orientation=\"vertical\" >\n\n                <ImageView\n                    android:id=\"@+id/day2_weather_icon_view\"\n                    android:layout_width=\"32.0dip\"\n                    android:layout_height=\"32.0dip\" />\n\n                <TextView\n                    android:id=\"@+id/day2_date_view\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:ellipsize=\"end\"\n                    android:gravity=\"center\"\n                    android:lines=\"1\"\n                    android:shadowColor=\"#bf000000\"\n                    android:shadowDx=\"1.0\"\n                    android:shadowDy=\"1.0\"\n                    android:shadowRadius=\"3.0\"\n                    android:textColor=\"@android:color/white\"\n                    android:textSize=\"12.0dip\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:id=\"@+id/day3_weather_layout\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"6.0dip\"\n                android:orientation=\"vertical\" >\n\n                <ImageView\n                    android:id=\"@+id/day3_weather_icon_view\"\n                    android:layout_width=\"32.0dip\"\n                    android:layout_height=\"32.0dip\" />\n\n                <TextView\n                    android:id=\"@+id/day3_date_view\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:ellipsize=\"end\"\n                    android:gravity=\"center\"\n                    android:lines=\"1\"\n                    android:shadowColor=\"#bf000000\"\n                    android:shadowDx=\"1.0\"\n                    android:shadowDy=\"1.0\"\n                    android:shadowRadius=\"3.0\"\n                    android:textColor=\"@android:color/white\"\n                    android:textSize=\"12.0dip\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:id=\"@+id/day4_weather_layout\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"6.0dip\"\n                android:orientation=\"vertical\" >\n\n                <ImageView\n                    android:id=\"@+id/day4_weather_icon_view\"\n                    android:layout_width=\"32.0dip\"\n                    android:layout_height=\"32.0dip\"\n                    android:gravity=\"top\" />\n\n                <TextView\n                    android:id=\"@+id/day4_date_view\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:ellipsize=\"end\"\n                    android:gravity=\"center\"\n                    android:lines=\"1\"\n                    android:shadowColor=\"#bf000000\"\n                    android:shadowDx=\"1.0\"\n                    android:shadowDy=\"1.0\"\n                    android:shadowRadius=\"3.0\"\n                    android:textColor=\"@android:color/white\"\n                    android:textSize=\"12.0dip\" />\n            </LinearLayout>\n        </LinearLayout>\n    </RelativeLayout>\n\n</RelativeLayout>"
  },
  {
    "path": "WayHoo/res/layout/weather_provider_layout_rigo_4x2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/weather_rigo_widget_4x2\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\" >\n\n    <ImageView\n        android:id=\"@+id/widget_backgroud\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\"\n        android:layout_margin=\"1.0dip\"\n        android:scaleType=\"centerCrop\" />\n\n    <ImageView\n        android:id=\"@+id/transparent_bg\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\"\n        android:clickable=\"false\"\n        android:focusable=\"false\"\n        android:scaleType=\"fitXY\"\n        android:src=\"@drawable/bg_widget_v\"\n        android:visibility=\"gone\" />\n\n    <ImageView\n        android:id=\"@+id/loading_backgroud\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\"\n        android:scaleType=\"fitXY\"\n        android:visibility=\"gone\" />\n\n    <ImageView\n        android:id=\"@+id/loading_logo\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_centerHorizontal=\"true\"\n        android:visibility=\"gone\" />\n\n    <TextView\n        android:id=\"@id/loading\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\"\n        android:layout_centerInParent=\"true\"\n        android:gravity=\"center\"\n        android:text=\"@string/widget_loading_tips\"\n        android:textSize=\"14.0dip\"\n        android:visibility=\"gone\" />\n\n    <RelativeLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\" >\n\n        <RelativeLayout\n            android:id=\"@+id/date_time_layout\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentTop=\"true\"\n            android:layout_marginLeft=\"8.0dip\" >\n\n            <ImageView\n                android:id=\"@+id/time_view\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"14.0dip\" />\n\n            <LinearLayout\n                android:id=\"@+id/date_view\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"4.0dip\"\n                android:layout_marginTop=\"14.0dip\"\n                android:layout_toRightOf=\"@id/time_view\"\n                android:orientation=\"vertical\" >\n\n                <TextView\n                    android:id=\"@+id/time_a_view\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:gravity=\"top\"\n                    android:paddingLeft=\"5.0dip\"\n                    android:shadowColor=\"#bf000000\"\n                    android:shadowDx=\"1.0\"\n                    android:shadowDy=\"1.0\"\n                    android:shadowRadius=\"3.0\"\n                    android:singleLine=\"true\"\n                    android:textColor=\"#ccffffff\"\n                    android:textSize=\"12.0dip\" />\n\n                <TextView\n                    android:id=\"@+id/solor_date_view\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:shadowColor=\"#bf000000\"\n                    android:shadowDx=\"1.0\"\n                    android:shadowDy=\"1.0\"\n                    android:shadowRadius=\"3.0\"\n                    android:singleLine=\"true\"\n                    android:text=\"\"\n                    android:textColor=\"#ccffffff\"\n                    android:textSize=\"12.0dip\" />\n\n                <TextView\n                    android:id=\"@+id/lunar_date_view\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:shadowColor=\"#bf000000\"\n                    android:shadowDx=\"1.0\"\n                    android:shadowDy=\"1.0\"\n                    android:shadowRadius=\"3.0\"\n                    android:singleLine=\"true\"\n                    android:text=\"\"\n                    android:textColor=\"#ccffffff\"\n                    android:textSize=\"12.0dip\" />\n\n                <TextView\n                    android:id=\"@+id/festival_view\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:shadowColor=\"#bf000000\"\n                    android:shadowDx=\"1.0\"\n                    android:shadowDy=\"1.0\"\n                    android:shadowRadius=\"3.0\"\n                    android:singleLine=\"true\"\n                    android:text=\"N/A\"\n                    android:textColor=\"#ccffffff\"\n                    android:textSize=\"12.0dip\"\n                    android:visibility=\"invisible\" />\n            </LinearLayout>\n        </RelativeLayout>\n\n        <RelativeLayout\n            android:id=\"@+id/refresh_layout_view\"\n            android:layout_width=\"50.0dip\"\n            android:layout_height=\"50.0dip\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_alignParentTop=\"true\"\n            android:layout_marginRight=\"4.0dip\"\n            android:gravity=\"right\"\n            android:paddingTop=\"5.0dip\" >\n\n            <FrameLayout\n                android:id=\"@id/widget_fresh_progressbar\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:visibility=\"gone\" >\n\n                <ProgressBar\n                    style=\"@style/weather_widget_refresh_style\"\n                    android:layout_width=\"26.0dip\"\n                    android:layout_height=\"26.0dip\" />\n            </FrameLayout>\n\n            <ImageView\n                android:id=\"@+id/refresh_view\"\n                android:layout_width=\"26.0dip\"\n                android:layout_height=\"26.0dip\"\n                android:src=\"@drawable/refresh_27x27_1\"\n                android:visibility=\"invisible\" />\n        </RelativeLayout>\n\n        <LinearLayout\n            android:id=\"@id/weather_temperature_layout\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentBottom=\"true\"\n            android:layout_alignParentLeft=\"true\"\n            android:layout_marginBottom=\"8.0dip\"\n            android:layout_marginLeft=\"5.0dip\"\n            android:orientation=\"horizontal\" >\n\n            <RelativeLayout\n                android:id=\"@+id/weather_view\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"fill_parent\" >\n\n                <TextView\n                    android:id=\"@id/weather_text_view\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_alignParentBottom=\"true\"\n                    android:layout_centerHorizontal=\"true\"\n                    android:gravity=\"center\"\n                    android:paddingLeft=\"1.0dip\"\n                    android:paddingRight=\"1.0dip\"\n                    android:shadowColor=\"#bf000000\"\n                    android:shadowDx=\"1.0\"\n                    android:shadowDy=\"1.0\"\n                    android:shadowRadius=\"3.0\"\n                    android:textColor=\"@android:color/white\"\n                    android:textSize=\"14.0dip\" />\n\n                <ImageView\n                    android:id=\"@+id/weather_icon_view\"\n                    android:layout_width=\"40.0dip\"\n                    android:layout_height=\"40.0dip\"\n                    android:layout_above=\"@id/weather_text_view\"\n                    android:layout_centerInParent=\"true\"\n                    android:layout_marginBottom=\"6.0dip\" />\n            </RelativeLayout>\n\n            <ImageView\n                android:id=\"@+id/weather_divider_view\"\n                android:layout_width=\"2.0px\"\n                android:layout_height=\"60.0dip\"\n                android:layout_marginLeft=\"6.0dip\"\n                android:layout_marginTop=\"13.0dip\"\n                android:background=\"#99ffffff\"\n                android:visibility=\"invisible\" />\n\n            <RelativeLayout\n                android:id=\"@id/temperature_pm_view\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"fill_parent\"\n                android:layout_marginLeft=\"6.0dip\"\n                android:gravity=\"bottom\" >\n\n                <RelativeLayout\n                    android:id=\"@id/pm_view\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"22.0dip\"\n                    android:layout_alignParentBottom=\"true\"\n                    android:layout_marginLeft=\"6.0dip\"\n                    android:background=\"@drawable/bg_pm25_01\"\n                    android:minWidth=\"44.0dip\"\n                    android:orientation=\"horizontal\"\n                    android:visibility=\"gone\" >\n\n                    <TextView\n                        android:id=\"@id/widget_pm_value_view\"\n                        style=\"@style/Shade\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_marginLeft=\"2.0dip\"\n                        android:layout_marginTop=\"5.0dip\"\n                        android:gravity=\"center_vertical\"\n                        android:singleLine=\"true\"\n                        android:text=\"60\"\n                        android:textColor=\"@android:color/white\"\n                        android:textSize=\"12.0dip\" />\n\n                    <ImageView\n                        android:id=\"@id/widget_aqi_divider\"\n                        android:layout_width=\"1.0px\"\n                        android:layout_height=\"18.0dip\"\n                        android:layout_gravity=\"center_horizontal\"\n                        android:layout_marginLeft=\"2.0dip\"\n                        android:layout_marginRight=\"2.0dip\"\n                        android:layout_marginTop=\"6.0dip\"\n                        android:layout_toRightOf=\"@id/widget_pm_value_view\"\n                        android:background=\"#7bffffff\" />\n\n                    <TextView\n                        android:id=\"@id/widget_pm_level_view\"\n                        style=\"@style/Shade\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"fill_parent\"\n                        android:layout_marginTop=\"5.0dip\"\n                        android:layout_toRightOf=\"@id/widget_aqi_divider\"\n                        android:gravity=\"center_vertical\"\n                        android:minWidth=\"14.0dip\"\n                        android:singleLine=\"true\"\n                        android:text=\"优\"\n                        android:textColor=\"@android:color/white\"\n                        android:textSize=\"12.0dip\" />\n                </RelativeLayout>\n\n                <LinearLayout\n                    android:id=\"@id/temperature_view\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_above=\"@id/pm_view\"\n                    android:layout_marginBottom=\"2.0dip\"\n                    android:orientation=\"horizontal\" >\n\n                    <RelativeLayout\n                        android:id=\"@id/current_temperature_layout\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_gravity=\"bottom|left|center\" >\n\n                        <ImageView\n                            android:id=\"@id/current_temperature_view\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:layout_alignParentLeft=\"true\" />\n\n                        <ImageView\n                            android:layout_width=\"8.0dip\"\n                            android:layout_height=\"8.0dip\"\n                            android:layout_alignParentRight=\"true\"\n                            android:layout_alignParentTop=\"true\"\n                            android:layout_marginLeft=\"2.0dip\"\n                            android:layout_marginTop=\"10.0dip\"\n                            android:visibility=\"gone\" />\n                    </RelativeLayout>\n\n                    <LinearLayout\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_gravity=\"bottom|center\"\n                        android:orientation=\"vertical\" >\n\n                        <RelativeLayout\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:layout_gravity=\"right\"\n                            android:layout_marginTop=\"5.0dip\" >\n\n                            <TextView\n                                android:id=\"@id/high_temperature_view\"\n                                android:layout_width=\"wrap_content\"\n                                android:layout_height=\"wrap_content\"\n                                android:layout_alignParentLeft=\"true\"\n                                android:shadowColor=\"#bf000000\"\n                                android:shadowDx=\"1.0\"\n                                android:shadowDy=\"1.0\"\n                                android:shadowRadius=\"3.0\"\n                                android:textColor=\"@android:color/white\"\n                                android:textSize=\"14.0dip\" />\n\n                            <ImageView\n                                android:layout_width=\"3.0dip\"\n                                android:layout_height=\"3.0dip\"\n                                android:layout_alignParentRight=\"true\"\n                                android:layout_alignParentTop=\"true\"\n                                android:layout_marginLeft=\"2.0dip\"\n                                android:layout_marginTop=\"3.0dip\"\n                                android:visibility=\"gone\" />\n                        </RelativeLayout>\n\n                        <RelativeLayout\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:layout_gravity=\"right\" >\n\n                            <TextView\n                                android:id=\"@id/low_temperature_view\"\n                                android:layout_width=\"wrap_content\"\n                                android:layout_height=\"wrap_content\"\n                                android:layout_alignParentLeft=\"true\"\n                                android:shadowColor=\"#bf000000\"\n                                android:shadowDx=\"1.0\"\n                                android:shadowDy=\"1.0\"\n                                android:shadowRadius=\"3.0\"\n                                android:textColor=\"@android:color/white\"\n                                android:textSize=\"14.0dip\" />\n\n                            <ImageView\n                                android:layout_width=\"3.0dip\"\n                                android:layout_height=\"3.0dip\"\n                                android:layout_alignParentRight=\"true\"\n                                android:layout_alignParentTop=\"true\"\n                                android:layout_marginLeft=\"3.0dip\"\n                                android:layout_marginTop=\"2.0dip\"\n                                android:visibility=\"gone\" />\n                        </RelativeLayout>\n                    </LinearLayout>\n                </LinearLayout>\n            </RelativeLayout>\n        </LinearLayout>\n\n        <LinearLayout\n            android:id=\"@+id/weather_location_layout\"\n            android:layout_width=\"140.0dip\"\n            android:layout_height=\"40.0dip\"\n            android:layout_alignParentBottom=\"true\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginBottom=\"5.0dip\"\n            android:layout_marginRight=\"5.0dip\"\n            android:gravity=\"bottom|right|center\"\n            android:orientation=\"horizontal\" >\n\n            <TextView\n                android:id=\"@id/location_view\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginRight=\"1.0dip\"\n                android:ellipsize=\"end\"\n                android:gravity=\"center\"\n                android:lines=\"1\"\n                android:shadowColor=\"#bf000000\"\n                android:shadowDx=\"1.0\"\n                android:shadowDy=\"1.0\"\n                android:shadowRadius=\"3.0\"\n                android:textColor=\"@android:color/white\"\n                android:textSize=\"18.0dip\" />\n\n            <ImageView\n                android:id=\"@+id/location_icon_view\"\n                android:layout_width=\"18.0dip\"\n                android:layout_height=\"18.0dip\"\n                android:layout_marginBottom=\"1.0dip\"\n                android:src=\"@drawable/current_loc_active_26x26\"\n                android:visibility=\"invisible\" />\n        </LinearLayout>\n    </RelativeLayout>\n\n</RelativeLayout>"
  },
  {
    "path": "WayHoo/res/values/arrays.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string-array name=\"special_solar_term_dates\">\n        <item>19840723|19840722|13</item>\n        <item>19841221|19841222|23</item>\n        <item>20130203|20130204|2</item>\n        <item>20130723|20130722|13</item>\n        <item>20131221|20131222|23</item>\n        <item>20140305|20140306|4</item>\n        <item>20150105|20150106|0</item>\n        <item>20161206|20161207|22</item>\n        <item>20170723|20170722|13</item>\n        <item>20171221|20171222|23</item>\n        <item>20180218|20180219|3</item>\n        <item>20180320|20180321|5</item>\n        <item>20190622|20190621|11</item>\n        <item>20200707|20200706|12</item>\n        <item>20200823|20200822|15</item>\n        <item>20201206|20201207|22</item>\n        <item>20220218|20220219|3</item>\n        <item>20230622|20230621|11</item>\n        <item>20231023|20231024|19</item>\n        <item>20231107|20231108|20</item>\n        <item>20240823|20240822|15</item>\n        <item>20260606|20260605|10</item>\n        <item>20300606|20300605|10</item>\n        <item>20350506|20350505|8</item>\n    </string-array>\n\n</resources>"
  },
  {
    "path": "WayHoo/res/values/attrs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <declare-styleable name=\"WeatherTypefacedTextView\">\n        <attr name=\"typeface\" format=\"string\" />\n    </declare-styleable>\n\n</resources>"
  },
  {
    "path": "WayHoo/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<resources>\r\n    <color name=\"yapps_purple_accent\">#ff6e329d</color>\r\n    <color name=\"red\">#77ff0000</color>\r\n    <color name=\"blue\">#770000ff</color>\r\n    <color name=\"green\">#7700ff00</color>\r\n    <color name=\"yellow\">#77ffff00</color>\r\n    <color name=\"black\">#ff000000</color>\r\n    <color name=\"gray\">#ff96989c</color>\r\n    <color name=\"screen_background_black\">#ff000000</color>\r\n    <color name=\"translucent_background\">#e0000000</color>\r\n    <color name=\"transparent_background\">#00000000</color>\r\n    <color name=\"almost_fully_transparent_background\">#01000000</color>\r\n    <color name=\"solid_red\">#ffff0000</color>\r\n    <color name=\"solid_blue\">#ff0000ff</color>\r\n    <color name=\"solid_green\">#ff00ff00</color>\r\n    <color name=\"solid_yellow\">#ffffff00</color>\r\n    <color name=\"solid_orange\">#ffff8040</color>\r\n    <color name=\"solid_white\">#ffffffff</color>\r\n    <color name=\"nav_text_purple\">#ffa78fa6</color>\r\n    <color name=\"label_txt_blue\">#ff9bd1ff</color>\r\n    <color name=\"popup_background_gray\">#ffdddddd</color>\r\n    <color name=\"login_background_dark\">#ff151715</color>\r\n    <color name=\"login_footer_dark\">#ff060609</color>\r\n    <color name=\"account_purple\">#ff6e329d</color>\r\n    <color name=\"account_dark_gray\">#ff4e4c57</color>\r\n    <color name=\"account_darker_gray\">#ff39373f</color>\r\n    <color name=\"account_bg_lavender\">#fff5f5f9</color>\r\n    <item type=\"color\" name=\"account_bg_purple\">@color/account_purple</item>\r\n    <color name=\"account_btn_txt_gray\">#ff3e3e3e</color>\r\n    <color name=\"account_btn_txt_purple\">#ffffffff</color>\r\n    <color name=\"account_btn_txt_purple_disabled\">#99ffffff</color>\r\n    <color name=\"account_btn_txt_gray_disabled\">#ff687080</color>\r\n    <color name=\"account_signin_txt_input\">#ff3e3e3e</color>\r\n    <color name=\"account_signin_txt_hint\">#ff91949d</color>\r\n    <color name=\"account_signin_txt_label\">#ff5f5f5f</color>\r\n    <color name=\"account_signin_error_lavender\">#fff45611</color>\r\n    <color name=\"account_signin_error_purple\">#ffff8b2d</color>\r\n    <color name=\"account_almost_transparent\">#01f5f5f9</color>\r\n    <color name=\"account_splash_mask\">#88ffffff</color>\r\n    <color name=\"account_sso_text\">#ff3f3f3f</color>\r\n    <color name=\"account_settings_items_text\">#ff19191a</color>\r\n    <item type=\"color\" name=\"account_loginScreen_background\">@*android:color/white</item>\r\n    <color name=\"account_header_text\">#ff19191a</color>\r\n    <item type=\"color\" name=\"account_button_text\">@color/yapps_purple_accent</item>\r\n    <color name=\"account_edit_hint\">#ff7e7d80</color>\r\n    <color name=\"account_subtitle\">#ff7e7d80</color>\r\n    <color name=\"account_legal\">#ff19191a</color>\r\n    <color name=\"account_legal_hightlight\">#ff0074b3</color>\r\n    <item type=\"color\" name=\"account_forgot_password\">@color/yapps_purple_accent</item>\r\n    <item type=\"color\" name=\"account_forgot_password_hightlight\">@color/yapps_purple_accent</item>\r\n    <color name=\"account_signup_actionbar_backButton_pressed\">#666230b4</color>\r\n    <color name=\"account_signin_bg\">#ff493747</color>\r\n    <item type=\"color\" name=\"account_splash_bg\">@color/account_loginScreen_background</item>\r\n    <color name=\"account_splash_version_txt_color\">#ff2a0827</color>\r\n    <color name=\"account_signin_version_txt_color\">#ff2a0827</color>\r\n    <color name=\"account_service_item_divider\">#ff555555</color>\r\n    <color name=\"account_sso_user_card_background\">#ffffffff</color>\r\n    <color name=\"account_sso_user_card_active_background\">#ffffffff</color>\r\n    <color name=\"account_sso_signout_button_background_default\">#ffed4133</color>\r\n    <color name=\"account_sso_signout_button_background_pressed\">#ffd93e31</color>\r\n    <color name=\"account_sso_signout_button_text\">#ffffffff</color>\r\n    <color name=\"account_sso_add_account_button_background_default\">#ff6e329d</color>\r\n    <color name=\"account_sso_add_account_button_background_pressed\">#ff602d88</color>\r\n    <color name=\"account_sso_add_account_button_text\">#ffffffff</color>\r\n    <color name=\"account_sso_action_bar_background\">#b0ffffff</color>\r\n    <color name=\"sidebar_text_light\">#ff000000</color>\r\n    <color name=\"sidebar_text_dark\">#ffffffff</color>\r\n    <item type=\"color\" name=\"sidebar_identity_text_color_light\">@color/sidebar_text_light</item>\r\n    <color name=\"sidebar_identity_text_color_dark\">#ffdedede</color>\r\n    <color name=\"sidebar_identity_subtitle_text_color_light\">#ff565657</color>\r\n    <color name=\"sidebar_identity_subtitle_text_color_dark\">#ffadadad</color>\r\n    <color name=\"sidebar_header_text_color_light\">#ff4c4c4d</color>\r\n    <color name=\"sidebar_header_text_color_dark\">#ff848484</color>\r\n    <item type=\"color\" name=\"sidebar_item_text_color_light\">@color/sidebar_text_light</item>\r\n    <item type=\"color\" name=\"sidebar_item_text_color_dark\">@color/sidebar_text_dark</item>\r\n    <color name=\"sidebar_badge_text_color_light\">#ff4c4c4d</color>\r\n    <color name=\"sidebar_badge_text_color_dark\">#ff848484</color>\r\n    <color name=\"sidebar_accessory_text_color_light\">#ff4c4c4d</color>\r\n    <color name=\"sidebar_accessory_text_color_dark\">#ff848484</color>\r\n    <color name=\"sidebar_footer_text_color_light\">#ffabaeb7</color>\r\n    <item type=\"color\" name=\"sidebar_footer_text_color_dark\">@color/sidebar_text_dark</item>\r\n    <color name=\"sidebar_item_background_light\">#ffffffff</color>\r\n    <color name=\"sidebar_item_background_dark\">#ff272627</color>\r\n    <color name=\"sidebar_item_expanded_background_light\">#fff6f5fb</color>\r\n    <color name=\"sidebar_item_expanded_background_dark\">#ff1f1e1f</color>\r\n    <color name=\"sidebar_item_expanded_background_activated_light\">#ffe7dcf0</color>\r\n    <color name=\"sidebar_item_expanded_background_activated_dark\">#ff433151</color>\r\n    <color name=\"sidebar_item_separator_light\">#ffe3e1e7</color>\r\n    <color name=\"sidebar_item_separator_dark\">#ff212021</color>\r\n    <color name=\"sidebar_item_background_activated_light\">#ffeae0f2</color>\r\n    <color name=\"sidebar_item_background_activated_dark\">#ff4c365d</color>\r\n    <color name=\"sidebar_header_background_light\">#fff2f0f6</color>\r\n    <color name=\"sidebar_header_background_dark\">#ff121213</color>\r\n    <color name=\"sidebar_footer_background_light\">#fff2f2f5</color>\r\n    <color name=\"sidebar_footer_background_dark\">#ff1d1d1d</color>\r\n    <color name=\"sidebar_identity_background_light\">#fff9f9fc</color>\r\n    <color name=\"sidebar_identity_background_dark\">#ff121212</color>\r\n    <color name=\"sidebar_subnav_item_background_light\">#ffffffff</color>\r\n    <color name=\"sidebar_subnav_item_background_dark\">#ff272627</color>\r\n    <item type=\"color\" name=\"sidebar_subnav_item_background_activated_light\">@color/sidebar_item_background_activated_light</item>\r\n    <item type=\"color\" name=\"sidebar_subnav_item_background_activated_dark\">@color/sidebar_item_background_activated_dark</item>\r\n    <color name=\"sidebar_accessory_background_light\">#00000000</color>\r\n    <color name=\"sidebar_accessory_background_dark\">#00000000</color>\r\n    <color name=\"sidebar_header_accessory_background_light\">#ffd5d5da</color>\r\n    <color name=\"sidebar_header_accessory_background_pressed_light\">#ffc0c0c5</color>\r\n    <color name=\"sidebar_header_accessory_background_dark\">#ff3b3a3b</color>\r\n    <color name=\"sidebar_header_accessory_background_pressed_dark\">#ff1b1b1b</color>\r\n    <color name=\"customview_toolbar_1\">#ff000000</color>\r\n    <color name=\"customview_toolbar_2\">#ff003ddd</color>\r\n    <color name=\"customview_toolbar_3\">#ff7c4912</color>\r\n    <color name=\"customview_toolbar_4\">#ff008b0d</color>\r\n    <color name=\"customview_toolbar_5\">#ff454545</color>\r\n    <color name=\"customview_toolbar_6\">#ff00c0dd</color>\r\n    <color name=\"customview_toolbar_7\">#fff2740d</color>\r\n    <color name=\"customview_toolbar_8\">#ffef1dc0</color>\r\n    <color name=\"customview_toolbar_9\">#ff7c00e9</color>\r\n    <color name=\"customview_toolbar_10\">#ffc80d0d</color>\r\n    <color name=\"semi_trans_background\">#af000000</color>\r\n    <color name=\"abs__background_holo_dark\">#ff000000</color>\r\n    <color name=\"abs__background_holo_light\">#fff3f3f3</color>\r\n    <item type=\"color\" name=\"abs__bright_foreground_holo_dark\">@color/abs__background_holo_light</item>\r\n    <item type=\"color\" name=\"abs__bright_foreground_holo_light\">@color/abs__background_holo_dark</item>\r\n    <color name=\"abs__bright_foreground_disabled_holo_dark\">#ff4c4c4c</color>\r\n    <color name=\"abs__bright_foreground_disabled_holo_light\">#ffb2b2b2</color>\r\n    <item type=\"color\" name=\"abs__bright_foreground_inverse_holo_dark\">@color/abs__bright_foreground_holo_light</item>\r\n    <item type=\"color\" name=\"abs__bright_foreground_inverse_holo_light\">@color/abs__bright_foreground_holo_dark</item>\r\n    <color name=\"abs__holo_blue_light\">#ff33b5e5</color>\r\n    <item type=\"color\" name=\"common_signin_btn_dark_text_default\">@*android:color/white</item>\r\n    <item type=\"color\" name=\"common_signin_btn_dark_text_pressed\">@*android:color/white</item>\r\n    <color name=\"common_signin_btn_dark_text_disabled\">#ffaaaaaa</color>\r\n    <item type=\"color\" name=\"common_signin_btn_dark_text_focused\">@*android:color/white</item>\r\n    <color name=\"common_signin_btn_light_text_default\">#ff737373</color>\r\n    <item type=\"color\" name=\"common_signin_btn_light_text_pressed\">@*android:color/white</item>\r\n    <color name=\"common_signin_btn_light_text_disabled\">#ffaaaaaa</color>\r\n    <color name=\"common_signin_btn_light_text_focused\">#ff737373</color>\r\n    <color name=\"common_signin_btn_default_background\">#ffdd4b39</color>\r\n    <color name=\"common_action_bar_splitter\">#ffd2d2d2</color>\r\n    <color name=\"alert_translucent_background\">#cc000000</color>\r\n    <color name=\"alert_translucent_red\">#b3800606</color>\r\n    <color name=\"signin_bg\">#ff493747</color>\r\n    <color name=\"splash_bg\">#ff60205a</color>\r\n    <color name=\"splash_version_txt_color\">#ff2a0827</color>\r\n    <color name=\"widget_setting_black\">#ff181818</color>\r\n    <color name=\"widget_setting_sub_black\">#ff1f1e1f</color>\r\n    <color name=\"widget_setting_grey\">#ff272627</color>\r\n    <color name=\"widget_setting_divider_black\">#ff212121</color>\r\n    <color name=\"toolbar_black\">#ff000000</color>\r\n    <color name=\"toolbar_blue\">#ff003ddd</color>\r\n    <color name=\"toolbar_brown\">#ff7c4912</color>\r\n    <color name=\"toolbar_green\">#ff008b0d</color>\r\n    <color name=\"toolbar_grey\">#ff454545</color>\r\n    <color name=\"toolbar_light_blue\">#ff00c0dd</color>\r\n    <color name=\"toolbar_orange\">#fff2740d</color>\r\n    <color name=\"toolbar_pink\">#ffef1dc0</color>\r\n    <color name=\"toolbar_purple\">#ff7c00e9</color>\r\n    <color name=\"toolbar_red\">#ffc80d0d</color>\r\n    <color name=\"transparent\">#00000000</color>\r\n    <color name=\"widget_configure_divider\">#ff808080</color>\r\n    <color name=\"widget_small_default_color\">#5019304e</color>\r\n    <color name=\"text_shadow\">#88000000</color>\r\n    <color name=\"splash_text_shadow\">#66000000</color>\r\n    <color name=\"flickr_attribution\">#ffdcdcdc</color>\r\n    <color name=\"forecast_gray\">#88ffffff</color>\r\n    <color name=\"map_placeholder_gray\">#ff808080</color>\r\n    <color name=\"util_row_highlight_color\">#ff373637</color>\r\n    <color name=\"util_bg_color\">#ff272627</color>\r\n    <color name=\"util_header_color\">#ff181818</color>\r\n    <color name=\"util_subheader_color\">#ff1f1e1f</color>\r\n    <color name=\"util_subheader_text_color\">#ff848484</color>\r\n    <color name=\"util_explain_text_color\">#ffa2a1a1</color>\r\n    <color name=\"util_list_separator_color\">#ff212021</color>\r\n    <color name=\"util_login_panel_color\">#ff363536</color>\r\n    <color name=\"util_fb_friends_color\">#ffc6c6c6</color>\r\n    <color name=\"util_toggle_on_color\">#ff3b8fd9</color>\r\n    <color name=\"util_toggle_off_color\">#ff727272</color>\r\n    <color name=\"util_toggle_stroke_color\">#ff5a5a5a</color>\r\n    <color name=\"util_cursor_color\">#ffeeeeee</color>\r\n    <color name=\"search_highlight\">#ff789aa7</color>\r\n    <color name=\"search_white\">#ffffffff</color>\r\n    <color name=\"daily_white\">#ffffffff</color>\r\n    <color name=\"daily_white_medium_opacity\">#77ffffff</color>\r\n    <color name=\"search_yellow\">#fffff581</color>\r\n    <color name=\"daily_blue\">#ff182c39</color>\r\n    <color name=\"forecast_bg_startcolor\">#00416173</color>\r\n    <color name=\"forecast_bg_endcolor\">#3f416173</color>\r\n    <color name=\"gray_background\">#ff272627</color>\r\n</resources>\r\n"
  },
  {
    "path": "WayHoo/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<resources>\r\n\r\n    <dimen name=\"uv_listPreferredItemHeightSmall\">64.0dip</dimen>\r\n    <dimen name=\"account_logo_bottomMargin\">20.0dip</dimen>\r\n    <dimen name=\"account_loginScreen_topPadding\">30.0dip</dimen>\r\n    <dimen name=\"account_loginScreen_bottomPadding\">23.0dip</dimen>\r\n    <dimen name=\"account_loginScreen_leftPadding\">20.0dip</dimen>\r\n    <dimen name=\"account_loginScreen_rightPadding\">20.0dip</dimen>\r\n    <dimen name=\"account_loginScreen_contentMaxWidth\">304.0dip</dimen>\r\n    <dimen name=\"account_loginScreen_imagePadding\">18.0dip</dimen>\r\n    <dimen name=\"account_loginScreen_errBottomPadding\">12.0dip</dimen>\r\n    <dimen name=\"account_loginScreen_buttonTextSize\">22.0sp</dimen>\r\n    <dimen name=\"account_slcScreen_buttonTextSize\">18.0sp</dimen>\r\n    <dimen name=\"account_loginScreen_buttonTopPadding\">6.0dip</dimen>\r\n    <dimen name=\"account_loginScreen_buttonBottomPadding\">6.0dip</dimen>\r\n    <dimen name=\"account_loginScreen_editTextSize\">17.0sp</dimen>\r\n    <dimen name=\"account_loginScreen_lableSmallTextSize\">14.0sp</dimen>\r\n    <dimen name=\"account_loginScreen_lableMediumTextSize\">16.0sp</dimen>\r\n    <dimen name=\"account_loginScreen_lableLargeTextSize\">18.0sp</dimen>\r\n    <dimen name=\"account_loginScreen_editTextPadding\">6.0dip</dimen>\r\n    <dimen name=\"account_loginScreen_loginEditTextPadding\">6.0dip</dimen>\r\n    <dimen name=\"account_loginScreen_clearIconPadding\">6.0dip</dimen>\r\n    <dimen name=\"account_loginScreen_itemsLeftMargin\">8.0dip</dimen>\r\n    <dimen name=\"account_loginScreen_itemsRightMargin\">8.0dip</dimen>\r\n    <dimen name=\"account_loginScreen_tosTopPadding\">24.0dip</dimen>\r\n    <dimen name=\"account_loginScreen_arLinkTopPadding\">16.0dip</dimen>\r\n    <dimen name=\"account_loginScreen_arLinkBottomPadding\">6.0dip</dimen>\r\n    <dimen name=\"account_sso_element_spacing\">10.0dip</dimen>\r\n    <dimen name=\"account_sso_picture_size\">70.0dip</dimen>\r\n    <dimen name=\"account_sso_item_min_height\">70.0dip</dimen>\r\n    <dimen name=\"account_sso_checkbox_width\">18.0dip</dimen>\r\n    <dimen name=\"account_sso_checkbox_user_card_spacing\">10.0dip</dimen>\r\n    <dimen name=\"account_sso_checkbox_tap_area_expansion\">12.0dip</dimen>\r\n    <dimen name=\"account_sso_checkbox_induced_offset\">28.0dip</dimen>\r\n    <dimen name=\"account_sso_margin\">41.0dip</dimen>\r\n    <dimen name=\"account_sso_left_margin\">13.0dip</dimen>\r\n    <dimen name=\"account_util_minus_one_px\">-1.0px</dimen>\r\n    <dimen name=\"SidebarLayout_DefaultMenuWidth\">280.0dip</dimen>\r\n    <dimen name=\"SidebarLayout_DefaultBezelWidth\">40.0dip</dimen>\r\n    <dimen name=\"SidebarLayout_DefaultMenuItemHeight\">44.0dip</dimen>\r\n    <dimen name=\"SliderLayout_defaultMenuWidth\">280.0dip</dimen>\r\n    <dimen name=\"pull_to_refresh_progress_width\">72.0dip</dimen>\r\n    <dimen name=\"pull_to_refresh_progress_height\">72.0dip</dimen>\r\n    <dimen name=\"pull_to_refresh_progress_marginLeft\">30.0dip</dimen>\r\n    <dimen name=\"pull_to_refresh_progress_marginRight\">20.0dip</dimen>\r\n    <dimen name=\"pull_to_refresh_paddingTop\">10.0dip</dimen>\r\n    <dimen name=\"pull_to_refresh_paddingBottom\">10.0dip</dimen>\r\n    <dimen name=\"customviews_color_picker_btn_size\">50.0dip</dimen>\r\n    <dimen name=\"customviews_color_picker_btn_padding\">10.0dip</dimen>\r\n    <dimen name=\"abs__config_prefDialogWidth\">320.0dip</dimen>\r\n    <dimen name=\"abs__action_bar_default_height\">48.0dip</dimen>\r\n    <dimen name=\"abs__action_bar_icon_vertical_padding\">8.0dip</dimen>\r\n    <dimen name=\"abs__action_bar_title_text_size\">18.0dip</dimen>\r\n    <dimen name=\"abs__action_bar_subtitle_text_size\">14.0dip</dimen>\r\n    <dimen name=\"abs__action_bar_subtitle_top_margin\">-3.0dip</dimen>\r\n    <dimen name=\"abs__action_bar_subtitle_bottom_margin\">5.0dip</dimen>\r\n    <dimen name=\"abs__action_button_min_width\">56.0dip</dimen>\r\n    <dimen name=\"abs__alert_dialog_title_height\">64.0dip</dimen>\r\n\r\n    <item name=\"abs__dialog_min_width_major\" type=\"dimen\">65.0%</item>\r\n    <item name=\"abs__dialog_min_width_minor\" type=\"dimen\">95.00001%</item>\r\n\r\n    <dimen name=\"abs__dropdownitem_text_padding_left\">8.0dip</dimen>\r\n    <dimen name=\"abs__dropdownitem_text_padding_right\">8.0dip</dimen>\r\n    <dimen name=\"abs__dropdownitem_icon_width\">32.0dip</dimen>\r\n    <dimen name=\"abs__search_view_text_min_width\">160.0dip</dimen>\r\n    <dimen name=\"abs__search_view_preferred_width\">320.0dip</dimen>\r\n    <dimen name=\"splash_screen_weather_text_size\">24.0sp</dimen>\r\n    <dimen name=\"splash_screen_margin\">170.0dip</dimen>\r\n    <dimen name=\"weather_signin_explain_textview_textsize\">22.0sp</dimen>\r\n    <dimen name=\"weather_signin_show_share_button_textsize\">22.0sp</dimen>\r\n    <dimen name=\"weather_signin_edit_location_marginright\">40.0sp</dimen>\r\n    <dimen name=\"weather_signin_edit_location_marginleft\">40.0sp</dimen>\r\n    <dimen name=\"weather_topbar_padding\">48.0dip</dimen>\r\n    <dimen name=\"ptr_padding\">160.0dip</dimen>\r\n    <dimen name=\"background_image_vertical_extra\">100.0dip</dimen>\r\n    <dimen name=\"weather_page_margin\">4.0dip</dimen>\r\n    <dimen name=\"weather_weather_info_marginleft\">10.0dip</dimen>\r\n    <dimen name=\"weather_weather_info_marginright\">10.0dip</dimen>\r\n    <dimen name=\"weather_weather_info_paddingtop\">2.0dip</dimen>\r\n    <dimen name=\"weather_weather_info_paddingleft\">8.0dip</dimen>\r\n    <dimen name=\"weather_weather_info_paddingright\">7.0dip</dimen>\r\n    <dimen name=\"weather_weather_info_paddingbottom\">2.0dip</dimen>\r\n    <dimen name=\"weather_weather_info_flippertext_textsize\">24.0sp</dimen>\r\n    <dimen name=\"weather_temperature_desc\">16.0sp</dimen>\r\n    <dimen name=\"weather_local_time\">12.0sp</dimen>\r\n    <dimen name=\"weather_last_update_time_stamp_textsize\">12.0sp</dimen>\r\n    <dimen name=\"weather_low_temperature_textsize\">14.0sp</dimen>\r\n    <dimen name=\"weather_temperature_textsize\">40.0sp</dimen>\r\n    <dimen name=\"weather_high_temperature_textsize\">14.0sp</dimen>\r\n    <dimen name=\"weather_extended_weather_info\">160.0dip</dimen>\r\n    <dimen name=\"weather_extended_forecast_view_layout_height\">160.0dip</dimen>\r\n    <dimen name=\"weather_weather_icon_layout_height\">60.0dip</dimen>\r\n    <dimen name=\"weather_alert_type_layout_portrait_height\">55.0dip</dimen>\r\n    <dimen name=\"weather_alert_type_layout_landscape_height\">45.0dip</dimen>\r\n    <dimen name=\"weather_alert_portrait_top_margin\">60.0dip</dimen>\r\n    <dimen name=\"weather_alert_portrait_expanded_top_margin\">15.0dip</dimen>\r\n    <dimen name=\"weather_alert_landscape_top_margin\">75.0dip</dimen>\r\n    <dimen name=\"weather_alert_landscape_expanded_top_margin\">75.0dip</dimen>\r\n    <dimen name=\"weather_alert_icon_TouchPaddingTop\">10.0dip</dimen>\r\n    <dimen name=\"weather_alert_icon_TouchPaddingBottom\">10.0dip</dimen>\r\n    <dimen name=\"weather_alert_icon_TouchPaddingLeft\">10.0dip</dimen>\r\n    <dimen name=\"weather_alert_icon_TouchPaddingRight\">80.0dip</dimen>\r\n    <dimen name=\"weather_last_update_time_stamp_marginright\">0.0dip</dimen>\r\n    <dimen name=\"H1_phone\">22.0sp</dimen>\r\n    <dimen name=\"H2_phone\">16.0sp</dimen>\r\n    <dimen name=\"H3_phone\">12.0sp</dimen>\r\n\r\n    <item name=\"drag_list_divider_height\" type=\"dimen\">@dimen/margin_sm</item>\r\n\r\n    <dimen name=\"topbar_icon_size\">22.0dip</dimen>\r\n    <dimen name=\"current_condition_icon_size\">36.0dip</dimen>\r\n    <dimen name=\"current_condition_height\">180.0dip</dimen>\r\n    <dimen name=\"current_temperature_phone_textsize\">150.0sp</dimen>\r\n    <dimen name=\"current_condition_copyright_phone_textsize\">12.0sp</dimen>\r\n    <dimen name=\"current_condition_icon_right_margin\">4.0dip</dimen>\r\n    <dimen name=\"temp_icon_size\">30.0dip</dimen>\r\n    <dimen name=\"degree_icon_size\">26.0dip</dimen>\r\n    <dimen name=\"flickr_icon_padding_bottom\">0.0dip</dimen>\r\n    <dimen name=\"temp_high_icon_padding_bottom\">0.0dip</dimen>\r\n    <dimen name=\"temp_icon_padding\">14.0dip</dimen>\r\n    <dimen name=\"high_low_text_margin_bottom\">-5.0dip</dimen>\r\n    <dimen name=\"current_condition_icon_margin_bottom\">2.0dip</dimen>\r\n    <dimen name=\"hourly_icon_size\">29.0dip</dimen>\r\n    <dimen name=\"daily_icon_size\">32.0dip</dimen>\r\n    <dimen name=\"daily_forecast_item_height\">40.0dip</dimen>\r\n    <dimen name=\"hourly_forecast_item_height\">24.0dip</dimen>\r\n    <dimen name=\"wunder_logo_item_height\">45.0dip</dimen>\r\n    <dimen name=\"day_spacing\">16.0dip</dimen>\r\n    <dimen name=\"details_card_height\">411.0dip</dimen>\r\n    <dimen name=\"details_icon_size\">92.0dip</dimen>\r\n    <dimen name=\"precipitation_card_height\">192.0dip</dimen>\r\n    <dimen name=\"precipitation_item_height\">110.0dip</dimen>\r\n    <dimen name=\"precipitation_item_width\">112.0dip</dimen>\r\n    <dimen name=\"time_text_size\">14.0sp</dimen>\r\n    <dimen name=\"sunmoon_card_height\">240.0dip</dimen>\r\n    <dimen name=\"sun_view_height\">115.0dip</dimen>\r\n    <dimen name=\"sun_arc_width\">140.0dip</dimen>\r\n    <dimen name=\"sun_arc_leftOffset\">20.0dip</dimen>\r\n    <dimen name=\"sun_arc_topOffset\">35.0dip</dimen>\r\n    <dimen name=\"sun_ball_radius\">4.0dip</dimen>\r\n    <dimen name=\"moon_icon_size\">20.0dip</dimen>\r\n    <dimen name=\"sunview_margin\">4.0dip</dimen>\r\n    <dimen name=\"negative_sunview_margin\">-4.0dip</dimen>\r\n    <dimen name=\"wind_view_height\">110.0dip</dimen>\r\n    <dimen name=\"big_pole_x\">38.0dip</dimen>\r\n    <dimen name=\"small_pole_x\">78.0dip</dimen>\r\n    <dimen name=\"pole_bottom_y\">98.0dip</dimen>\r\n    <dimen name=\"pressure_line_y\">73.0dip</dimen>\r\n    <dimen name=\"pressure_text_y\">4.0dip</dimen>\r\n    <dimen name=\"wind_text_x\">6.0dip</dimen>\r\n    <dimen name=\"wind_text_y\">29.0dip</dimen>\r\n    <dimen name=\"barometerTickSpacing\">9.0dip</dimen>\r\n    <dimen name=\"map_image_height\">192.0dip</dimen>\r\n    <dimen name=\"map_control_margin\">30.0dip</dimen>\r\n    <dimen name=\"map_country_text_padding\">6.0dip</dimen>\r\n    <dimen name=\"ptr_indicator_right_padding\">10.0dip</dimen>\r\n    <dimen name=\"ptr_indicator_corner_radius\">12.0dip</dimen>\r\n    <dimen name=\"ptr_indicator_internal_padding\">4.0dip</dimen>\r\n    <dimen name=\"ptr_header_footer_left_right_padding\">24.0dip</dimen>\r\n    <dimen name=\"ptr_header_footer_top_bottom_padding\">12.0dip</dimen>\r\n    <dimen name=\"ptr_margin_sun_left\">58.0dip</dimen>\r\n    <dimen name=\"ptr_margin_sun_right\">24.0dip</dimen>\r\n    <dimen name=\"ptr_height\">64.0dip</dimen>\r\n    <dimen name=\"ptr_margin_yahoo_top\">16.0dip</dimen>\r\n    <dimen name=\"ptr_font_updated\">12.0dip</dimen>\r\n    <dimen name=\"ptr_center_offset\">34.0dip</dimen>\r\n    <dimen name=\"util_margin_list_separator\">8.0dip</dimen>\r\n    <dimen name=\"util_margin_social_label\">8.0dip</dimen>\r\n    <dimen name=\"util_margin_med\">13.0dip</dimen>\r\n    <dimen name=\"util_margin_lg\">24.0dip</dimen>\r\n    <dimen name=\"util_margin_panel\">12.0dip</dimen>\r\n    <dimen name=\"util_margin_panel_small\">10.0dip</dimen>\r\n    <dimen name=\"util_margin_explain_raise\">-3.0dip</dimen>\r\n    <dimen name=\"util_margin_editloc_city\">52.0dip</dimen>\r\n    <dimen name=\"util_row_height_lg\">44.0dip</dimen>\r\n    <dimen name=\"util_row_height_med\">41.0dip</dimen>\r\n    <dimen name=\"util_row_height_sm\">30.0dip</dimen>\r\n    <dimen name=\"util_toggle_width\">54.0dip</dimen>\r\n    <dimen name=\"util_toggle_height\">27.0dip</dimen>\r\n    <dimen name=\"util_font_lg\">17.0sp</dimen>\r\n    <dimen name=\"util_font_med\">12.0sp</dimen>\r\n    <dimen name=\"util_font_sm\">10.0sp</dimen>\r\n    <dimen name=\"util_font_xs\">8.0sp</dimen>\r\n    <dimen name=\"util_font_sm_heading\">11.0sp</dimen>\r\n    <dimen name=\"util_delete_size\">20.0dip</dimen>\r\n    <dimen name=\"util_fb_toggle_size\">17.0dip</dimen>\r\n    <dimen name=\"util_fb_toggle_touch_size\">48.0dip</dimen>\r\n    <dimen name=\"util_search_size\">16.0dip</dimen>\r\n    <dimen name=\"util_login_panel_height\">108.0dip</dimen>\r\n    <dimen name=\"util_list_separator_height\">2.0dip</dimen>\r\n    <dimen name=\"util_social_size\">21.0dip</dimen>\r\n    <dimen name=\"util_margin_yahoo_left\">34.0dip</dimen>\r\n    <dimen name=\"util_toggle_corner_radius\">2.0dip</dimen>\r\n    <dimen name=\"util_toggle_stroke_width\">1.0px</dimen>\r\n    <dimen name=\"widget_margin\">2.5dip</dimen>\r\n    <dimen name=\"widget_marginTopBottom\">5.0dip</dimen>\r\n    <dimen name=\"widget_padding\">10.0dip</dimen>\r\n    <dimen name=\"widget_padding_hourly_top\">7.0dip</dimen>\r\n    <dimen name=\"widget_padding_hourly_bottom\">9.0dip</dimen>\r\n    <dimen name=\"widget_paddingTopBottom\">7.0dip</dimen>\r\n    <dimen name=\"widget_small_padding\">5.0dip</dimen>\r\n    <dimen name=\"widget_setting_item_height\">48.5dip</dimen>\r\n    <dimen name=\"widget_setting_item_height_no_border\">48.0dip</dimen>\r\n    <dimen name=\"widget_setting_fading_edge_length\">10.0dip</dimen>\r\n    <dimen name=\"widget_setting_title_height\">43.0dip</dimen>\r\n    <dimen name=\"widget_setting_left_right_padding\">15.0dip</dimen>\r\n    <dimen name=\"widget_arrow_min_max_bottom_padding\">8.0dip</dimen>\r\n    <dimen name=\"widget_hourly_icon_margin_left\">17.0dip</dimen>\r\n    <dimen name=\"widget_hourly_icon_size\">29.0dip</dimen>\r\n    <dimen name=\"widget_hourly_icon_margin\">15.0dip</dimen>\r\n    <dimen name=\"widget_hourly_container_margin\">15.0dip</dimen>\r\n    <dimen name=\"widget_alert_view_height\">20.0dip</dimen>\r\n    <dimen name=\"widget_alert_icon_right_margin\">2.5dip</dimen>\r\n    <dimen name=\"widget_button_margin_bottom\">15.0dip</dimen>\r\n    <dimen name=\"widget_refresh_icon_size\">13.5dip</dimen>\r\n    <dimen name=\"widget_checkbox_size\">18.0dip</dimen>\r\n\r\n</resources>"
  },
  {
    "path": "WayHoo/res/values/drawables.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <item name=\"main_info_background_normal\" type=\"drawable\">#ffd9e5f0</item>\n    <item name=\"list_background_press\" type=\"drawable\">#ffcfdae3</item>\n    <item name=\"list_background_normal\" type=\"drawable\">#ffd9e5f0</item>\n    <item name=\"click_unselect_background\" type=\"drawable\">#00000000</item>\n    <item name=\"click_select_background\" type=\"drawable\">#ff0271ab</item>\n    <item name=\"transparent_background\" type=\"drawable\">#00000000</item>\n    <item name=\"screen_background_black\" type=\"drawable\">#ff000000</item>\n    <item name=\"translucent_background\" type=\"drawable\">#ce000000</item>\n    <item name=\"widget_translucent_background\" type=\"drawable\">#be01101c</item>\n    <item name=\"btn_background_on\" type=\"drawable\">#ffd9e5f0</item>\n    <item name=\"btn_background\" type=\"drawable\">#ff7fcaff</item>\n    <item name=\"problem_background_normal\" type=\"drawable\">#ffffffff</item>\n    <item name=\"problem_background_pressed\" type=\"drawable\">#ff2ab9ff</item>\n    <item name=\"review_content_bg_color\" type=\"drawable\">#ffe1ecf3</item>\n    <item name=\"netdisk_actionbar_btn_select_background\" type=\"drawable\">#ff0271ab</item>\n    <item name=\"transparent\" type=\"drawable\">#00000000</item>\n    <item name=\"home_setting_background\" type=\"drawable\">#48ffffff</item>\n    <item name=\"main_setting_background\" type=\"drawable\">#ff323538</item>\n    <item name=\"setting_item_selected\" type=\"drawable\">#33000000</item>\n    \n    \n    <item name=\"sidemenu_category\" type=\"drawable\">#90000000</item>\n    <item name=\"sidemenu_item_normal\" type=\"drawable\">#30000000</item>\n\n</resources>"
  },
  {
    "path": "WayHoo/res/values/ids.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <item name=\"details_icon\" type=\"id\"/>\n\n</resources>"
  },
  {
    "path": "WayHoo/res/values/integers.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <integer name=\"dragged_item_fade_out_duration\">150</integer>\n</resources>"
  },
  {
    "path": "WayHoo/res/values/menudrawer_attrs.xml",
    "content": "<resources>\n\n    <!-- Reference to a style for the menu drawer. -->\n    <attr name=\"menuDrawerStyle\" format=\"reference\" />\n\n    <!-- Styleables used for styling the menu drawer. -->\n    <declare-styleable name=\"MenuDrawer\">\n\n        <!-- Drawable to use for the background of the content. -->\n        <attr name=\"mdContentBackground\" format=\"reference\" />\n\n        <!-- Drawable to use for the background of the menu. -->\n        <attr name=\"mdMenuBackground\" format=\"reference\" />\n\n        <!-- The size of the menu. -->\n        <attr name=\"mdMenuSize\" format=\"dimension\" />\n\n        <!-- Drawable used as indicator for the active view. -->\n        <attr name=\"mdActiveIndicator\" format=\"reference\" />\n\n        <!-- Defines whether the content will have a dropshadow onto the menu. Default is true. -->\n        <attr name=\"mdDropShadowEnabled\" format=\"boolean\" />\n\n        <!-- The size of the drop shadow. Default is 6dp -->\n        <attr name=\"mdDropShadowSize\" format=\"dimension\" />\n\n        <!-- The color of the drop shadow. Default is #FF000000. -->\n        <attr name=\"mdDropShadowColor\" format=\"color\" />\n\n        <!-- Drawable used for the drop shadow. -->\n        <attr name=\"mdDropShadow\" format=\"reference\" />\n\n        <!-- The touch bezel size. -->\n        <attr name=\"mdTouchBezelSize\" format=\"dimension\" />\n\n        <!-- Whether the indicator should be animated between active views. -->\n        <attr name=\"mdAllowIndicatorAnimation\" format=\"boolean\" />\n\n        <!-- The maximum animation duration -->\n        <attr name=\"mdMaxAnimationDuration\" format=\"integer\" />\n\n        <!-- Drawable that replaces the up indicator -->\n        <attr name=\"mdSlideDrawable\" format=\"reference\" />\n\n        <!-- String to use as the up indicators content description when the drawer is open -->\n        <attr name=\"mdDrawerOpenUpContentDescription\" format=\"string\" />\n\n        <!-- String to use as the up indicators content description when the drawer is closed -->\n        <attr name=\"mdDrawerClosedUpContentDescription\" format=\"string\" />\n\n        <!-- Whether an overlay should be drawn as the drawer is opened and closed -->\n        <attr name=\"mdDrawOverlay\" format=\"boolean\" />\n\n        <!-- The position of the drawer -->\n        <attr name=\"mdPosition\" format=\"enum\">\n            <enum name=\"left\" value=\"0\" />\n            <enum name=\"top\" value=\"1\" />\n            <enum name=\"right\" value=\"2\" />\n            <enum name=\"bottom\" value=\"3\" />\n            <enum name=\"start\" value=\"4\" />\n            <enum name=\"end\" value=\"5\" />\n        </attr>\n    </declare-styleable>\n\n</resources>\n"
  },
  {
    "path": "WayHoo/res/values/menudrawer_colors.xml",
    "content": "<resources>\n\n    <!-- The default background of the menu. -->\n    <color name=\"md__defaultBackground\">#00000000</color>\n\n</resources>\n"
  },
  {
    "path": "WayHoo/res/values/menudrawer_ids.xml",
    "content": "<resources>\n\n    <!-- ID used when defining the content layout in XML. -->\n    <item name=\"mdContent\" type=\"id\" />\n\n    <!-- ID used when defining the menu layout in XML. -->\n    <item name=\"mdMenu\" type=\"id\" />\n\n    <!-- The ID of the content container. -->\n    <item name=\"md__content\" type=\"id\" />\n\n    <!-- The ID of the menu container. -->\n    <item name=\"md__menu\" type=\"id\" />\n\n    <!-- The ID of the drawer. -->\n    <item name=\"md__drawer\" type=\"id\" />\n\n    <!-- Used with View#setTag(int) to specify a position for the active view. -->\n    <item name=\"mdActiveViewPosition\" type=\"id\" />\n\n    <item name=\"md__translationX\" type=\"id\" />\n\n    <item name=\"md__translationY\" type=\"id\" />\n</resources>\n"
  },
  {
    "path": "WayHoo/res/values/menudrawer_strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"md__drawerOpenIndicatorDesc\">Close drawer</string>\n\n    <string name=\"md__drawerClosedIndicatorDesc\">Open drawer</string>\n\n</resources>\n"
  },
  {
    "path": "WayHoo/res/values/menudrawer_styles.xml",
    "content": "<resources>\n\n    <style name=\"Widget\" />\n\n    <!-- Base theme for the menu drawer. -->\n    <style name=\"Widget.MenuDrawer\">\n        <item name=\"mdMenuBackground\">@color/md__defaultBackground</item>\n        <item name=\"mdContentBackground\">?android:attr/windowBackground</item>\n        <item name=\"mdDrawerOpenUpContentDescription\">@string/md__drawerOpenIndicatorDesc</item>\n        <item name=\"mdDrawerClosedUpContentDescription\">@string/md__drawerClosedIndicatorDesc</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "WayHoo/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"app_name\">简洁天气</string>\n    <string name=\"press_again_exit\">再按一次退出</string>\n    <string name=\"speak_launch_project_weather\">Launch Project Weather web site</string>\n    <string name=\"pull_to_refresh_pull_sub_label\">上次刷新:%s</string>\n    <string name=\"pull_to_refresh_pull_sub_label_none\">从未</string>\n    <string name=\"enter_city_name\">请输入城市名称、拼音、区号或邮编</string>\n    <!-- time -->\n    <string name=\"year_before\">年前</string>\n    <string name=\"month_before\">月前</string>\n    <string name=\"day\">天前</string>\n    <string name=\"hour\">小时前</string>\n    <string name=\"min\">分钟前</string>\n    <string name=\"sec\">秒前</string>\n    <string name=\"justnow\">刚刚</string>\n    <string name=\"yesterday\">昨天</string>\n    <string name=\"the_day_before_yesterday\">前天</string>\n    <string name=\"today\">今天</string>\n    <string name=\"date_format\">M月d日 HH:mm</string>\n    <string name=\"year_format\">yyyy年 M月d日 HH:mm</string>\n    <string name=\"net_error\">您的网络不给力哦！</string>\n    <string name=\"city_max_toast\">最多只能添加9个城市哦！</string>\n    <string name=\"get_weatherifo_fail\">获取天气失败了哦！</string>\n    <string name=\"cancle_auto_get_location\">您取消了自动定位！</string>\n    <string name=\"getlocation_fail\">获取位置信息失败！</string>\n    <string name=\"no_this_city\">暂时没有该城市的数据！请手动选择！</string>\n    <string name=\"get_location_scuess\">定位到您在：%s</string>\n    <string name=\"city_exists\">您已经选择过该城市哦！</string>\n    <string name=\"share_fail\">分享失败！</string>\n    <string name=\"please_wait\">请稍后...</string>\n    <string name=\"share_to\">您想分享到？</string>\n    <string name=\"app_information\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;\\\n        本应用是以简洁作为核心要素的一款轻型实用天气软件哦!如果您想了解更多,\\\n        欢迎访问我的博客主页:http://blog.csdn.net/way_ping_li,\\\n        由于时间仓促，错误与疏忽之处在所难免,\\\n        欢迎各位提出宝贵的建议！</string>\n    <string name=\"all_rights_reserved\">Copyright © 2012-2015 way. All Rights Reserved.</string>\n    <string name=\"get_weatherinfo_scuessed\">刷新成功</string>\n    <string name=\"widget_loading_tips\">正在加载,请稍侯...</string>\n        <string name=\"comm_error_noneNetwork\">没有网络连接</string>\n    <string name=\"comm_error_resultIllegal\">数据解析出错</string>\n    <string name=\"comm_error_timeout\">网络不给力</string>\n    <!-- lunar -->\n    <string name=\"nongli\">农历</string>\n    <string name=\"nian\">年</string>\n    <string name=\"chineseNumber1\">一</string>\n    <string name=\"chineseNumber2\">二</string>\n    <string name=\"chineseNumber3\">三</string>\n    <string name=\"chineseNumber4\">四</string>\n    <string name=\"chineseNumber5\">五</string>\n    <string name=\"chineseNumber6\">六</string>\n    <string name=\"chineseNumber7\">七</string>\n    <string name=\"chineseNumber8\">八</string>\n    <string name=\"chineseNumber9\">九</string>\n    <string name=\"chineseNumber10\">十</string>\n    <string name=\"chineseNumber11\">十一</string>\n    <string name=\"chineseNumber12\">十二</string>\n    <string name=\"chineseTen0\">初</string>\n    <string name=\"chineseTen1\">十</string>\n    <string name=\"chineseTen2\">廿</string>\n    <string name=\"chineseTen3\">三</string>\n    <string name=\"chineseTen4\">二</string>\n    <string name=\"animals0\">鼠</string>\n    <string name=\"animals1\">牛</string>\n    <string name=\"animals2\">虎</string>\n    <string name=\"animals3\">兔</string>\n    <string name=\"animals4\">龙</string>\n    <string name=\"animals5\">蛇</string>\n    <string name=\"animals6\">马</string>\n    <string name=\"animals7\">羊</string>\n    <string name=\"animals8\">猴</string>\n    <string name=\"animals9\">鸡</string>\n    <string name=\"animals10\">狗</string>\n    <string name=\"animals11\">猪</string>\n    <string name=\"jia\">甲</string>\n    <string name=\"yi\">乙</string>\n    <string name=\"bing\">丙</string>\n    <string name=\"ding\">丁</string>\n    <string name=\"wutian\">戊</string>\n    <string name=\"ji\">己</string>\n    <string name=\"geng\">庚</string>\n    <string name=\"xin\">辛</string>\n    <string name=\"ren\">壬</string>\n    <string name=\"gui\">癸</string>\n    <string name=\"zi\">子</string>\n    <string name=\"chou\">丑</string>\n    <string name=\"yin\">寅</string>\n    <string name=\"mao\">卯</string>\n    <string name=\"chen\">辰</string>\n    <string name=\"si\">巳</string>\n    <string name=\"wudi\">午</string>\n    <string name=\"wei\">未</string>\n    <string name=\"shen\">申</string>\n    <string name=\"you\">酉</string>\n    <string name=\"xu\">戌</string>\n    <string name=\"hai\">亥</string>\n    <string name=\"leap_month\">闰</string>\n    <string name=\"month\">月</string>\n    <string name=\"zheng\">正</string>\n    <string name=\"zhou\">周</string>\n    <string name=\"year\">年</string>\n    <string name=\"day_ri\">日</string>\n    <string name=\"terms0\">小寒</string>\n    <string name=\"terms1\">大寒</string>\n    <string name=\"terms2\">立春</string>\n    <string name=\"terms3\">雨水</string>\n    <string name=\"terms4\">惊蛰</string>\n    <string name=\"terms5\">春分</string>\n    <!-- SPRD: bug259225 show two field according to CMCC case19 -->\n    <string name=\"terms6\">清明/清明节</string>\n    <string name=\"terms7\">谷雨</string>\n    <string name=\"terms8\">立夏</string>\n    <string name=\"terms9\">小满</string>\n    <string name=\"terms10\">芒种</string>\n    <string name=\"terms11\">夏至</string>\n    <string name=\"terms12\">小暑</string>\n    <string name=\"terms13\">大暑</string>\n    <string name=\"terms14\">立秋</string>\n    <string name=\"terms15\">处暑</string>\n    <string name=\"terms16\">白露</string>\n    <string name=\"terms17\">秋分</string>\n    <string name=\"terms18\">寒露</string>\n    <string name=\"terms19\">霜降</string>\n    <string name=\"terms20\">立冬</string>\n    <string name=\"terms21\">小雪</string>\n    <string name=\"terms22\">大雪</string>\n    <string name=\"terms23\">冬至</string>\n    <string name=\"chunjie\">春节</string>\n    <string name=\"yuanxiao\">元宵节</string>\n    <string name=\"duanwu\">端午节</string>\n    <string name=\"zhongqiu\">中秋节</string>\n    <string name=\"chongyang\">重阳节</string>\n    <string name=\"laba\">腊八</string>\n    <string name=\"xiaonian\">小年</string>\n    <string name=\"chuxi\">除夕</string>\n    <string name=\"qixi\">七夕</string>\n    <string name=\"new_Year_day\">元旦</string>\n    <string name=\"valentin_day\">情人节</string>\n    <string name=\"women_day\">妇女节</string>\n    <string name=\"arbor_day\">植树节</string>\n    <string name=\"tomb_day\">清明节</string>\n    <string name=\"labol_day\">劳动节</string>\n    <string name=\"youth_day\">青年节</string>\n    <string name=\"children_day\">儿童节</string>\n    <string name=\"Communist_day\">建党节</string>\n    <string name=\"army_day\">建军节</string>\n    <string name=\"teacher_day\">教师节</string>\n    <string name=\"national_day\">国庆节</string>\n    <string name=\"christmas_day\">圣诞节</string>\n</resources>"
  },
  {
    "path": "WayHoo/res/values/styles.xml",
    "content": "<resources>\n\n    <style name=\"AppTheme\" parent=\"@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen\"></style>\n\n    <style name=\"SecondAppTheme\" parent=\"@style/AppTheme\">\n        <item name=\"android:windowIsTranslucent\">true</item>\n    </style>\n\n    <!-- 从下往上弹出风格的dialog -->\n    <style name=\"menu_dialog\" parent=\"@android:style/Theme.DeviceDefault.Dialog\">\n        <item name=\"android:windowFrame\">@null</item>\n        <item name=\"android:windowIsFloating\">false</item>\n        <item name=\"android:windowIsTranslucent\">true</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:background\">@android:color/transparent</item>\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:backgroundDimEnabled\">true</item>\n        <item name=\"android:windowFullscreen\">false</item>\n        <item name=\"android:backgroundDimAmount\">0.6</item>\n        <item name=\"android:windowAnimationStyle\">@style/bottom_anim_style</item>\n    </style>\n    <!-- 从上往下弹出的dialog -->\n    <style name=\"search_dialog\" parent=\"@android:style/Theme.DeviceDefault.Dialog\">\n        <item name=\"android:windowFrame\">@null</item>\n        <item name=\"android:windowIsFloating\">false</item>\n        <item name=\"android:windowIsTranslucent\">true</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:background\">@android:color/transparent</item>\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:backgroundDimEnabled\">true</item>\n        <item name=\"android:windowFullscreen\">false</item>\n        <item name=\"android:backgroundDimAmount\">0.6</item>\n        <item name=\"android:windowAnimationStyle\">@style/top_anim_style</item>\n    </style>\n    <!-- 从下往上运动动画 -->\n    <style name=\"bottom_anim_style\">\n        <item name=\"android:windowEnterAnimation\">@anim/in_from_bottom</item>\n        <item name=\"android:windowExitAnimation\">@anim/out_from_bottom</item>\n    </style>\n    <!-- 从上往下运动动画 -->\n    <style name=\"top_anim_style\">\n        <item name=\"android:windowEnterAnimation\">@anim/in_from_top</item>\n        <item name=\"android:windowExitAnimation\">@anim/out_from_top</item>\n    </style>\n\n    <!-- 使用系统动画风格的dialog -->\n    <style name=\"load_dialog\" parent=\"@android:style/Theme.DeviceDefault.Dialog\">\n\n        <!-- 设置完全透明 -->\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <!-- 设置无边框 -->\n        <!-- <item name=\"android:windowFrame\">@null</item> -->\n        <!-- 设置无标题 -->\n        <item name=\"android:windowNoTitle\">true</item>\n        <!-- 设置为浮动窗口 -->\n        <item name=\"android:windowIsFloating\">true</item>\n        <item name=\"android:windowIsTranslucent\">true</item>\n        <item name=\"android:windowContentOverlay\">@null</item>\n        <!-- 设置动画 -->\n        <item name=\"android:windowAnimationStyle\">@android:style/Animation.Dialog</item>\n        <!-- 设置屏幕变暗 -->\n        <item name=\"android:backgroundDimEnabled\">true</item>\n    </style>\n\n    <style name=\"NotificationPrimaryText\">\n        <item name=\"android:textColor\">#cccccc</item>\n        <item name=\"android:textSize\">14sp</item>\n    </style>\n\n    <style name=\"Shade\">\n        <item name=\"android:shadowColor\">#ff434343</item>\n        <item name=\"android:shadowDx\">1.0</item>\n        <item name=\"android:shadowDy\">1.0</item>\n        <item name=\"android:shadowRadius\">2.0</item>\n    </style>\n\n    <style name=\"Label\">\n        <item name=\"android:textSize\">@dimen/text_lg</item>\n        <item name=\"android:textColor\">@android:color/white</item>\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:singleLine\">true</item>\n    </style>\n\n    <style name=\"Label.Shadow\" parent=\"@style/Label\">\n        <item name=\"android:shadowColor\">#88000000</item>\n        <item name=\"android:shadowDx\">1.0</item>\n        <item name=\"android:shadowDy\">1.0</item>\n        <item name=\"android:shadowRadius\">1.5</item>\n    </style>\n\n    <style name=\"Label.Shadow.ExtraLarge\" parent=\"@style/Label.Shadow\">\n        <item name=\"android:textSize\">@dimen/text_xl</item>\n    </style>\n\n    <style name=\"Label.Shadow.Medium\" parent=\"@style/Label.Shadow\">\n        <item name=\"android:textSize\">@dimen/text_md</item>\n    </style>\n\n    <style name=\"Label.Shadow.Small\" parent=\"@style/Label.Shadow\">\n        <item name=\"android:textSize\">@dimen/text_sm</item>\n    </style>\n\n    <style name=\"WeatherModule\">\n        <item name=\"android:background\">@drawable/module_bg</item>\n        <item name=\"android:padding\">@dimen/padding_sm</item>\n        <item name=\"android:layout_width\">fill_parent</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"Line\">\n        <item name=\"android:layout_width\">fill_parent</item>\n        <item name=\"android:layout_height\">1.0dip</item>\n    </style>\n\n    <style name=\"Line.Solid\" parent=\"@style/Line\">\n        <item name=\"android:layout_marginTop\">@dimen/margin_xs</item>\n        <item name=\"android:layout_marginBottom\">@dimen/margin_xs</item>\n        <item name=\"android:src\">@color/forecast_gray</item>\n    </style>\n\n    <style name=\"Line.Dotted\" parent=\"@style/Line\">\n        <item name=\"android:layout_marginBottom\">0.0dip</item>\n        <item name=\"android:src\">@drawable/dotted_line_624</item>\n        <item name=\"android:scaleType\">fitXY</item>\n    </style>\n\n    <style name=\"Label.Medium\" parent=\"@style/Label\">\n        <item name=\"android:textSize\">@dimen/text_md</item>\n    </style>\n\n    <style name=\"YahooText\" parent=\"@android:style/TextAppearance\" />\n\n    <style name=\"YahooText.Widget\" parent=\"@style/YahooText\">\n        <item name=\"android:textColor\">#ffffffff</item>\n        <item name=\"android:includeFontPadding\">false</item>\n        <item name=\"android:shadowColor\">#be000000</item>\n        <item name=\"android:shadowDx\">1.0</item>\n        <item name=\"android:shadowDy\">1.0</item>\n        <item name=\"android:shadowRadius\">1.5</item>\n    </style>\n\n    <style name=\"YahooText.Widget42\" parent=\"@style/YahooText.Widget\">\n        <item name=\"android:textSize\">13.0dip</item>\n        <item name=\"android:textColor\">#ffffffff</item>\n    </style>\n\n    <style name=\"YahooText.Widget42.FlickrAuthor\" parent=\"@style/YahooText.Widget42\">\n        <item name=\"android:textSize\">6.0dip</item>\n        <item name=\"android:textColor\">#ffdcdcdc</item>\n    </style>\n\n    <style name=\"YahooText.WidgetAlert\" parent=\"@style/YahooText.Widget\">\n        <item name=\"android:textSize\">12.0dip</item>\n        <!-- <item name=\"android:fontFamily\">sans-serif</item> -->\n    </style>\n\n    <style name=\"YahooText.Widget42.DigitalClock\" parent=\"@style/YahooText.Widget42\">\n        <item name=\"android:textSize\">13.0dip</item>\n    </style>\n\n    <style name=\"YahooText.Widget42.Large\" parent=\"@style/YahooText.Widget42\">\n        <item name=\"android:textSize\">50.0dip</item>\n    </style>\n\n    <style name=\"YahooText.Widget42.BigTime\" parent=\"@style/YahooText.Widget42\">\n        <item name=\"android:textSize\">80.0dip</item>\n    </style>\n\n    <style name=\"YahooText.Widget42.Location\" parent=\"@style/YahooText.Widget42\">\n        <item name=\"android:textSize\">19.0dip</item>\n    </style>\n\n    <style name=\"YahooText.Widget.ErrorText\" parent=\"@style/YahooText.Widget\">\n        <item name=\"android:textSize\">15.0sp</item>\n    </style>\n\n    <style name=\"ForecastHourly\">\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n    </style>\n\n    <style name=\"weather_widget_refresh_style\" parent=\"@android:style/Widget.ProgressBar\">\n        <item name=\"android:indeterminateDrawable\">@drawable/refresh_icon_anim</item>\n        <item name=\"android:layout_width\">48dip</item>\n        <item name=\"android:layout_height\">48dip</item>\n    </style>\n\n    <style name=\"MenuDrawer\" />\n\n    <style name=\"MenuDrawer.Widget\" />\n\n    <style name=\"MenuDrawer.Widget.Category\">\n        <item name=\"android:textStyle\">bold</item>\n        <item name=\"android:textColor\">@color/sidebar_header_text_color_dark</item>\n        <item name=\"android:textSize\">14sp</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <!-- <item name=\"android:paddingLeft\">16dp</item> -->\n        <item name=\"android:background\">@color/sidebar_header_background_dark</item>\n        <item name=\"android:singleLine\">true</item>\n        <item name=\"android:ellipsize\">end</item>\n        <item name=\"android:paddingLeft\">12.0dip</item>\n        <item name=\"android:paddingTop\">5.0dip</item>\n        <item name=\"android:paddingRight\">15.0dip</item>\n        <item name=\"android:paddingBottom\">5.0dip</item>\n    </style>\n\n    <style name=\"MenuDrawer.Widget.Title\">\n        <item name=\"android:background\">@drawable/sidebar_item_background_dark_selector</item>\n        <item name=\"android:textAppearance\">?android:attr/textAppearance</item>\n        <item name=\"android:textColor\">@color/sidebar_text_dark</item>\n        <item name=\"android:textSize\">17sp</item>\n        <!--\n        <item name=\"android:paddingLeft\">16dp</item>\n        <item name=\"android:paddingRight\">32dp</item>\n        <item name=\"android:paddingTop\">8dp</item>\n        <item name=\"android:paddingBottom\">8dp</item>\n        -->\n        <item name=\"android:drawablePadding\">16dp</item>\n        <item name=\"android:gravity\">center_vertical</item>\n        <item name=\"android:paddingLeft\">8.0dip</item>\n        <item name=\"android:paddingTop\">8.5dip</item>\n        <item name=\"android:paddingRight\">16.0dip</item>\n        <item name=\"android:paddingBottom\">8.5dip</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "WayHoo/res/values/swipeback_attrs.xml",
    "content": "<resources>\n\n    <!-- 滑动返回style声明 -->\n    <declare-styleable name=\"SwipeBackLayout\">\n        <attr name=\"edge_size\" format=\"dimension\" />\n        <attr name=\"edge_flag\">\n            <enum name=\"left\" value=\"0\" />\n            <enum name=\"right\" value=\"1\" />\n            <enum name=\"bottom\" value=\"2\" />\n            <enum name=\"all\" value=\"3\" />\n        </attr>\n        <attr name=\"shadow_left\" format=\"reference\" />\n        <attr name=\"shadow_right\" format=\"reference\" />\n        <attr name=\"shadow_bottom\" format=\"reference\" />\n    </declare-styleable>\n\n    <attr name=\"SwipeBackLayoutStyle\" format=\"reference\" />\n\n</resources>\n"
  },
  {
    "path": "WayHoo/res/values/swipeback_styles.xml",
    "content": "<resources>\n\n    <!-- 滑动返回主题 -->\n    <style name=\"SwipeBackLayout\">\n        <item name=\"edge_size\">50dip</item>\n        <item name=\"shadow_left\">@drawable/shadow_left</item>\n        <item name=\"shadow_right\">@drawable/shadow_right</item>\n        <item name=\"shadow_bottom\">@drawable/shadow_bottom</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "WayHoo/res/values/vpi__attrs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2012 Jake Wharton\n     Copyright (C) 2011 Patrik Åkerfeldt\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n\n<resources>\n    <declare-styleable name=\"ViewPagerIndicator\">\n        <!-- Style of the circle indicator. -->\n        <attr name=\"vpiCirclePageIndicatorStyle\" format=\"reference\"/>\n        <!-- Style of the icon indicator's views. -->\n        <attr name=\"vpiIconPageIndicatorStyle\" format=\"reference\"/>\n        <!-- Style of the line indicator. -->\n        <attr name=\"vpiLinePageIndicatorStyle\" format=\"reference\"/>\n        <!-- Style of the title indicator. -->\n        <attr name=\"vpiTitlePageIndicatorStyle\" format=\"reference\"/>\n        <!-- Style of the tab indicator's tabs. -->\n        <attr name=\"vpiTabPageIndicatorStyle\" format=\"reference\"/>\n        <!-- Style of the underline indicator. -->\n        <attr name=\"vpiUnderlinePageIndicatorStyle\" format=\"reference\"/>\n    </declare-styleable>\n\n    <attr name=\"centered\" format=\"boolean\" />\n    <attr name=\"selectedColor\" format=\"color\" />\n    <attr name=\"strokeWidth\" format=\"dimension\" />\n    <attr name=\"unselectedColor\" format=\"color\" />\n\n    <declare-styleable name=\"CirclePageIndicator\">\n        <!-- Whether or not the indicators should be centered. -->\n        <attr name=\"centered\" />\n        <!-- Color of the filled circle that represents the current page. -->\n        <attr name=\"fillColor\" format=\"color\" />\n        <!-- Color of the filled circles that represents pages. -->\n        <attr name=\"pageColor\" format=\"color\" />\n        <!-- Orientation of the indicator. -->\n        <attr name=\"android:orientation\"/>\n        <!-- Radius of the circles. This is also the spacing between circles. -->\n        <attr name=\"radius\" format=\"dimension\" />\n        <!-- Whether or not the selected indicator snaps to the circles. -->\n        <attr name=\"snap\" format=\"boolean\" />\n        <!-- Color of the open circles. -->\n        <attr name=\"strokeColor\" format=\"color\" />\n        <!-- Width of the stroke used to draw the circles. -->\n        <attr name=\"strokeWidth\" />\n        <!-- View background -->\n        <attr name=\"android:background\"/>\n    </declare-styleable>\n\n    <declare-styleable name=\"LinePageIndicator\">\n        <!-- Whether or not the indicators should be centered. -->\n        <attr name=\"centered\" />\n        <!-- Color of the unselected lines that represent the pages. -->\n        <attr name=\"unselectedColor\" />\n        <!-- Color of the selected line that represents the current page. -->\n        <attr name=\"selectedColor\" />\n        <!-- Width of each indicator line. -->\n        <attr name=\"lineWidth\" format=\"dimension\" />\n        <!-- Width of each indicator line's stroke. -->\n        <attr name=\"strokeWidth\" />\n        <!-- Width of the gap between each indicator line. -->\n        <attr name=\"gapWidth\" format=\"dimension\" />\n        <!-- View background -->\n        <attr name=\"android:background\"/>\n    </declare-styleable>\n\n    <declare-styleable name=\"TitlePageIndicator\">\n        <!-- Screen edge padding. -->\n        <attr name=\"clipPadding\" format=\"dimension\" />\n        <!-- Color of the footer line and indicator. -->\n        <attr name=\"footerColor\" format=\"color\" />\n        <!-- Height of the footer line. -->\n        <attr name=\"footerLineHeight\" format=\"dimension\" />\n        <!-- Style of the indicator. Default is triangle. -->\n        <attr name=\"footerIndicatorStyle\">\n            <enum name=\"none\" value=\"0\" />\n            <enum name=\"triangle\" value=\"1\" />\n            <enum name=\"underline\" value=\"2\" />\n        </attr>\n        <!-- Height of the indicator above the footer line. -->\n        <attr name=\"footerIndicatorHeight\" format=\"dimension\" />\n        <!-- Left and right padding of the underline indicator. -->\n        <attr name=\"footerIndicatorUnderlinePadding\" format=\"dimension\" />\n        <!-- Padding between the bottom of the title and the footer. -->\n        <attr name=\"footerPadding\" format=\"dimension\" />\n        <!-- Position of the line. -->\n        <attr name=\"linePosition\">\n            <enum name=\"bottom\" value=\"0\"/>\n            <enum name=\"top\" value=\"1\"/>\n        </attr>\n        <!-- Color of the selected title. -->\n        <attr name=\"selectedColor\" />\n        <!-- Whether or not the selected item is displayed as bold. -->\n        <attr name=\"selectedBold\" format=\"boolean\" />\n        <!-- Color of regular titles. -->\n        <attr name=\"android:textColor\" />\n        <!-- Size of title text. -->\n        <attr name=\"android:textSize\" />\n        <!-- Padding between titles when bumping into each other. -->\n        <attr name=\"titlePadding\" format=\"dimension\" />\n        <!-- Padding between titles and the top of the View. -->\n        <attr name=\"topPadding\" format=\"dimension\" />\n        <!-- View background -->\n        <attr name=\"android:background\"/>\n    </declare-styleable>\n\n    <declare-styleable name=\"UnderlinePageIndicator\">\n        <!-- Whether or not the selected indicator fades. -->\n        <attr name=\"fades\" format=\"boolean\" />\n        <!-- Length of the delay to fade the indicator. -->\n        <attr name=\"fadeDelay\" format=\"integer\" />\n        <!-- Length of the indicator fade to transparent. -->\n        <attr name=\"fadeLength\" format=\"integer\" />\n        <!-- Color of the selected line that represents the current page. -->\n        <attr name=\"selectedColor\" />\n        <!-- View background -->\n        <attr name=\"android:background\"/>\n    </declare-styleable>\n</resources>\n"
  },
  {
    "path": "WayHoo/res/values/vpi__colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2012 Jake Wharton\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n\n<resources>\n    <color name=\"vpi__background_holo_dark\">#ff000000</color>\n    <color name=\"vpi__background_holo_light\">#fff3f3f3</color>\n    <color name=\"vpi__bright_foreground_holo_dark\">@color/vpi__background_holo_light</color>\n    <color name=\"vpi__bright_foreground_holo_light\">@color/vpi__background_holo_dark</color>\n    <color name=\"vpi__bright_foreground_disabled_holo_dark\">#ff4c4c4c</color>\n    <color name=\"vpi__bright_foreground_disabled_holo_light\">#ffb2b2b2</color>\n    <color name=\"vpi__bright_foreground_inverse_holo_dark\">@color/vpi__bright_foreground_holo_light</color>\n    <color name=\"vpi__bright_foreground_inverse_holo_light\">@color/vpi__bright_foreground_holo_dark</color>\n</resources>\n"
  },
  {
    "path": "WayHoo/res/values/vpi__defaults.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2012 Jake Wharton\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n\n<resources>\n    <bool name=\"default_circle_indicator_centered\">true</bool>\n    <color name=\"default_circle_indicator_fill_color\">#FFFFFFFF</color>\n    <color name=\"default_circle_indicator_page_color\">#00000000</color>\n    <integer name=\"default_circle_indicator_orientation\">0</integer>\n    <dimen name=\"default_circle_indicator_radius\">3dp</dimen>\n    <bool name=\"default_circle_indicator_snap\">false</bool>\n    <color name=\"default_circle_indicator_stroke_color\">#FFDDDDDD</color>\n    <dimen name=\"default_circle_indicator_stroke_width\">1dp</dimen>\n\n    <dimen name=\"default_line_indicator_line_width\">12dp</dimen>\n    <dimen name=\"default_line_indicator_gap_width\">4dp</dimen>\n    <dimen name=\"default_line_indicator_stroke_width\">1dp</dimen>\n    <color name=\"default_line_indicator_selected_color\">#FF33B5E5</color>\n    <color name=\"default_line_indicator_unselected_color\">#FFBBBBBB</color>\n    <bool name=\"default_line_indicator_centered\">true</bool>\n\n    <dimen name=\"default_title_indicator_clip_padding\">4dp</dimen>\n    <color name=\"default_title_indicator_footer_color\">#FF33B5E5</color>\n    <dimen name=\"default_title_indicator_footer_line_height\">2dp</dimen>\n    <integer name=\"default_title_indicator_footer_indicator_style\">2</integer>\n    <dimen name=\"default_title_indicator_footer_indicator_height\">4dp</dimen>\n    <dimen name=\"default_title_indicator_footer_indicator_underline_padding\">20dp</dimen>\n    <dimen name=\"default_title_indicator_footer_padding\">7dp</dimen>\n    <integer name=\"default_title_indicator_line_position\">0</integer>\n    <color name=\"default_title_indicator_selected_color\">#FFFFFFFF</color>\n    <bool name=\"default_title_indicator_selected_bold\">true</bool>\n    <color name=\"default_title_indicator_text_color\">#BBFFFFFF</color>\n    <dimen name=\"default_title_indicator_text_size\">15dp</dimen>\n    <dimen name=\"default_title_indicator_title_padding\">5dp</dimen>\n    <dimen name=\"default_title_indicator_top_padding\">7dp</dimen>\n\n    <bool name=\"default_underline_indicator_fades\">true</bool>\n    <integer name=\"default_underline_indicator_fade_delay\">300</integer>\n    <integer name=\"default_underline_indicator_fade_length\">400</integer>\n    <color name=\"default_underline_indicator_selected_color\">#FF33B5E5</color>\n</resources>"
  },
  {
    "path": "WayHoo/res/values/vpi__styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2011 Jake Wharton\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n\n<resources>\n    <style name=\"Theme.PageIndicatorDefaults\" parent=\"android:Theme\">\n        <item name=\"vpiIconPageIndicatorStyle\">@style/Widget.IconPageIndicator</item>\n        <item name=\"vpiTabPageIndicatorStyle\">@style/Widget.TabPageIndicator</item>\n    </style>\n\n    <style name=\"Widget\">\n    </style>\n\n    <style name=\"Widget.TabPageIndicator\" parent=\"Widget\">\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:background\">@drawable/vpi__tab_indicator</item>\n        <item name=\"android:paddingLeft\">22dip</item>\n        <item name=\"android:paddingRight\">22dip</item>\n        <item name=\"android:paddingTop\">12dp</item>\n        <item name=\"android:paddingBottom\">12dp</item>\n        <item name=\"android:textAppearance\">@style/TextAppearance.TabPageIndicator</item>\n        <item name=\"android:textSize\">12sp</item>\n        <item name=\"android:maxLines\">1</item>\n    </style>\n\n    <style name=\"TextAppearance.TabPageIndicator\" parent=\"Widget\">\n        <item name=\"android:textStyle\">bold</item>\n        <item name=\"android:textColor\">@color/vpi__dark_theme</item>\n    </style>\n\n    <style name=\"Widget.IconPageIndicator\" parent=\"Widget\">\n        <item name=\"android:layout_marginLeft\">6dp</item>\n        <item name=\"android:layout_marginRight\">6dp</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "WayHoo/res/values-hdpi/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<resources>\r\n    <dimen name=\"weather_topbar_padding\">48.0dip</dimen>\r\n    <dimen name=\"weather_weather_info_flippertext_textsize\">28.0sp</dimen>\r\n    <dimen name=\"weather_temperature_desc\">14.0sp</dimen>\r\n    <dimen name=\"weather_local_time\">11.0sp</dimen>\r\n    <dimen name=\"weather_last_update_time_stamp_textsize\">11.0sp</dimen>\r\n    <dimen name=\"flickr_icon_padding_bottom\">0.0dip</dimen>\r\n    <dimen name=\"temp_high_icon_padding_bottom\">0.0dip</dimen>\r\n    <dimen name=\"temp_icon_padding\">14.0dip</dimen>\r\n    <dimen name=\"high_low_text_margin_bottom\">-5.0dip</dimen>\r\n    <dimen name=\"current_condition_icon_margin_bottom\">2.0dip</dimen>\r\n    <dimen name=\"extended_forecast_small_top_name\">14.0sp</dimen>\r\n    <dimen name=\"extended_forecast_small_top_value\">24.0sp</dimen>\r\n    <dimen name=\"extended_forecast_small_top_extra\">11.0sp</dimen>\r\n    <dimen name=\"padding_xs\">6.0dip</dimen>\r\n    <dimen name=\"padding_sm\">10.0dip</dimen>\r\n    <dimen name=\"padding_md\">16.0dip</dimen>\r\n    <dimen name=\"padding_lg\">22.0dip</dimen>\r\n    <dimen name=\"padding_lg2\">20.0dip</dimen>\r\n    <dimen name=\"margin_xxs\">4.0dip</dimen>\r\n    <dimen name=\"margin_xs\">6.0dip</dimen>\r\n    <dimen name=\"margin_sm\">8.0dip</dimen>\r\n    <dimen name=\"margin_md\">16.0dip</dimen>\r\n    <dimen name=\"margin_lg\">22.0dip</dimen>\r\n    <dimen name=\"negative_margin_lg\">-16.0dip</dimen>\r\n    <dimen name=\"negative_margin_sm\">-10.0dip</dimen>\r\n    <dimen name=\"negative_margin_md\">-4.0dip</dimen>\r\n    <dimen name=\"negative_margin_xs\">-3.0dip</dimen>\r\n    <dimen name=\"text_sm\">7.0sp</dimen>\r\n    <dimen name=\"text_md\">11.0sp</dimen>\r\n    <dimen name=\"text_lg\">17.0sp</dimen>\r\n    <dimen name=\"text_xl\">125.0sp</dimen>\r\n    <dimen name=\"temp_high_icon_margin_left\">23.0dip</dimen>\r\n</resources>\r\n"
  },
  {
    "path": "WayHoo/res/values-xhdpi/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<resources>\r\n    <dimen name=\"city_manager_tiny_height\">160.0dip</dimen>\r\n    \r\n    <dimen name=\"weather_topbar_padding\">48.0dip</dimen>\r\n    <dimen name=\"weather_temperature_desc\">20.0sp</dimen>\r\n    <dimen name=\"weather_local_time\">16.0sp</dimen>\r\n    <dimen name=\"weather_last_update_time_stamp_textsize\">16.0sp</dimen>\r\n    <dimen name=\"weather_low_temperature_textsize\">16.0sp</dimen>\r\n    <dimen name=\"weather_temperature_textsize\">40.0sp</dimen>\r\n    <dimen name=\"weather_high_temperature_textsize\">16.0sp</dimen>\r\n    <dimen name=\"topbar_icon_size\">24.0dip</dimen>\r\n    <dimen name=\"current_condition_icon_size\">36.0dip</dimen>\r\n    <dimen name=\"current_condition_height\">180.0dip</dimen>\r\n    <dimen name=\"current_temperature_phone_textsize\">150.0sp</dimen>\r\n    <dimen name=\"current_condition_copyright_phone_textsize\">12.0sp</dimen>\r\n    <dimen name=\"current_condition_icon_right_margin\">4.0dip</dimen>\r\n    <dimen name=\"temp_icon_size\">30.0dip</dimen>\r\n    <dimen name=\"flickr_icon_padding_bottom\">1.0dip</dimen>\r\n    <dimen name=\"temp_high_icon_padding_bottom\">1.0dip</dimen>\r\n    <dimen name=\"temp_icon_padding\">18.0dip</dimen>\r\n    <dimen name=\"high_low_text_margin_bottom\">-5.0dip</dimen>\r\n    <dimen name=\"current_condition_icon_margin_bottom\">5.0dip</dimen>\r\n    <dimen name=\"hourly_icon_size\">29.0dip</dimen>\r\n    <dimen name=\"daily_icon_size\">32.0dip</dimen>\r\n    <dimen name=\"daily_forecast_item_height\">43.0dip</dimen>\r\n    <dimen name=\"hourly_forecast_item_height\">69.0dip</dimen>\r\n    <dimen name=\"wunder_logo_item_height\">45.0dip</dimen>\r\n    <dimen name=\"details_card_height\">411.0dip</dimen>\r\n    <dimen name=\"details_icon_size\">92.0dip</dimen>\r\n    <dimen name=\"precipitation_card_height\">192.0dip</dimen>\r\n    <dimen name=\"precipitation_item_height\">110.0dip</dimen>\r\n    <dimen name=\"precipitation_item_width\">112.0dip</dimen>\r\n    <dimen name=\"time_text_size\">14.0sp</dimen>\r\n    <dimen name=\"sunmoon_card_height\">240.0dip</dimen>\r\n    <dimen name=\"sun_view_height\">152.0dip</dimen>\r\n    <dimen name=\"sun_arc_width\">210.0dip</dimen>\r\n    <dimen name=\"sun_arc_leftOffset\">20.0dip</dimen>\r\n    <dimen name=\"sun_arc_topOffset\">40.0dip</dimen>\r\n    <dimen name=\"sun_ball_radius\">4.0dip</dimen>\r\n    <dimen name=\"moon_icon_size\">25.0dip</dimen>\r\n    <dimen name=\"sunview_margin\">6.0dip</dimen>\r\n    <dimen name=\"negative_sunview_margin\">-6.0dip</dimen>\r\n    <dimen name=\"wind_view_height\">106.0dip</dimen>\r\n    <dimen name=\"big_pole_x\">38.0dip</dimen>\r\n    <dimen name=\"small_pole_x\">78.0dip</dimen>\r\n    <dimen name=\"pole_bottom_y\">98.0dip</dimen>\r\n    <dimen name=\"pressure_line_y\">73.0dip</dimen>\r\n    <dimen name=\"pressure_text_y\">4.0dip</dimen>\r\n    <dimen name=\"wind_text_x\">10.0dip</dimen>\r\n    <dimen name=\"wind_text_y\">29.0dip</dimen>\r\n    <dimen name=\"util_margin_list_separator\">10.0dip</dimen>\r\n    <dimen name=\"util_margin_social_label\">10.0dip</dimen>\r\n    <dimen name=\"util_margin_med\">15.0dip</dimen>\r\n    <dimen name=\"util_margin_lg\">26.0dip</dimen>\r\n    <dimen name=\"util_margin_panel\">14.0dip</dimen>\r\n    <dimen name=\"util_margin_panel_small\">12.0dip</dimen>\r\n    <dimen name=\"util_margin_explain_raise\">-4.0dip</dimen>\r\n    <dimen name=\"util_row_height_lg\">46.0dip</dimen>\r\n    <dimen name=\"util_row_height_med\">43.0dip</dimen>\r\n    <dimen name=\"util_row_height_sm\">32.0dip</dimen>\r\n    <dimen name=\"util_toggle_width\">56.0dip</dimen>\r\n    <dimen name=\"util_toggle_height\">29.0dip</dimen>\r\n    <dimen name=\"util_font_lg\">19.0sp</dimen>\r\n    <dimen name=\"util_font_med\">14.0sp</dimen>\r\n    <dimen name=\"util_font_sm\">12.0sp</dimen>\r\n    <dimen name=\"util_font_xs\">10.0sp</dimen>\r\n    <dimen name=\"util_font_sm_heading\">13.0sp</dimen>\r\n    <dimen name=\"util_delete_size\">22.0dip</dimen>\r\n    <dimen name=\"util_fb_toggle_size\">17.0dip</dimen>\r\n    <dimen name=\"util_fb_toggle_touch_size\">50.0dip</dimen>\r\n    <dimen name=\"util_search_size\">18.0dip</dimen>\r\n    <dimen name=\"util_login_panel_height\">110.0dip</dimen>\r\n    <dimen name=\"util_list_separator_height\">2.0dip</dimen>\r\n    <dimen name=\"util_social_size\">23.0dip</dimen>\r\n    <dimen name=\"util_margin_yahoo_left\">36.0dip</dimen>\r\n    <dimen name=\"util_toggle_corner_radius\">3.0dip</dimen>\r\n    <dimen name=\"util_toggle_stroke_width\">1.0px</dimen>\r\n    <dimen name=\"extended_forecast_small_top_name\">14.0sp</dimen>\r\n    <dimen name=\"extended_forecast_small_top_value\">24.0sp</dimen>\r\n    <dimen name=\"extended_forecast_small_top_extra\">11.0sp</dimen>\r\n    <dimen name=\"padding_xxs\">4.0dip</dimen>\r\n    <dimen name=\"padding_xs\">8.0dip</dimen>\r\n    <dimen name=\"padding_sm\">12.0dip</dimen>\r\n    <dimen name=\"padding_md\">18.0dip</dimen>\r\n    <dimen name=\"padding_lg\">24.0dip</dimen>\r\n    <dimen name=\"padding_lg2\">22.0dip</dimen>\r\n    <dimen name=\"margin_xxs\">4.0dip</dimen>\r\n    <dimen name=\"margin_xs\">8.0dip</dimen>\r\n    <dimen name=\"margin_md\">18.0dip</dimen>\r\n    <dimen name=\"margin_lg\">24.0dip</dimen>\r\n    <dimen name=\"negative_margin_lg\">-18.0dip</dimen>\r\n    <dimen name=\"negative_margin_sm\">-12.0dip</dimen>\r\n    <dimen name=\"negative_margin_md\">-5.0dip</dimen>\r\n    <dimen name=\"negative_margin_xs\">-4.0dip</dimen>\r\n    <dimen name=\"text_sm\">8.0sp</dimen>\r\n    <dimen name=\"text_md\">13.0sp</dimen>\r\n    <dimen name=\"text_lg\">19.0sp</dimen>\r\n    <dimen name=\"text_xl\">127.0sp</dimen>\r\n    <dimen name=\"temp_high_icon_margin_left\">24.0dip</dimen>\r\n    <dimen name=\"indicator_right_padding\">10.0dip</dimen>\r\n    <dimen name=\"indicator_corner_radius\">12.0dip</dimen>\r\n    <dimen name=\"indicator_internal_padding\">4.0dip</dimen>\r\n    <dimen name=\"header_footer_left_right_padding\">24.0dip</dimen>\r\n    <dimen name=\"header_footer_top_bottom_padding\">12.0dip</dimen>\r\n    <dimen name=\"weather_flipweather_weather_info_flippertext_textsizeper_location_textsize\">35.0sp</dimen>\r\n</resources>\r\n"
  },
  {
    "path": "WayHoo/res/values-zh-rCN/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"app_name\">简洁天气</string>\n    <string name=\"press_again_exit\">再按一次退出</string>\n    <string name=\"speak_launch_project_weather\">Launch Project Weather web site</string>\n    <string name=\"pull_to_refresh_pull_sub_label\">上次刷新:%s</string>\n    <string name=\"pull_to_refresh_pull_sub_label_none\">从未</string>\n    <string name=\"enter_city_name\">请输入城市名称、拼音、区号或邮编</string>\n    <!-- time -->\n    <string name=\"year_before\">年前</string>\n    <string name=\"month_before\">月前</string>\n    <string name=\"day\">天前</string>\n    <string name=\"hour\">小时前</string>\n    <string name=\"min\">分钟前</string>\n    <string name=\"sec\">秒前</string>\n    <string name=\"justnow\">刚刚</string>\n    <string name=\"yesterday\">昨天</string>\n    <string name=\"the_day_before_yesterday\">前天</string>\n    <string name=\"today\">今天</string>\n    <string name=\"date_format\">M月d日 HH:mm</string>\n    <string name=\"year_format\">yyyy年 M月d日 HH:mm</string>\n    <string name=\"net_error\">您的网络不给力哦！</string>\n    <string name=\"city_max_toast\">最多只能添加9个城市哦！</string>\n    <string name=\"get_weatherifo_fail\">获取天气失败了哦！</string>\n    <string name=\"cancle_auto_get_location\">您取消了自动定位！</string>\n    <string name=\"getlocation_fail\">获取位置信息失败！</string>\n    <string name=\"no_this_city\">暂时没有该城市的数据！请手动选择！</string>\n    <string name=\"get_location_scuess\">定位到您在：%s</string>\n    <string name=\"city_exists\">您已经选择过该城市哦！</string>\n    <string name=\"share_fail\">分享失败！</string>\n    <string name=\"please_wait\">请稍后...</string>\n    <string name=\"share_to\">您想分享到？</string>\n    <string name=\"app_information\">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;\\\n        本应用是以简洁作为核心要素的一款轻型实用天气软件哦!如果您想了解更多,\\\n        欢迎访问我的博客主页:http://blog.csdn.net/way_ping_li,\\\n        由于时间仓促，错误与疏忽之处在所难免,\\\n        欢迎各位提出宝贵的建议！</string>\n    <string name=\"all_rights_reserved\">Copyright © 2012-2015 way. All Rights Reserved.</string>\n    <string name=\"get_weatherinfo_scuessed\">刷新成功</string>\n    <string name=\"widget_loading_tips\">正在加载,请稍侯...</string>\n    <!-- lunar -->\n    <string name=\"nongli\">农历</string>\n    <string name=\"nian\">年</string>\n    <string name=\"chineseNumber1\">一</string>\n    <string name=\"chineseNumber2\">二</string>\n    <string name=\"chineseNumber3\">三</string>\n    <string name=\"chineseNumber4\">四</string>\n    <string name=\"chineseNumber5\">五</string>\n    <string name=\"chineseNumber6\">六</string>\n    <string name=\"chineseNumber7\">七</string>\n    <string name=\"chineseNumber8\">八</string>\n    <string name=\"chineseNumber9\">九</string>\n    <string name=\"chineseNumber10\">十</string>\n    <string name=\"chineseNumber11\">十一</string>\n    <string name=\"chineseNumber12\">十二</string>\n    <string name=\"chineseTen0\">初</string>\n    <string name=\"chineseTen1\">十</string>\n    <string name=\"chineseTen2\">廿</string>\n    <string name=\"chineseTen3\">三</string>\n    <string name=\"chineseTen4\">二</string>\n    <string name=\"animals0\">鼠</string>\n    <string name=\"animals1\">牛</string>\n    <string name=\"animals2\">虎</string>\n    <string name=\"animals3\">兔</string>\n    <string name=\"animals4\">龙</string>\n    <string name=\"animals5\">蛇</string>\n    <string name=\"animals6\">马</string>\n    <string name=\"animals7\">羊</string>\n    <string name=\"animals8\">猴</string>\n    <string name=\"animals9\">鸡</string>\n    <string name=\"animals10\">狗</string>\n    <string name=\"animals11\">猪</string>\n    <string name=\"jia\">甲</string>\n    <string name=\"yi\">乙</string>\n    <string name=\"bing\">丙</string>\n    <string name=\"ding\">丁</string>\n    <string name=\"wutian\">戊</string>\n    <string name=\"ji\">己</string>\n    <string name=\"geng\">庚</string>\n    <string name=\"xin\">辛</string>\n    <string name=\"ren\">壬</string>\n    <string name=\"gui\">癸</string>\n    <string name=\"zi\">子</string>\n    <string name=\"chou\">丑</string>\n    <string name=\"yin\">寅</string>\n    <string name=\"mao\">卯</string>\n    <string name=\"chen\">辰</string>\n    <string name=\"si\">巳</string>\n    <string name=\"wudi\">午</string>\n    <string name=\"wei\">未</string>\n    <string name=\"shen\">申</string>\n    <string name=\"you\">酉</string>\n    <string name=\"xu\">戌</string>\n    <string name=\"hai\">亥</string>\n    <string name=\"leap_month\">闰</string>\n    <string name=\"month\">月</string>\n    <string name=\"zheng\">正</string>\n    <string name=\"zhou\">周</string>\n    <string name=\"year\">年</string>\n    <string name=\"day_ri\">日</string>\n    <string name=\"terms0\">小寒</string>\n    <string name=\"terms1\">大寒</string>\n    <string name=\"terms2\">立春</string>\n    <string name=\"terms3\">雨水</string>\n    <string name=\"terms4\">惊蛰</string>\n    <string name=\"terms5\">春分</string>\n    <!-- SPRD: bug259225 show two field according to CMCC case19 -->\n    <string name=\"terms6\">清明/清明节</string>\n    <string name=\"terms7\">谷雨</string>\n    <string name=\"terms8\">立夏</string>\n    <string name=\"terms9\">小满</string>\n    <string name=\"terms10\">芒种</string>\n    <string name=\"terms11\">夏至</string>\n    <string name=\"terms12\">小暑</string>\n    <string name=\"terms13\">大暑</string>\n    <string name=\"terms14\">立秋</string>\n    <string name=\"terms15\">处暑</string>\n    <string name=\"terms16\">白露</string>\n    <string name=\"terms17\">秋分</string>\n    <string name=\"terms18\">寒露</string>\n    <string name=\"terms19\">霜降</string>\n    <string name=\"terms20\">立冬</string>\n    <string name=\"terms21\">小雪</string>\n    <string name=\"terms22\">大雪</string>\n    <string name=\"terms23\">冬至</string>\n    <string name=\"chunjie\">春节</string>\n    <string name=\"yuanxiao\">元宵节</string>\n    <string name=\"duanwu\">端午节</string>\n    <string name=\"zhongqiu\">中秋节</string>\n    <string name=\"chongyang\">重阳节</string>\n    <string name=\"laba\">腊八</string>\n    <string name=\"xiaonian\">小年</string>\n    <string name=\"chuxi\">除夕</string>\n    <string name=\"qixi\">七夕</string>\n    <string name=\"new_Year_day\">元旦</string>\n    <string name=\"valentin_day\">情人节</string>\n    <string name=\"women_day\">妇女节</string>\n    <string name=\"arbor_day\">植树节</string>\n    <string name=\"tomb_day\">清明节</string>\n    <string name=\"labol_day\">劳动节</string>\n    <string name=\"youth_day\">青年节</string>\n    <string name=\"children_day\">儿童节</string>\n    <string name=\"Communist_day\">建党节</string>\n    <string name=\"army_day\">建军节</string>\n    <string name=\"teacher_day\">教师节</string>\n    <string name=\"national_day\">国庆节</string>\n    <string name=\"christmas_day\">圣诞节</string>\n</resources>"
  },
  {
    "path": "WayHoo/res/xml/weather_appwidget_info_rigo_2x2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<appwidget-provider xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:initialLayout=\"@layout/weather_provider_layout_rigo_2x2\"\n    android:minHeight=\"110.0dip\"\n    android:minWidth=\"110.0dip\"\n    android:previewImage=\"@drawable/widget2_2\"\n    android:updatePeriodMillis=\"0\" />\n"
  },
  {
    "path": "WayHoo/res/xml/weather_appwidget_info_rigo_4x1.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<appwidget-provider xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:initialLayout=\"@layout/weather_provider_layout_rigo_4x1\"\n    android:minHeight=\"30.0dip\"\n    android:minWidth=\"294.0dip\"\n    android:previewImage=\"@drawable/widget4_1\"\n    android:updatePeriodMillis=\"0\" />\n"
  },
  {
    "path": "WayHoo/res/xml/weather_appwidget_info_rigo_4x2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<appwidget-provider xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:initialLayout=\"@layout/weather_provider_layout_rigo_4x2\"\n    android:minHeight=\"110.0dip\"\n    android:minWidth=\"294.0dip\"\n    android:previewImage=\"@drawable/widget4_2\"\n    android:updatePeriodMillis=\"0\" />\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/AuthFailureError.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley;\n\nimport android.content.Intent;\n\nimport com.android.volley.NetworkResponse;\nimport com.android.volley.VolleyError;\n\n/**\n * Error indicating that there was an authentication failure when performing a Request.\n */\n@SuppressWarnings(\"serial\")\npublic class AuthFailureError extends VolleyError {\n    /** An intent that can be used to resolve this exception. (Brings up the password dialog.) */\n    private Intent mResolutionIntent;\n\n    public AuthFailureError() { }\n\n    public AuthFailureError(Intent intent) {\n        mResolutionIntent = intent;\n    }\n\n    public AuthFailureError(NetworkResponse response) {\n        super(response);\n    }\n\n    public AuthFailureError(String message) {\n        super(message);\n    }\n\n    public AuthFailureError(String message, Exception reason) {\n        super(message, reason);\n    }\n\n    public Intent getResolutionIntent() {\n        return mResolutionIntent;\n    }\n\n    @Override\n    public String getMessage() {\n        if (mResolutionIntent != null) {\n            return \"User needs to (re)enter credentials.\";\n        }\n        return super.getMessage();\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/Cache.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley;\n\nimport java.util.Collections;\nimport java.util.Map;\n\n/**\n * An interface for a cache keyed by a String with a byte array as data.\n */\npublic interface Cache {\n    /**\n     * Retrieves an entry from the cache.\n     * @param key Cache key\n     * @return An {@link Entry} or null in the event of a cache miss\n     */\n    public Entry get(String key);\n\n    /**\n     * Adds or replaces an entry to the cache.\n     * @param key Cache key\n     * @param entry Data to store and metadata for cache coherency, TTL, etc.\n     */\n    public void put(String key, Entry entry);\n\n    /**\n     * Performs any potentially long-running actions needed to initialize the cache;\n     * will be called from a worker thread.\n     */\n    public void initialize();\n\n    /**\n     * Invalidates an entry in the cache.\n     * @param key Cache key\n     * @param fullExpire True to fully expire the entry, false to soft expire\n     */\n    public void invalidate(String key, boolean fullExpire);\n\n    /**\n     * Removes an entry from the cache.\n     * @param key Cache key\n     */\n    public void remove(String key);\n\n    /**\n     * Empties the cache.\n     */\n    public void clear();\n\n    /**\n     * Data and metadata for an entry returned by the cache.\n     */\n    public static class Entry {\n        /** The data returned from cache. */\n        public byte[] data;\n\n        /** ETag for cache coherency. */\n        public String etag;\n\n        /** Date of this response as reported by the server. */\n        public long serverDate;\n\n        /** TTL for this record. */\n        public long ttl;\n\n        /** Soft TTL for this record. */\n        public long softTtl;\n\n        /** Immutable response headers as received from server; must be non-null. */\n        public Map<String, String> responseHeaders = Collections.emptyMap();\n\n        /** True if the entry is expired. */\n        public boolean isExpired() {\n            return this.ttl < System.currentTimeMillis();\n        }\n\n        /** True if a refresh is needed from the original data source. */\n        public boolean refreshNeeded() {\n            return this.softTtl < System.currentTimeMillis();\n        }\n    }\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/CacheDispatcher.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley;\n\nimport android.os.Process;\n\nimport java.util.concurrent.BlockingQueue;\n\n/**\n * Provides a thread for performing cache triage on a queue of requests.\n *\n * Requests added to the specified cache queue are resolved from cache.\n * Any deliverable response is posted back to the caller via a\n * {@link ResponseDelivery}.  Cache misses and responses that require\n * refresh are enqueued on the specified network queue for processing\n * by a {@link NetworkDispatcher}.\n */\npublic class CacheDispatcher extends Thread {\n\n    private static final boolean DEBUG = VolleyLog.DEBUG;\n\n    /** The queue of requests coming in for triage. */\n    private final BlockingQueue<Request<?>> mCacheQueue;\n\n    /** The queue of requests going out to the network. */\n    private final BlockingQueue<Request<?>> mNetworkQueue;\n\n    /** The cache to read from. */\n    private final Cache mCache;\n\n    /** For posting responses. */\n    private final ResponseDelivery mDelivery;\n\n    /** Used for telling us to die. */\n    private volatile boolean mQuit = false;\n\n    /**\n     * Creates a new cache triage dispatcher thread.  You must call {@link #start()}\n     * in order to begin processing.\n     *\n     * @param cacheQueue Queue of incoming requests for triage\n     * @param networkQueue Queue to post requests that require network to\n     * @param cache Cache interface to use for resolution\n     * @param delivery Delivery interface to use for posting responses\n     */\n    public CacheDispatcher(\n            BlockingQueue<Request<?>> cacheQueue, BlockingQueue<Request<?>> networkQueue,\n            Cache cache, ResponseDelivery delivery) {\n        mCacheQueue = cacheQueue;\n        mNetworkQueue = networkQueue;\n        mCache = cache;\n        mDelivery = delivery;\n    }\n\n    /**\n     * Forces this dispatcher to quit immediately.  If any requests are still in\n     * the queue, they are not guaranteed to be processed.\n     */\n    public void quit() {\n        mQuit = true;\n        interrupt();\n    }\n\n    @Override\n    public void run() {\n        if (DEBUG) VolleyLog.v(\"start new dispatcher\");\n        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);\n\n        // Make a blocking call to initialize the cache.\n        mCache.initialize();\n\n        while (true) {\n            try {\n                // Get a request from the cache triage queue, blocking until\n                // at least one is available.\n                final Request<?> request = mCacheQueue.take();\n                request.addMarker(\"cache-queue-take\");\n\n                // If the request has been canceled, don't bother dispatching it.\n                if (request.isCanceled()) {\n                    request.finish(\"cache-discard-canceled\");\n                    continue;\n                }\n\n                // Attempt to retrieve this item from cache.\n                Cache.Entry entry = mCache.get(request.getCacheKey());\n                if (entry == null) {\n                    request.addMarker(\"cache-miss\");\n                    // Cache miss; send off to the network dispatcher.\n                    mNetworkQueue.put(request);\n                    continue;\n                }\n\n                // If it is completely expired, just send it to the network.\n                if (entry.isExpired()) {\n                    request.addMarker(\"cache-hit-expired\");\n                    request.setCacheEntry(entry);\n                    mNetworkQueue.put(request);\n                    continue;\n                }\n\n                // We have a cache hit; parse its data for delivery back to the request.\n                request.addMarker(\"cache-hit\");\n                Response<?> response = request.parseNetworkResponse(\n                        new NetworkResponse(entry.data, entry.responseHeaders));\n                request.addMarker(\"cache-hit-parsed\");\n\n                if (!entry.refreshNeeded()) {\n                    // Completely unexpired cache hit. Just deliver the response.\n                    mDelivery.postResponse(request, response);\n                } else {\n                    // Soft-expired cache hit. We can deliver the cached response,\n                    // but we need to also send the request to the network for\n                    // refreshing.\n                    request.addMarker(\"cache-hit-refresh-needed\");\n                    request.setCacheEntry(entry);\n\n                    // Mark the response as intermediate.\n                    response.intermediate = true;\n\n                    // Post the intermediate response back to the user and have\n                    // the delivery then forward the request along to the network.\n                    mDelivery.postResponse(request, response, new Runnable() {\n                        @Override\n                        public void run() {\n                            try {\n                                mNetworkQueue.put(request);\n                            } catch (InterruptedException e) {\n                                // Not much we can do about this.\n                            }\n                        }\n                    });\n                }\n\n            } catch (InterruptedException e) {\n                // We may have been interrupted because it was time to quit.\n                if (mQuit) {\n                    return;\n                }\n                continue;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/DefaultRetryPolicy.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley;\n\n/**\n * Default retry policy for requests.\n */\npublic class DefaultRetryPolicy implements RetryPolicy {\n    /** The current timeout in milliseconds. */\n    private int mCurrentTimeoutMs;\n\n    /** The current retry count. */\n    private int mCurrentRetryCount;\n\n    /** The maximum number of attempts. */\n    private final int mMaxNumRetries;\n\n    /** The backoff multiplier for for the policy. */\n    private final float mBackoffMultiplier;\n\n    /** The default socket timeout in milliseconds */\n    public static final int DEFAULT_TIMEOUT_MS = 2500;\n\n    /** The default number of retries */\n    public static final int DEFAULT_MAX_RETRIES = 1;\n\n    /** The default backoff multiplier */\n    public static final float DEFAULT_BACKOFF_MULT = 1f;\n\n    /**\n     * Constructs a new retry policy using the default timeouts.\n     */\n    public DefaultRetryPolicy() {\n        this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);\n    }\n\n    /**\n     * Constructs a new retry policy.\n     * @param initialTimeoutMs The initial timeout for the policy.\n     * @param maxNumRetries The maximum number of retries.\n     * @param backoffMultiplier Backoff multiplier for the policy.\n     */\n    public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {\n        mCurrentTimeoutMs = initialTimeoutMs;\n        mMaxNumRetries = maxNumRetries;\n        mBackoffMultiplier = backoffMultiplier;\n    }\n\n    /**\n     * Returns the current timeout.\n     */\n    @Override\n    public int getCurrentTimeout() {\n        return mCurrentTimeoutMs;\n    }\n\n    /**\n     * Returns the current retry count.\n     */\n    @Override\n    public int getCurrentRetryCount() {\n        return mCurrentRetryCount;\n    }\n\n    /**\n     * Prepares for the next retry by applying a backoff to the timeout.\n     * @param error The error code of the last attempt.\n     */\n    @Override\n    public void retry(VolleyError error) throws VolleyError {\n        mCurrentRetryCount++;\n        mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);\n        if (!hasAttemptRemaining()) {\n            throw error;\n        }\n    }\n\n    /**\n     * Returns true if this policy has attempts remaining, false otherwise.\n     */\n    protected boolean hasAttemptRemaining() {\n        return mCurrentRetryCount <= mMaxNumRetries;\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/ExecutorDelivery.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley;\n\nimport android.os.Handler;\n\nimport java.util.concurrent.Executor;\n\n/**\n * Delivers responses and errors.\n */\npublic class ExecutorDelivery implements ResponseDelivery {\n    /** Used for posting responses, typically to the main thread. */\n    private final Executor mResponsePoster;\n\n    /**\n     * Creates a new response delivery interface.\n     * @param handler {@link Handler} to post responses on\n     */\n    public ExecutorDelivery(final Handler handler) {\n        // Make an Executor that just wraps the handler.\n        mResponsePoster = new Executor() {\n            @Override\n            public void execute(Runnable command) {\n                handler.post(command);\n            }\n        };\n    }\n\n    /**\n     * Creates a new response delivery interface, mockable version\n     * for testing.\n     * @param executor For running delivery tasks\n     */\n    public ExecutorDelivery(Executor executor) {\n        mResponsePoster = executor;\n    }\n\n    @Override\n    public void postResponse(Request<?> request, Response<?> response) {\n        postResponse(request, response, null);\n    }\n\n    @Override\n    public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {\n        request.markDelivered();\n        request.addMarker(\"post-response\");\n        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));\n    }\n\n    @Override\n    public void postError(Request<?> request, VolleyError error) {\n        request.addMarker(\"post-error\");\n        Response<?> response = Response.error(error);\n        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));\n    }\n\n    /**\n     * A Runnable used for delivering network responses to a listener on the\n     * main thread.\n     */\n    @SuppressWarnings(\"rawtypes\")\n    private class ResponseDeliveryRunnable implements Runnable {\n        private final Request mRequest;\n        private final Response mResponse;\n        private final Runnable mRunnable;\n\n        public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {\n            mRequest = request;\n            mResponse = response;\n            mRunnable = runnable;\n        }\n\n        @SuppressWarnings(\"unchecked\")\n        @Override\n        public void run() {\n            // If this request has canceled, finish it and don't deliver.\n            if (mRequest.isCanceled()) {\n                mRequest.finish(\"canceled-at-delivery\");\n                return;\n            }\n\n            // Deliver a normal response or error, depending.\n            if (mResponse.isSuccess()) {\n                mRequest.deliverResponse(mResponse.result);\n            } else {\n                mRequest.deliverError(mResponse.error);\n            }\n\n            // If this is an intermediate response, add a marker, otherwise we're done\n            // and the request can be finished.\n            if (mResponse.intermediate) {\n                mRequest.addMarker(\"intermediate-response\");\n            } else {\n                mRequest.finish(\"done\");\n            }\n\n            // If we have been provided a post-delivery runnable, run it.\n            if (mRunnable != null) {\n                mRunnable.run();\n            }\n       }\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/Network.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley;\n\n/**\n * An interface for performing requests.\n */\npublic interface Network {\n    /**\n     * Performs the specified request.\n     * @param request Request to process\n     * @return A {@link NetworkResponse} with data and caching metadata; will never be null\n     * @throws VolleyError on errors\n     */\n    public NetworkResponse performRequest(Request<?> request) throws VolleyError;\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/NetworkDispatcher.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley;\n\nimport android.annotation.TargetApi;\nimport android.net.TrafficStats;\nimport android.os.Build;\nimport android.os.Process;\n\nimport java.util.concurrent.BlockingQueue;\n\n/**\n * Provides a thread for performing network dispatch from a queue of requests.\n *\n * Requests added to the specified queue are processed from the network via a\n * specified {@link Network} interface. Responses are committed to cache, if\n * eligible, using a specified {@link Cache} interface. Valid responses and\n * errors are posted back to the caller via a {@link ResponseDelivery}.\n */\npublic class NetworkDispatcher extends Thread {\n    /** The queue of requests to service. */\n    private final BlockingQueue<Request<?>> mQueue;\n    /** The network interface for processing requests. */\n    private final Network mNetwork;\n    /** The cache to write to. */\n    private final Cache mCache;\n    /** For posting responses and errors. */\n    private final ResponseDelivery mDelivery;\n    /** Used for telling us to die. */\n    private volatile boolean mQuit = false;\n\n    /**\n     * Creates a new network dispatcher thread.  You must call {@link #start()}\n     * in order to begin processing.\n     *\n     * @param queue Queue of incoming requests for triage\n     * @param network Network interface to use for performing requests\n     * @param cache Cache interface to use for writing responses to cache\n     * @param delivery Delivery interface to use for posting responses\n     */\n    public NetworkDispatcher(BlockingQueue<Request<?>> queue,\n            Network network, Cache cache,\n            ResponseDelivery delivery) {\n        mQueue = queue;\n        mNetwork = network;\n        mCache = cache;\n        mDelivery = delivery;\n    }\n\n    /**\n     * Forces this dispatcher to quit immediately.  If any requests are still in\n     * the queue, they are not guaranteed to be processed.\n     */\n    public void quit() {\n        mQuit = true;\n        interrupt();\n    }\n\n    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)\n    private void addTrafficStatsTag(Request<?> request) {\n        // Tag the request (if API >= 14)\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {\n            TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());\n        }\n    }\n\n    @Override\n    public void run() {\n        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);\n        Request<?> request;\n        while (true) {\n            try {\n                // Take a request from the queue.\n                request = mQueue.take();\n            } catch (InterruptedException e) {\n                // We may have been interrupted because it was time to quit.\n                if (mQuit) {\n                    return;\n                }\n                continue;\n            }\n\n            try {\n                request.addMarker(\"network-queue-take\");\n\n                // If the request was cancelled already, do not perform the\n                // network request.\n                if (request.isCanceled()) {\n                    request.finish(\"network-discard-cancelled\");\n                    continue;\n                }\n\n                addTrafficStatsTag(request);\n\n                // Perform the network request.\n                NetworkResponse networkResponse = mNetwork.performRequest(request);\n                request.addMarker(\"network-http-complete\");\n\n                // If the server returned 304 AND we delivered a response already,\n                // we're done -- don't deliver a second identical response.\n                if (networkResponse.notModified && request.hasHadResponseDelivered()) {\n                    request.finish(\"not-modified\");\n                    continue;\n                }\n\n                // Parse the response here on the worker thread.\n                Response<?> response = request.parseNetworkResponse(networkResponse);\n                request.addMarker(\"network-parse-complete\");\n\n                // Write to cache if applicable.\n                // TODO: Only update cache metadata instead of entire record for 304s.\n                if (request.shouldCache() && response.cacheEntry != null) {\n                    mCache.put(request.getCacheKey(), response.cacheEntry);\n                    request.addMarker(\"network-cache-written\");\n                }\n\n                // Post the response back.\n                request.markDelivered();\n                mDelivery.postResponse(request, response);\n            } catch (VolleyError volleyError) {\n                parseAndDeliverNetworkError(request, volleyError);\n            } catch (Exception e) {\n                VolleyLog.e(e, \"Unhandled exception %s\", e.toString());\n                mDelivery.postError(request, new VolleyError(e));\n            }\n        }\n    }\n\n    private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {\n        error = request.parseNetworkError(error);\n        mDelivery.postError(request, error);\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/NetworkError.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley;\n\nimport com.android.volley.NetworkResponse;\nimport com.android.volley.VolleyError;\n\n/**\n * Indicates that there was a network error when performing a Volley request.\n */\n@SuppressWarnings(\"serial\")\npublic class NetworkError extends VolleyError {\n    public NetworkError() {\n        super();\n    }\n\n    public NetworkError(Throwable cause) {\n        super(cause);\n    }\n\n    public NetworkError(NetworkResponse networkResponse) {\n        super(networkResponse);\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/NetworkResponse.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley;\n\nimport org.apache.http.HttpStatus;\n\nimport java.util.Collections;\nimport java.util.Map;\n\n/**\n * Data and headers returned from {@link Network#performRequest(Request)}.\n */\npublic class NetworkResponse {\n    /**\n     * Creates a new network response.\n     * @param statusCode the HTTP status code\n     * @param data Response body\n     * @param headers Headers returned with this response, or null for none\n     * @param notModified True if the server returned a 304 and the data was already in cache\n     */\n    public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,\n            boolean notModified) {\n        this.statusCode = statusCode;\n        this.data = data;\n        this.headers = headers;\n        this.notModified = notModified;\n    }\n\n    public NetworkResponse(byte[] data) {\n        this(HttpStatus.SC_OK, data, Collections.<String, String>emptyMap(), false);\n    }\n\n    public NetworkResponse(byte[] data, Map<String, String> headers) {\n        this(HttpStatus.SC_OK, data, headers, false);\n    }\n\n    /** The HTTP status code. */\n    public final int statusCode;\n\n    /** Raw data from this response. */\n    public final byte[] data;\n\n    /** Response headers. */\n    public final Map<String, String> headers;\n\n    /** True if the server returned a 304 (Not Modified). */\n    public final boolean notModified;\n}"
  },
  {
    "path": "WayHoo/src/com/android/volley/NoConnectionError.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley;\n\n/**\n * Error indicating that no connection could be established when performing a Volley request.\n */\n@SuppressWarnings(\"serial\")\npublic class NoConnectionError extends NetworkError {\n    public NoConnectionError() {\n        super();\n    }\n\n    public NoConnectionError(Throwable reason) {\n        super(reason);\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/ParseError.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley;\n\nimport com.android.volley.NetworkResponse;\nimport com.android.volley.VolleyError;\n\n/**\n * Indicates that the server's response could not be parsed.\n */\n@SuppressWarnings(\"serial\")\npublic class ParseError extends VolleyError {\n    public ParseError() { }\n\n    public ParseError(NetworkResponse networkResponse) {\n        super(networkResponse);\n    }\n\n    public ParseError(Throwable cause) {\n        super(cause);\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/Request.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley;\n\nimport android.net.TrafficStats;\nimport android.net.Uri;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.os.SystemClock;\nimport android.text.TextUtils;\n\nimport com.android.volley.VolleyLog.MarkerLog;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\nimport java.util.Collections;\nimport java.util.Map;\n\n/**\n * Base class for all network requests.\n *\n * @param <T> The type of parsed response this request expects.\n */\npublic abstract class Request<T> implements Comparable<Request<T>> {\n\n    /**\n     * Default encoding for POST or PUT parameters. See {@link #getParamsEncoding()}.\n     */\n    private static final String DEFAULT_PARAMS_ENCODING = \"UTF-8\";\n\n    /**\n     * Supported request methods.\n     */\n    public interface Method {\n        int DEPRECATED_GET_OR_POST = -1;\n        int GET = 0;\n        int POST = 1;\n        int PUT = 2;\n        int DELETE = 3;\n        int HEAD = 4;\n        int OPTIONS = 5;\n        int TRACE = 6;\n        int PATCH = 7;\n    }\n\n    /** An event log tracing the lifetime of this request; for debugging. */\n    private final MarkerLog mEventLog = MarkerLog.ENABLED ? new MarkerLog() : null;\n\n    /**\n     * Request method of this request.  Currently supports GET, POST, PUT, DELETE, HEAD, OPTIONS,\n     * TRACE, and PATCH.\n     */\n    private final int mMethod;\n\n    /** URL of this request. */\n    private final String mUrl;\n\n    /** Default tag for {@link TrafficStats}. */\n    private final int mDefaultTrafficStatsTag;\n\n    /** Listener interface for errors. */\n    private final Response.ErrorListener mErrorListener;\n\n    /** Sequence number of this request, used to enforce FIFO ordering. */\n    private Integer mSequence;\n\n    /** The request queue this request is associated with. */\n    private RequestQueue mRequestQueue;\n\n    /** Whether or not responses to this request should be cached. */\n    private boolean mShouldCache = true;\n\n    /** Whether or not this request has been canceled. */\n    private boolean mCanceled = false;\n\n    /** Whether or not a response has been delivered for this request yet. */\n    private boolean mResponseDelivered = false;\n\n    // A cheap variant of request tracing used to dump slow requests.\n    private long mRequestBirthTime = 0;\n\n    /** Threshold at which we should log the request (even when debug logging is not enabled). */\n    private static final long SLOW_REQUEST_THRESHOLD_MS = 3000;\n\n    /** The retry policy for this request. */\n    private RetryPolicy mRetryPolicy;\n\n    /**\n     * When a request can be retrieved from cache but must be refreshed from\n     * the network, the cache entry will be stored here so that in the event of\n     * a \"Not Modified\" response, we can be sure it hasn't been evicted from cache.\n     */\n    private Cache.Entry mCacheEntry = null;\n\n    /** An opaque token tagging this request; used for bulk cancellation. */\n    private Object mTag;\n\n    /**\n     * Creates a new request with the given URL and error listener.  Note that\n     * the normal response listener is not provided here as delivery of responses\n     * is provided by subclasses, who have a better idea of how to deliver an\n     * already-parsed response.\n     *\n     * @deprecated Use {@link #Request(int, String, com.android.volley.Response.ErrorListener)}.\n     */\n    @Deprecated\n    public Request(String url, Response.ErrorListener listener) {\n        this(Method.DEPRECATED_GET_OR_POST, url, listener);\n    }\n\n    /**\n     * Creates a new request with the given method (one of the values from {@link Method}),\n     * URL, and error listener.  Note that the normal response listener is not provided here as\n     * delivery of responses is provided by subclasses, who have a better idea of how to deliver\n     * an already-parsed response.\n     */\n    public Request(int method, String url, Response.ErrorListener listener) {\n        mMethod = method;\n        mUrl = url;\n        mErrorListener = listener;\n        setRetryPolicy(new DefaultRetryPolicy());\n\n        mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);\n    }\n\n    /**\n     * Return the method for this request.  Can be one of the values in {@link Method}.\n     */\n    public int getMethod() {\n        return mMethod;\n    }\n\n    /**\n     * Set a tag on this request. Can be used to cancel all requests with this\n     * tag by {@link RequestQueue#cancelAll(Object)}.\n     *\n     * @return This Request object to allow for chaining.\n     */\n    public Request<?> setTag(Object tag) {\n        mTag = tag;\n        return this;\n    }\n\n    /**\n     * Returns this request's tag.\n     * @see Request#setTag(Object)\n     */\n    public Object getTag() {\n        return mTag;\n    }\n\n    /**\n     * @return A tag for use with {@link TrafficStats#setThreadStatsTag(int)}\n     */\n    public int getTrafficStatsTag() {\n        return mDefaultTrafficStatsTag;\n    }\n\n    /**\n     * @return The hashcode of the URL's host component, or 0 if there is none.\n     */\n    private static int findDefaultTrafficStatsTag(String url) {\n        if (!TextUtils.isEmpty(url)) {\n            Uri uri = Uri.parse(url);\n            if (uri != null) {\n                String host = uri.getHost();\n                if (host != null) {\n                    return host.hashCode();\n                }\n            }\n        }\n        return 0;\n    }\n\n    /**\n     * Sets the retry policy for this request.\n     *\n     * @return This Request object to allow for chaining.\n     */\n    public Request<?> setRetryPolicy(RetryPolicy retryPolicy) {\n        mRetryPolicy = retryPolicy;\n        return this;\n    }\n\n    /**\n     * Adds an event to this request's event log; for debugging.\n     */\n    public void addMarker(String tag) {\n        if (MarkerLog.ENABLED) {\n            mEventLog.add(tag, Thread.currentThread().getId());\n        } else if (mRequestBirthTime == 0) {\n            mRequestBirthTime = SystemClock.elapsedRealtime();\n        }\n    }\n\n    /**\n     * Notifies the request queue that this request has finished (successfully or with error).\n     *\n     * <p>Also dumps all events from this request's event log; for debugging.</p>\n     */\n    void finish(final String tag) {\n        if (mRequestQueue != null) {\n            mRequestQueue.finish(this);\n        }\n        if (MarkerLog.ENABLED) {\n            final long threadId = Thread.currentThread().getId();\n            if (Looper.myLooper() != Looper.getMainLooper()) {\n                // If we finish marking off of the main thread, we need to\n                // actually do it on the main thread to ensure correct ordering.\n                Handler mainThread = new Handler(Looper.getMainLooper());\n                mainThread.post(new Runnable() {\n                    @Override\n                    public void run() {\n                        mEventLog.add(tag, threadId);\n                        mEventLog.finish(this.toString());\n                    }\n                });\n                return;\n            }\n\n            mEventLog.add(tag, threadId);\n            mEventLog.finish(this.toString());\n        } else {\n            long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime;\n            if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) {\n                VolleyLog.d(\"%d ms: %s\", requestTime, this.toString());\n            }\n        }\n    }\n\n    /**\n     * Associates this request with the given queue. The request queue will be notified when this\n     * request has finished.\n     *\n     * @return This Request object to allow for chaining.\n     */\n    public Request<?> setRequestQueue(RequestQueue requestQueue) {\n        mRequestQueue = requestQueue;\n        return this;\n    }\n\n    /**\n     * Sets the sequence number of this request.  Used by {@link RequestQueue}.\n     *\n     * @return This Request object to allow for chaining.\n     */\n    public final Request<?> setSequence(int sequence) {\n        mSequence = sequence;\n        return this;\n    }\n\n    /**\n     * Returns the sequence number of this request.\n     */\n    public final int getSequence() {\n        if (mSequence == null) {\n            throw new IllegalStateException(\"getSequence called before setSequence\");\n        }\n        return mSequence;\n    }\n\n    /**\n     * Returns the URL of this request.\n     */\n    public String getUrl() {\n        return mUrl;\n    }\n\n    /**\n     * Returns the cache key for this request.  By default, this is the URL.\n     */\n    public String getCacheKey() {\n        return getUrl();\n    }\n\n    /**\n     * Annotates this request with an entry retrieved for it from cache.\n     * Used for cache coherency support.\n     *\n     * @return This Request object to allow for chaining.\n     */\n    public Request<?> setCacheEntry(Cache.Entry entry) {\n        mCacheEntry = entry;\n        return this;\n    }\n\n    /**\n     * Returns the annotated cache entry, or null if there isn't one.\n     */\n    public Cache.Entry getCacheEntry() {\n        return mCacheEntry;\n    }\n\n    /**\n     * Mark this request as canceled.  No callback will be delivered.\n     */\n    public void cancel() {\n        mCanceled = true;\n    }\n\n    /**\n     * Returns true if this request has been canceled.\n     */\n    public boolean isCanceled() {\n        return mCanceled;\n    }\n\n    /**\n     * Returns a list of extra HTTP headers to go along with this request. Can\n     * throw {@link AuthFailureError} as authentication may be required to\n     * provide these values.\n     * @throws AuthFailureError In the event of auth failure\n     */\n    public Map<String, String> getHeaders() throws AuthFailureError {\n        return Collections.emptyMap();\n    }\n\n    /**\n     * Returns a Map of POST parameters to be used for this request, or null if\n     * a simple GET should be used.  Can throw {@link AuthFailureError} as\n     * authentication may be required to provide these values.\n     *\n     * <p>Note that only one of getPostParams() and getPostBody() can return a non-null\n     * value.</p>\n     * @throws AuthFailureError In the event of auth failure\n     *\n     * @deprecated Use {@link #getParams()} instead.\n     */\n    @Deprecated\n    protected Map<String, String> getPostParams() throws AuthFailureError {\n        return getParams();\n    }\n\n    /**\n     * Returns which encoding should be used when converting POST parameters returned by\n     * {@link #getPostParams()} into a raw POST body.\n     *\n     * <p>This controls both encodings:\n     * <ol>\n     *     <li>The string encoding used when converting parameter names and values into bytes prior\n     *         to URL encoding them.</li>\n     *     <li>The string encoding used when converting the URL encoded parameters into a raw\n     *         byte array.</li>\n     * </ol>\n     *\n     * @deprecated Use {@link #getParamsEncoding()} instead.\n     */\n    @Deprecated\n    protected String getPostParamsEncoding() {\n        return getParamsEncoding();\n    }\n\n    /**\n     * @deprecated Use {@link #getBodyContentType()} instead.\n     */\n    @Deprecated\n    public String getPostBodyContentType() {\n        return getBodyContentType();\n    }\n\n    /**\n     * Returns the raw POST body to be sent.\n     *\n     * @throws AuthFailureError In the event of auth failure\n     *\n     * @deprecated Use {@link #getBody()} instead.\n     */\n    @Deprecated\n    public byte[] getPostBody() throws AuthFailureError {\n        // Note: For compatibility with legacy clients of volley, this implementation must remain\n        // here instead of simply calling the getBody() function because this function must\n        // call getPostParams() and getPostParamsEncoding() since legacy clients would have\n        // overridden these two member functions for POST requests.\n        Map<String, String> postParams = getPostParams();\n        if (postParams != null && postParams.size() > 0) {\n            return encodeParameters(postParams, getPostParamsEncoding());\n        }\n        return null;\n    }\n\n    /**\n     * Returns a Map of parameters to be used for a POST or PUT request.  Can throw\n     * {@link AuthFailureError} as authentication may be required to provide these values.\n     *\n     * <p>Note that you can directly override {@link #getBody()} for custom data.</p>\n     *\n     * @throws AuthFailureError in the event of auth failure\n     */\n    protected Map<String, String> getParams() throws AuthFailureError {\n        return null;\n    }\n\n    /**\n     * Returns which encoding should be used when converting POST or PUT parameters returned by\n     * {@link #getParams()} into a raw POST or PUT body.\n     *\n     * <p>This controls both encodings:\n     * <ol>\n     *     <li>The string encoding used when converting parameter names and values into bytes prior\n     *         to URL encoding them.</li>\n     *     <li>The string encoding used when converting the URL encoded parameters into a raw\n     *         byte array.</li>\n     * </ol>\n     */\n    protected String getParamsEncoding() {\n        return DEFAULT_PARAMS_ENCODING;\n    }\n\n    public String getBodyContentType() {\n        return \"application/x-www-form-urlencoded; charset=\" + getParamsEncoding();\n    }\n\n    /**\n     * Returns the raw POST or PUT body to be sent.\n     *\n     * @throws AuthFailureError in the event of auth failure\n     */\n    public byte[] getBody() throws AuthFailureError {\n        Map<String, String> params = getParams();\n        if (params != null && params.size() > 0) {\n            return encodeParameters(params, getParamsEncoding());\n        }\n        return null;\n    }\n\n    /**\n     * Converts <code>params</code> into an application/x-www-form-urlencoded encoded string.\n     */\n    private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) {\n        StringBuilder encodedParams = new StringBuilder();\n        try {\n            for (Map.Entry<String, String> entry : params.entrySet()) {\n                encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));\n                encodedParams.append('=');\n                encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));\n                encodedParams.append('&');\n            }\n            return encodedParams.toString().getBytes(paramsEncoding);\n        } catch (UnsupportedEncodingException uee) {\n            throw new RuntimeException(\"Encoding not supported: \" + paramsEncoding, uee);\n        }\n    }\n\n    /**\n     * Set whether or not responses to this request should be cached.\n     *\n     * @return This Request object to allow for chaining.\n     */\n    public final Request<?> setShouldCache(boolean shouldCache) {\n        mShouldCache = shouldCache;\n        return this;\n    }\n\n    /**\n     * Returns true if responses to this request should be cached.\n     */\n    public final boolean shouldCache() {\n        return mShouldCache;\n    }\n\n    /**\n     * Priority values.  Requests will be processed from higher priorities to\n     * lower priorities, in FIFO order.\n     */\n    public enum Priority {\n        LOW,\n        NORMAL,\n        HIGH,\n        IMMEDIATE\n    }\n\n    /**\n     * Returns the {@link Priority} of this request; {@link Priority#NORMAL} by default.\n     */\n    public Priority getPriority() {\n        return Priority.NORMAL;\n    }\n\n    /**\n     * Returns the socket timeout in milliseconds per retry attempt. (This value can be changed\n     * per retry attempt if a backoff is specified via backoffTimeout()). If there are no retry\n     * attempts remaining, this will cause delivery of a {@link TimeoutError} error.\n     */\n    public final int getTimeoutMs() {\n        return mRetryPolicy.getCurrentTimeout();\n    }\n\n    /**\n     * Returns the retry policy that should be used  for this request.\n     */\n    public RetryPolicy getRetryPolicy() {\n        return mRetryPolicy;\n    }\n\n    /**\n     * Mark this request as having a response delivered on it.  This can be used\n     * later in the request's lifetime for suppressing identical responses.\n     */\n    public void markDelivered() {\n        mResponseDelivered = true;\n    }\n\n    /**\n     * Returns true if this request has had a response delivered for it.\n     */\n    public boolean hasHadResponseDelivered() {\n        return mResponseDelivered;\n    }\n\n    /**\n     * Subclasses must implement this to parse the raw network response\n     * and return an appropriate response type. This method will be\n     * called from a worker thread.  The response will not be delivered\n     * if you return null.\n     * @param response Response from the network\n     * @return The parsed response, or null in the case of an error\n     */\n    abstract protected Response<T> parseNetworkResponse(NetworkResponse response);\n\n    /**\n     * Subclasses can override this method to parse 'networkError' and return a more specific error.\n     *\n     * <p>The default implementation just returns the passed 'networkError'.</p>\n     *\n     * @param volleyError the error retrieved from the network\n     * @return an NetworkError augmented with additional information\n     */\n    protected VolleyError parseNetworkError(VolleyError volleyError) {\n        return volleyError;\n    }\n\n    /**\n     * Subclasses must implement this to perform delivery of the parsed\n     * response to their listeners.  The given response is guaranteed to\n     * be non-null; responses that fail to parse are not delivered.\n     * @param response The parsed response returned by\n     * {@link #parseNetworkResponse(NetworkResponse)}\n     */\n    abstract protected void deliverResponse(T response);\n\n    /**\n     * Delivers error message to the ErrorListener that the Request was\n     * initialized with.\n     *\n     * @param error Error details\n     */\n    public void deliverError(VolleyError error) {\n        if (mErrorListener != null) {\n            mErrorListener.onErrorResponse(error);\n        }\n    }\n\n    /**\n     * Our comparator sorts from high to low priority, and secondarily by\n     * sequence number to provide FIFO ordering.\n     */\n    @Override\n    public int compareTo(Request<T> other) {\n        Priority left = this.getPriority();\n        Priority right = other.getPriority();\n\n        // High-priority requests are \"lesser\" so they are sorted to the front.\n        // Equal priorities are sorted by sequence number to provide FIFO ordering.\n        return left == right ?\n                this.mSequence - other.mSequence :\n                right.ordinal() - left.ordinal();\n    }\n\n    @Override\n    public String toString() {\n        String trafficStatsTag = \"0x\" + Integer.toHexString(getTrafficStatsTag());\n        return (mCanceled ? \"[X] \" : \"[ ] \") + getUrl() + \" \" + trafficStatsTag + \" \"\n                + getPriority() + \" \" + mSequence;\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/RequestQueue.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley;\n\nimport android.os.Handler;\nimport android.os.Looper;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedList;\nimport java.util.Map;\nimport java.util.Queue;\nimport java.util.Set;\nimport java.util.concurrent.PriorityBlockingQueue;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * A request dispatch queue with a thread pool of dispatchers.\n *\n * Calling {@link #add(Request)} will enqueue the given Request for dispatch,\n * resolving from either cache or network on a worker thread, and then delivering\n * a parsed response on the main thread.\n */\npublic class RequestQueue {\n\n    /** Used for generating monotonically-increasing sequence numbers for requests. */\n    private AtomicInteger mSequenceGenerator = new AtomicInteger();\n\n    /**\n     * Staging area for requests that already have a duplicate request in flight.\n     *\n     * <ul>\n     *     <li>containsKey(cacheKey) indicates that there is a request in flight for the given cache\n     *          key.</li>\n     *     <li>get(cacheKey) returns waiting requests for the given cache key. The in flight request\n     *          is <em>not</em> contained in that list. Is null if no requests are staged.</li>\n     * </ul>\n     */\n    private final Map<String, Queue<Request<?>>> mWaitingRequests =\n            new HashMap<String, Queue<Request<?>>>();\n\n    /**\n     * The set of all requests currently being processed by this RequestQueue. A Request\n     * will be in this set if it is waiting in any queue or currently being processed by\n     * any dispatcher.\n     */\n    private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();\n\n    /** The cache triage queue. */\n    private final PriorityBlockingQueue<Request<?>> mCacheQueue =\n        new PriorityBlockingQueue<Request<?>>();\n\n    /** The queue of requests that are actually going out to the network. */\n    private final PriorityBlockingQueue<Request<?>> mNetworkQueue =\n        new PriorityBlockingQueue<Request<?>>();\n\n    /** Number of network request dispatcher threads to start. */\n    private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;\n\n    /** Cache interface for retrieving and storing responses. */\n    private final Cache mCache;\n\n    /** Network interface for performing requests. */\n    private final Network mNetwork;\n\n    /** Response delivery mechanism. */\n    private final ResponseDelivery mDelivery;\n\n    /** The network dispatchers. */\n    private NetworkDispatcher[] mDispatchers;\n\n    /** The cache dispatcher. */\n    private CacheDispatcher mCacheDispatcher;\n\n    /**\n     * Creates the worker pool. Processing will not begin until {@link #start()} is called.\n     *\n     * @param cache A Cache to use for persisting responses to disk\n     * @param network A Network interface for performing HTTP requests\n     * @param threadPoolSize Number of network dispatcher threads to create\n     * @param delivery A ResponseDelivery interface for posting responses and errors\n     */\n    public RequestQueue(Cache cache, Network network, int threadPoolSize,\n            ResponseDelivery delivery) {\n        mCache = cache;\n        mNetwork = network;\n        mDispatchers = new NetworkDispatcher[threadPoolSize];\n        mDelivery = delivery;\n    }\n\n    /**\n     * Creates the worker pool. Processing will not begin until {@link #start()} is called.\n     *\n     * @param cache A Cache to use for persisting responses to disk\n     * @param network A Network interface for performing HTTP requests\n     * @param threadPoolSize Number of network dispatcher threads to create\n     */\n    public RequestQueue(Cache cache, Network network, int threadPoolSize) {\n        this(cache, network, threadPoolSize,\n                new ExecutorDelivery(new Handler(Looper.getMainLooper())));\n    }\n\n    /**\n     * Creates the worker pool. Processing will not begin until {@link #start()} is called.\n     *\n     * @param cache A Cache to use for persisting responses to disk\n     * @param network A Network interface for performing HTTP requests\n     */\n    public RequestQueue(Cache cache, Network network) {\n        this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);\n    }\n\n    /**\n     * Starts the dispatchers in this queue.\n     */\n    public void start() {\n        stop();  // Make sure any currently running dispatchers are stopped.\n        // Create the cache dispatcher and start it.\n        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);\n        mCacheDispatcher.start();\n\n        // Create network dispatchers (and corresponding threads) up to the pool size.\n        for (int i = 0; i < mDispatchers.length; i++) {\n            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,\n                    mCache, mDelivery);\n            mDispatchers[i] = networkDispatcher;\n            networkDispatcher.start();\n        }\n    }\n\n    /**\n     * Stops the cache and network dispatchers.\n     */\n    public void stop() {\n        if (mCacheDispatcher != null) {\n            mCacheDispatcher.quit();\n        }\n        for (int i = 0; i < mDispatchers.length; i++) {\n            if (mDispatchers[i] != null) {\n                mDispatchers[i].quit();\n            }\n        }\n    }\n\n    /**\n     * Gets a sequence number.\n     */\n    public int getSequenceNumber() {\n        return mSequenceGenerator.incrementAndGet();\n    }\n\n    /**\n     * Gets the {@link Cache} instance being used.\n     */\n    public Cache getCache() {\n        return mCache;\n    }\n\n    /**\n     * A simple predicate or filter interface for Requests, for use by\n     * {@link RequestQueue#cancelAll(RequestFilter)}.\n     */\n    public interface RequestFilter {\n        public boolean apply(Request<?> request);\n    }\n\n    /**\n     * Cancels all requests in this queue for which the given filter applies.\n     * @param filter The filtering function to use\n     */\n    public void cancelAll(RequestFilter filter) {\n        synchronized (mCurrentRequests) {\n            for (Request<?> request : mCurrentRequests) {\n                if (filter.apply(request)) {\n                    request.cancel();\n                }\n            }\n        }\n    }\n\n    /**\n     * Cancels all requests in this queue with the given tag. Tag must be non-null\n     * and equality is by identity.\n     */\n    public void cancelAll(final Object tag) {\n        if (tag == null) {\n            throw new IllegalArgumentException(\"Cannot cancelAll with a null tag\");\n        }\n        cancelAll(new RequestFilter() {\n            @Override\n            public boolean apply(Request<?> request) {\n                return request.getTag() == tag;\n            }\n        });\n    }\n\n    /**\n     * Adds a Request to the dispatch queue.\n     * @param request The request to service\n     * @return The passed-in request\n     */\n    public <T> Request<T> add(Request<T> request) {\n        // Tag the request as belonging to this queue and add it to the set of current requests.\n        request.setRequestQueue(this);\n        synchronized (mCurrentRequests) {\n            mCurrentRequests.add(request);\n        }\n\n        // Process requests in the order they are added.\n        request.setSequence(getSequenceNumber());\n        request.addMarker(\"add-to-queue\");\n\n        // If the request is uncacheable, skip the cache queue and go straight to the network.\n        if (!request.shouldCache()) {\n            mNetworkQueue.add(request);\n            return request;\n        }\n\n        // Insert request into stage if there's already a request with the same cache key in flight.\n        synchronized (mWaitingRequests) {\n            String cacheKey = request.getCacheKey();\n            if (mWaitingRequests.containsKey(cacheKey)) {\n                // There is already a request in flight. Queue up.\n                Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);\n                if (stagedRequests == null) {\n                    stagedRequests = new LinkedList<Request<?>>();\n                }\n                stagedRequests.add(request);\n                mWaitingRequests.put(cacheKey, stagedRequests);\n                if (VolleyLog.DEBUG) {\n                    VolleyLog.v(\"Request for cacheKey=%s is in flight, putting on hold.\", cacheKey);\n                }\n            } else {\n                // Insert 'null' queue for this cacheKey, indicating there is now a request in\n                // flight.\n                mWaitingRequests.put(cacheKey, null);\n                mCacheQueue.add(request);\n            }\n            return request;\n        }\n    }\n\n    /**\n     * Called from {@link Request#finish(String)}, indicating that processing of the given request\n     * has finished.\n     *\n     * <p>Releases waiting requests for <code>request.getCacheKey()</code> if\n     *      <code>request.shouldCache()</code>.</p>\n     */\n    void finish(Request<?> request) {\n        // Remove from the set of requests currently being processed.\n        synchronized (mCurrentRequests) {\n            mCurrentRequests.remove(request);\n        }\n\n        if (request.shouldCache()) {\n            synchronized (mWaitingRequests) {\n                String cacheKey = request.getCacheKey();\n                Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);\n                if (waitingRequests != null) {\n                    if (VolleyLog.DEBUG) {\n                        VolleyLog.v(\"Releasing %d waiting requests for cacheKey=%s.\",\n                                waitingRequests.size(), cacheKey);\n                    }\n                    // Process all queued up requests. They won't be considered as in flight, but\n                    // that's not a problem as the cache has been primed by 'request'.\n                    mCacheQueue.addAll(waitingRequests);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/Response.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley;\n\n/**\n * Encapsulates a parsed response for delivery.\n *\n * @param <T> Parsed type of this response\n */\npublic class Response<T> {\n\n    /** Callback interface for delivering parsed responses. */\n    public interface Listener<T> {\n        /** Called when a response is received. */\n        public void onResponse(T response);\n    }\n\n    /** Callback interface for delivering error responses. */\n    public interface ErrorListener {\n        /**\n         * Callback method that an error has been occurred with the\n         * provided error code and optional user-readable message.\n         */\n        public void onErrorResponse(VolleyError error);\n    }\n\n    /** Returns a successful response containing the parsed result. */\n    public static <T> Response<T> success(T result, Cache.Entry cacheEntry) {\n        return new Response<T>(result, cacheEntry);\n    }\n\n    /**\n     * Returns a failed response containing the given error code and an optional\n     * localized message displayed to the user.\n     */\n    public static <T> Response<T> error(VolleyError error) {\n        return new Response<T>(error);\n    }\n\n    /** Parsed response, or null in the case of error. */\n    public final T result;\n\n    /** Cache metadata for this response, or null in the case of error. */\n    public final Cache.Entry cacheEntry;\n\n    /** Detailed error information if <code>errorCode != OK</code>. */\n    public final VolleyError error;\n\n    /** True if this response was a soft-expired one and a second one MAY be coming. */\n    public boolean intermediate = false;\n\n    /**\n     * Returns whether this response is considered successful.\n     */\n    public boolean isSuccess() {\n        return error == null;\n    }\n\n\n    private Response(T result, Cache.Entry cacheEntry) {\n        this.result = result;\n        this.cacheEntry = cacheEntry;\n        this.error = null;\n    }\n\n    private Response(VolleyError error) {\n        this.result = null;\n        this.cacheEntry = null;\n        this.error = error;\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/ResponseDelivery.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley;\n\npublic interface ResponseDelivery {\n    /**\n     * Parses a response from the network or cache and delivers it.\n     */\n    public void postResponse(Request<?> request, Response<?> response);\n\n    /**\n     * Parses a response from the network or cache and delivers it. The provided\n     * Runnable will be executed after delivery.\n     */\n    public void postResponse(Request<?> request, Response<?> response, Runnable runnable);\n\n    /**\n     * Posts an error for the given request.\n     */\n    public void postError(Request<?> request, VolleyError error);\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/RetryPolicy.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley;\n\n/**\n * Retry policy for a request.\n */\npublic interface RetryPolicy {\n\n    /**\n     * Returns the current timeout (used for logging).\n     */\n    public int getCurrentTimeout();\n\n    /**\n     * Returns the current retry count (used for logging).\n     */\n    public int getCurrentRetryCount();\n\n    /**\n     * Prepares for the next retry by applying a backoff to the timeout.\n     * @param error The error code of the last attempt.\n     * @throws VolleyError In the event that the retry could not be performed (for example if we\n     * ran out of attempts), the passed in error is thrown.\n     */\n    public void retry(VolleyError error) throws VolleyError;\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/ServerError.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley;\n\nimport com.android.volley.NetworkResponse;\nimport com.android.volley.VolleyError;\n\n/**\n * Indicates that the error responded with an error response.\n */\n@SuppressWarnings(\"serial\")\npublic class ServerError extends VolleyError {\n    public ServerError(NetworkResponse networkResponse) {\n        super(networkResponse);\n    }\n\n    public ServerError() {\n        super();\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/TimeoutError.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley;\n\n/**\n * Indicates that the connection or the socket timed out.\n */\n@SuppressWarnings(\"serial\")\npublic class TimeoutError extends VolleyError { }\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/VolleyError.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley;\n\n/**\n * Exception style class encapsulating Volley errors\n */\n@SuppressWarnings(\"serial\")\npublic class VolleyError extends Exception {\n    public final NetworkResponse networkResponse;\n\n    public VolleyError() {\n        networkResponse = null;\n    }\n\n    public VolleyError(NetworkResponse response) {\n        networkResponse = response;\n    }\n\n    public VolleyError(String exceptionMessage) {\n       super(exceptionMessage);\n       networkResponse = null;\n    }\n\n    public VolleyError(String exceptionMessage, Throwable reason) {\n        super(exceptionMessage, reason);\n        networkResponse = null;\n    }\n\n    public VolleyError(Throwable cause) {\n        super(cause);\n        networkResponse = null;\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/VolleyLog.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley;\n\nimport android.os.SystemClock;\nimport android.util.Log;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\n\n/** Logging helper class. */\npublic class VolleyLog {\n    public static String TAG = \"Volley\";\n\n    public static boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE);\n\n    /**\n     * Customize the log tag for your application, so that other apps\n     * using Volley don't mix their logs with yours.\n     * <br />\n     * Enable the log property for your tag before starting your app:\n     * <br />\n     * {@code adb shell setprop log.tag.&lt;tag&gt;}\n     */\n    public static void setTag(String tag) {\n        d(\"Changing log tag to %s\", tag);\n        TAG = tag;\n\n        // Reinitialize the DEBUG \"constant\"\n        DEBUG = Log.isLoggable(TAG, Log.VERBOSE);\n    }\n\n    public static void v(String format, Object... args) {\n        if (DEBUG) {\n            Log.v(TAG, buildMessage(format, args));\n        }\n    }\n\n    public static void d(String format, Object... args) {\n        Log.d(TAG, buildMessage(format, args));\n    }\n\n    public static void e(String format, Object... args) {\n        Log.e(TAG, buildMessage(format, args));\n    }\n\n    public static void e(Throwable tr, String format, Object... args) {\n        Log.e(TAG, buildMessage(format, args), tr);\n    }\n\n    public static void wtf(String format, Object... args) {\n        Log.wtf(TAG, buildMessage(format, args));\n    }\n\n    public static void wtf(Throwable tr, String format, Object... args) {\n        Log.wtf(TAG, buildMessage(format, args), tr);\n    }\n\n    /**\n     * Formats the caller's provided message and prepends useful info like\n     * calling thread ID and method name.\n     */\n    private static String buildMessage(String format, Object... args) {\n        String msg = (args == null) ? format : String.format(Locale.US, format, args);\n        StackTraceElement[] trace = new Throwable().fillInStackTrace().getStackTrace();\n\n        String caller = \"<unknown>\";\n        // Walk up the stack looking for the first caller outside of VolleyLog.\n        // It will be at least two frames up, so start there.\n        for (int i = 2; i < trace.length; i++) {\n            Class<?> clazz = trace[i].getClass();\n            if (!clazz.equals(VolleyLog.class)) {\n                String callingClass = trace[i].getClassName();\n                callingClass = callingClass.substring(callingClass.lastIndexOf('.') + 1);\n                callingClass = callingClass.substring(callingClass.lastIndexOf('$') + 1);\n\n                caller = callingClass + \".\" + trace[i].getMethodName();\n                break;\n            }\n        }\n        return String.format(Locale.US, \"[%d] %s: %s\",\n                Thread.currentThread().getId(), caller, msg);\n    }\n\n    /**\n     * A simple event log with records containing a name, thread ID, and timestamp.\n     */\n    static class MarkerLog {\n        public static final boolean ENABLED = VolleyLog.DEBUG;\n\n        /** Minimum duration from first marker to last in an marker log to warrant logging. */\n        private static final long MIN_DURATION_FOR_LOGGING_MS = 0;\n\n        private static class Marker {\n            public final String name;\n            public final long thread;\n            public final long time;\n\n            public Marker(String name, long thread, long time) {\n                this.name = name;\n                this.thread = thread;\n                this.time = time;\n            }\n        }\n\n        private final List<Marker> mMarkers = new ArrayList<Marker>();\n        private boolean mFinished = false;\n\n        /** Adds a marker to this log with the specified name. */\n        public synchronized void add(String name, long threadId) {\n            if (mFinished) {\n                throw new IllegalStateException(\"Marker added to finished log\");\n            }\n\n            mMarkers.add(new Marker(name, threadId, SystemClock.elapsedRealtime()));\n        }\n\n        /**\n         * Closes the log, dumping it to logcat if the time difference between\n         * the first and last markers is greater than {@link #MIN_DURATION_FOR_LOGGING_MS}.\n         * @param header Header string to print above the marker log.\n         */\n        public synchronized void finish(String header) {\n            mFinished = true;\n\n            long duration = getTotalDuration();\n            if (duration <= MIN_DURATION_FOR_LOGGING_MS) {\n                return;\n            }\n\n            long prevTime = mMarkers.get(0).time;\n            d(\"(%-4d ms) %s\", duration, header);\n            for (Marker marker : mMarkers) {\n                long thisTime = marker.time;\n                d(\"(+%-4d) [%2d] %s\", (thisTime - prevTime), marker.thread, marker.name);\n                prevTime = thisTime;\n            }\n        }\n\n        @Override\n        protected void finalize() throws Throwable {\n            // Catch requests that have been collected (and hence end-of-lifed)\n            // but had no debugging output printed for them.\n            if (!mFinished) {\n                finish(\"Request on the loose\");\n                e(\"Marker log finalized without finish() - uncaught exit point for request\");\n            }\n        }\n\n        /** Returns the time difference between the first and last events in this log. */\n        private long getTotalDuration() {\n            if (mMarkers.size() == 0) {\n                return 0;\n            }\n\n            long first = mMarkers.get(0).time;\n            long last = mMarkers.get(mMarkers.size() - 1).time;\n            return last - first;\n        }\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/toolbox/AndroidAuthenticator.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley.toolbox;\n\nimport com.android.volley.AuthFailureError;\n\nimport android.accounts.Account;\nimport android.accounts.AccountManager;\nimport android.accounts.AccountManagerFuture;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\n\n/**\n * An Authenticator that uses {@link AccountManager} to get auth\n * tokens of a specified type for a specified account.\n */\npublic class AndroidAuthenticator implements Authenticator {\n    private final Context mContext;\n    private final Account mAccount;\n    private final String mAuthTokenType;\n    private final boolean mNotifyAuthFailure;\n\n    /**\n     * Creates a new authenticator.\n     * @param context Context for accessing AccountManager\n     * @param account Account to authenticate as\n     * @param authTokenType Auth token type passed to AccountManager\n     */\n    public AndroidAuthenticator(Context context, Account account, String authTokenType) {\n        this(context, account, authTokenType, false);\n    }\n\n    /**\n     * Creates a new authenticator.\n     * @param context Context for accessing AccountManager\n     * @param account Account to authenticate as\n     * @param authTokenType Auth token type passed to AccountManager\n     * @param notifyAuthFailure Whether to raise a notification upon auth failure\n     */\n    public AndroidAuthenticator(Context context, Account account, String authTokenType,\n            boolean notifyAuthFailure) {\n        mContext = context;\n        mAccount = account;\n        mAuthTokenType = authTokenType;\n        mNotifyAuthFailure = notifyAuthFailure;\n    }\n\n    /**\n     * Returns the Account being used by this authenticator.\n     */\n    public Account getAccount() {\n        return mAccount;\n    }\n\n    // TODO: Figure out what to do about notifyAuthFailure\n    @SuppressWarnings(\"deprecation\")\n    @Override\n    public String getAuthToken() throws AuthFailureError {\n        final AccountManager accountManager = AccountManager.get(mContext);\n        AccountManagerFuture<Bundle> future = accountManager.getAuthToken(mAccount,\n                mAuthTokenType, mNotifyAuthFailure, null, null);\n        Bundle result;\n        try {\n            result = future.getResult();\n        } catch (Exception e) {\n            throw new AuthFailureError(\"Error while retrieving auth token\", e);\n        }\n        String authToken = null;\n        if (future.isDone() && !future.isCancelled()) {\n            if (result.containsKey(AccountManager.KEY_INTENT)) {\n                Intent intent = result.getParcelable(AccountManager.KEY_INTENT);\n                throw new AuthFailureError(intent);\n            }\n            authToken = result.getString(AccountManager.KEY_AUTHTOKEN);\n        }\n        if (authToken == null) {\n            throw new AuthFailureError(\"Got null auth token for type: \" + mAuthTokenType);\n        }\n\n        return authToken;\n    }\n\n    @Override\n    public void invalidateAuthToken(String authToken) {\n        AccountManager.get(mContext).invalidateAuthToken(mAccount.type, authToken);\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/toolbox/Authenticator.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley.toolbox;\n\nimport com.android.volley.AuthFailureError;\n\n/**\n * An interface for interacting with auth tokens.\n */\npublic interface Authenticator {\n    /**\n     * Synchronously retrieves an auth token.\n     *\n     * @throws AuthFailureError If authentication did not succeed\n     */\n    public String getAuthToken() throws AuthFailureError;\n\n    /**\n     * Invalidates the provided auth token.\n     */\n    public void invalidateAuthToken(String authToken);\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/toolbox/BasicNetwork.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley.toolbox;\n\nimport android.os.SystemClock;\n\nimport com.android.volley.AuthFailureError;\nimport com.android.volley.Cache;\nimport com.android.volley.Network;\nimport com.android.volley.NetworkError;\nimport com.android.volley.NetworkResponse;\nimport com.android.volley.NoConnectionError;\nimport com.android.volley.Request;\nimport com.android.volley.RetryPolicy;\nimport com.android.volley.ServerError;\nimport com.android.volley.TimeoutError;\nimport com.android.volley.VolleyError;\nimport com.android.volley.VolleyLog;\n\nimport org.apache.http.Header;\nimport org.apache.http.HttpEntity;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.HttpStatus;\nimport org.apache.http.StatusLine;\nimport org.apache.http.conn.ConnectTimeoutException;\nimport org.apache.http.impl.cookie.DateUtils;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.MalformedURLException;\nimport java.net.SocketTimeoutException;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * A network performing Volley requests over an {@link HttpStack}.\n */\npublic class BasicNetwork implements Network {\n    protected static final boolean DEBUG = VolleyLog.DEBUG;\n\n    private static int SLOW_REQUEST_THRESHOLD_MS = 3000;\n\n    private static int DEFAULT_POOL_SIZE = 4096;\n\n    protected final HttpStack mHttpStack;\n\n    protected final ByteArrayPool mPool;\n\n    /**\n     * @param httpStack HTTP stack to be used\n     */\n    public BasicNetwork(HttpStack httpStack) {\n        // If a pool isn't passed in, then build a small default pool that will give us a lot of\n        // benefit and not use too much memory.\n        this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));\n    }\n\n    /**\n     * @param httpStack HTTP stack to be used\n     * @param pool a buffer pool that improves GC performance in copy operations\n     */\n    public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) {\n        mHttpStack = httpStack;\n        mPool = pool;\n    }\n\n    @Override\n    public NetworkResponse performRequest(Request<?> request) throws VolleyError {\n        long requestStart = SystemClock.elapsedRealtime();\n        while (true) {\n            HttpResponse httpResponse = null;\n            byte[] responseContents = null;\n            Map<String, String> responseHeaders = new HashMap<String, String>();\n            try {\n                // Gather headers.\n                Map<String, String> headers = new HashMap<String, String>();\n                addCacheHeaders(headers, request.getCacheEntry());\n                httpResponse = mHttpStack.performRequest(request, headers);\n                StatusLine statusLine = httpResponse.getStatusLine();\n                int statusCode = statusLine.getStatusCode();\n\n                responseHeaders = convertHeaders(httpResponse.getAllHeaders());\n                // Handle cache validation.\n                if (statusCode == HttpStatus.SC_NOT_MODIFIED) {\n                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,\n                            request.getCacheEntry() == null ? null : request.getCacheEntry().data,\n                            responseHeaders, true);\n                }\n\n                // Some responses such as 204s do not have content.  We must check.\n                if (httpResponse.getEntity() != null) {\n                  responseContents = entityToBytes(httpResponse.getEntity());\n                } else {\n                  // Add 0 byte response as a way of honestly representing a\n                  // no-content request.\n                  responseContents = new byte[0];\n                }\n\n                // if the request is slow, log it.\n                long requestLifetime = SystemClock.elapsedRealtime() - requestStart;\n                logSlowRequests(requestLifetime, request, responseContents, statusLine);\n\n                if (statusCode < 200 || statusCode > 299) {\n                    throw new IOException();\n                }\n                return new NetworkResponse(statusCode, responseContents, responseHeaders, false);\n            } catch (SocketTimeoutException e) {\n                attemptRetryOnException(\"socket\", request, new TimeoutError());\n            } catch (ConnectTimeoutException e) {\n                attemptRetryOnException(\"connection\", request, new TimeoutError());\n            } catch (MalformedURLException e) {\n                throw new RuntimeException(\"Bad URL \" + request.getUrl(), e);\n            } catch (IOException e) {\n                int statusCode = 0;\n                NetworkResponse networkResponse = null;\n                if (httpResponse != null) {\n                    statusCode = httpResponse.getStatusLine().getStatusCode();\n                } else {\n                    throw new NoConnectionError(e);\n                }\n                VolleyLog.e(\"Unexpected response code %d for %s\", statusCode, request.getUrl());\n                if (responseContents != null) {\n                    networkResponse = new NetworkResponse(statusCode, responseContents,\n                            responseHeaders, false);\n                    if (statusCode == HttpStatus.SC_UNAUTHORIZED ||\n                            statusCode == HttpStatus.SC_FORBIDDEN) {\n                        attemptRetryOnException(\"auth\",\n                                request, new AuthFailureError(networkResponse));\n                    } else {\n                        // TODO: Only throw ServerError for 5xx status codes.\n                        throw new ServerError(networkResponse);\n                    }\n                } else {\n                    throw new NetworkError(networkResponse);\n                }\n            }\n        }\n    }\n\n    /**\n     * Logs requests that took over SLOW_REQUEST_THRESHOLD_MS to complete.\n     */\n    private void logSlowRequests(long requestLifetime, Request<?> request,\n            byte[] responseContents, StatusLine statusLine) {\n        if (DEBUG || requestLifetime > SLOW_REQUEST_THRESHOLD_MS) {\n            VolleyLog.d(\"HTTP response for request=<%s> [lifetime=%d], [size=%s], \" +\n                    \"[rc=%d], [retryCount=%s]\", request, requestLifetime,\n                    responseContents != null ? responseContents.length : \"null\",\n                    statusLine.getStatusCode(), request.getRetryPolicy().getCurrentRetryCount());\n        }\n    }\n\n    /**\n     * Attempts to prepare the request for a retry. If there are no more attempts remaining in the\n     * request's retry policy, a timeout exception is thrown.\n     * @param request The request to use.\n     */\n    private static void attemptRetryOnException(String logPrefix, Request<?> request,\n            VolleyError exception) throws VolleyError {\n        RetryPolicy retryPolicy = request.getRetryPolicy();\n        int oldTimeout = request.getTimeoutMs();\n\n        try {\n            retryPolicy.retry(exception);\n        } catch (VolleyError e) {\n            request.addMarker(\n                    String.format(\"%s-timeout-giveup [timeout=%s]\", logPrefix, oldTimeout));\n            throw e;\n        }\n        request.addMarker(String.format(\"%s-retry [timeout=%s]\", logPrefix, oldTimeout));\n    }\n\n    private void addCacheHeaders(Map<String, String> headers, Cache.Entry entry) {\n        // If there's no cache entry, we're done.\n        if (entry == null) {\n            return;\n        }\n\n        if (entry.etag != null) {\n            headers.put(\"If-None-Match\", entry.etag);\n        }\n\n        if (entry.serverDate > 0) {\n            Date refTime = new Date(entry.serverDate);\n            headers.put(\"If-Modified-Since\", DateUtils.formatDate(refTime));\n        }\n    }\n\n    protected void logError(String what, String url, long start) {\n        long now = SystemClock.elapsedRealtime();\n        VolleyLog.v(\"HTTP ERROR(%s) %d ms to fetch %s\", what, (now - start), url);\n    }\n\n    /** Reads the contents of HttpEntity into a byte[]. */\n    private byte[] entityToBytes(HttpEntity entity) throws IOException, ServerError {\n        PoolingByteArrayOutputStream bytes =\n                new PoolingByteArrayOutputStream(mPool, (int) entity.getContentLength());\n        byte[] buffer = null;\n        try {\n            InputStream in = entity.getContent();\n            if (in == null) {\n                throw new ServerError();\n            }\n            buffer = mPool.getBuf(1024);\n            int count;\n            while ((count = in.read(buffer)) != -1) {\n                bytes.write(buffer, 0, count);\n            }\n            return bytes.toByteArray();\n        } finally {\n            try {\n                // Close the InputStream and release the resources by \"consuming the content\".\n                entity.consumeContent();\n            } catch (IOException e) {\n                // This can happen if there was an exception above that left the entity in\n                // an invalid state.\n                VolleyLog.v(\"Error occured when calling consumingContent\");\n            }\n            mPool.returnBuf(buffer);\n            bytes.close();\n        }\n    }\n\n    /**\n     * Converts Headers[] to Map<String, String>.\n     */\n    private static Map<String, String> convertHeaders(Header[] headers) {\n        Map<String, String> result = new HashMap<String, String>();\n        for (int i = 0; i < headers.length; i++) {\n            result.put(headers[i].getName(), headers[i].getValue());\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/toolbox/ByteArrayPool.java",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley.toolbox;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.LinkedList;\nimport java.util.List;\n\n/**\n * ByteArrayPool is a source and repository of <code>byte[]</code> objects. Its purpose is to\n * supply those buffers to consumers who need to use them for a short period of time and then\n * dispose of them. Simply creating and disposing such buffers in the conventional manner can\n * considerable heap churn and garbage collection delays on Android, which lacks good management of\n * short-lived heap objects. It may be advantageous to trade off some memory in the form of a\n * permanently allocated pool of buffers in order to gain heap performance improvements; that is\n * what this class does.\n * <p>\n * A good candidate user for this class is something like an I/O system that uses large temporary\n * <code>byte[]</code> buffers to copy data around. In these use cases, often the consumer wants\n * the buffer to be a certain minimum size to ensure good performance (e.g. when copying data chunks\n * off of a stream), but doesn't mind if the buffer is larger than the minimum. Taking this into\n * account and also to maximize the odds of being able to reuse a recycled buffer, this class is\n * free to return buffers larger than the requested size. The caller needs to be able to gracefully\n * deal with getting buffers any size over the minimum.\n * <p>\n * If there is not a suitably-sized buffer in its recycling pool when a buffer is requested, this\n * class will allocate a new buffer and return it.\n * <p>\n * This class has no special ownership of buffers it creates; the caller is free to take a buffer\n * it receives from this pool, use it permanently, and never return it to the pool; additionally,\n * it is not harmful to return to this pool a buffer that was allocated elsewhere, provided there\n * are no other lingering references to it.\n * <p>\n * This class ensures that the total size of the buffers in its recycling pool never exceeds a\n * certain byte limit. When a buffer is returned that would cause the pool to exceed the limit,\n * least-recently-used buffers are disposed.\n */\npublic class ByteArrayPool {\n    /** The buffer pool, arranged both by last use and by buffer size */\n    private List<byte[]> mBuffersByLastUse = new LinkedList<byte[]>();\n    private List<byte[]> mBuffersBySize = new ArrayList<byte[]>(64);\n\n    /** The total size of the buffers in the pool */\n    private int mCurrentSize = 0;\n\n    /**\n     * The maximum aggregate size of the buffers in the pool. Old buffers are discarded to stay\n     * under this limit.\n     */\n    private final int mSizeLimit;\n\n    /** Compares buffers by size */\n    protected static final Comparator<byte[]> BUF_COMPARATOR = new Comparator<byte[]>() {\n        @Override\n        public int compare(byte[] lhs, byte[] rhs) {\n            return lhs.length - rhs.length;\n        }\n    };\n\n    /**\n     * @param sizeLimit the maximum size of the pool, in bytes\n     */\n    public ByteArrayPool(int sizeLimit) {\n        mSizeLimit = sizeLimit;\n    }\n\n    /**\n     * Returns a buffer from the pool if one is available in the requested size, or allocates a new\n     * one if a pooled one is not available.\n     *\n     * @param len the minimum size, in bytes, of the requested buffer. The returned buffer may be\n     *        larger.\n     * @return a byte[] buffer is always returned.\n     */\n    public synchronized byte[] getBuf(int len) {\n        for (int i = 0; i < mBuffersBySize.size(); i++) {\n            byte[] buf = mBuffersBySize.get(i);\n            if (buf.length >= len) {\n                mCurrentSize -= buf.length;\n                mBuffersBySize.remove(i);\n                mBuffersByLastUse.remove(buf);\n                return buf;\n            }\n        }\n        return new byte[len];\n    }\n\n    /**\n     * Returns a buffer to the pool, throwing away old buffers if the pool would exceed its allotted\n     * size.\n     *\n     * @param buf the buffer to return to the pool.\n     */\n    public synchronized void returnBuf(byte[] buf) {\n        if (buf == null || buf.length > mSizeLimit) {\n            return;\n        }\n        mBuffersByLastUse.add(buf);\n        int pos = Collections.binarySearch(mBuffersBySize, buf, BUF_COMPARATOR);\n        if (pos < 0) {\n            pos = -pos - 1;\n        }\n        mBuffersBySize.add(pos, buf);\n        mCurrentSize += buf.length;\n        trim();\n    }\n\n    /**\n     * Removes buffers from the pool until it is under its size limit.\n     */\n    private synchronized void trim() {\n        while (mCurrentSize > mSizeLimit) {\n            byte[] buf = mBuffersByLastUse.remove(0);\n            mBuffersBySize.remove(buf);\n            mCurrentSize -= buf.length;\n        }\n    }\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/toolbox/ClearCacheRequest.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley.toolbox;\n\nimport com.android.volley.Cache;\nimport com.android.volley.NetworkResponse;\nimport com.android.volley.Request;\nimport com.android.volley.Response;\n\nimport android.os.Handler;\nimport android.os.Looper;\n\n/**\n * A synthetic request used for clearing the cache.\n */\npublic class ClearCacheRequest extends Request<Object> {\n    private final Cache mCache;\n    private final Runnable mCallback;\n\n    /**\n     * Creates a synthetic request for clearing the cache.\n     * @param cache Cache to clear\n     * @param callback Callback to make on the main thread once the cache is clear,\n     * or null for none\n     */\n    public ClearCacheRequest(Cache cache, Runnable callback) {\n        super(Method.GET, null, null);\n        mCache = cache;\n        mCallback = callback;\n    }\n\n    @Override\n    public boolean isCanceled() {\n        // This is a little bit of a hack, but hey, why not.\n        mCache.clear();\n        if (mCallback != null) {\n            Handler handler = new Handler(Looper.getMainLooper());\n            handler.postAtFrontOfQueue(mCallback);\n        }\n        return true;\n    }\n\n    @Override\n    public Priority getPriority() {\n        return Priority.IMMEDIATE;\n    }\n\n    @Override\n    protected Response<Object> parseNetworkResponse(NetworkResponse response) {\n        return null;\n    }\n\n    @Override\n    protected void deliverResponse(Object response) {\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/toolbox/DiskBasedCache.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley.toolbox;\n\nimport android.os.SystemClock;\n\nimport com.android.volley.Cache;\nimport com.android.volley.VolleyLog;\n\nimport java.io.EOFException;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.FilterInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\n/**\n * Cache implementation that caches files directly onto the hard disk in the specified\n * directory. The default disk usage size is 5MB, but is configurable.\n */\npublic class DiskBasedCache implements Cache {\n\n    /** Map of the Key, CacheHeader pairs */\n    private final Map<String, CacheHeader> mEntries =\n            new LinkedHashMap<String, CacheHeader>(16, .75f, true);\n\n    /** Total amount of space currently used by the cache in bytes. */\n    private long mTotalSize = 0;\n\n    /** The root directory to use for the cache. */\n    private final File mRootDirectory;\n\n    /** The maximum size of the cache in bytes. */\n    private final int mMaxCacheSizeInBytes;\n\n    /** Default maximum disk usage in bytes. */\n    private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024;\n\n    /** High water mark percentage for the cache */\n    private static final float HYSTERESIS_FACTOR = 0.9f;\n\n    /** Magic number for current version of cache file format. */\n    private static final int CACHE_MAGIC = 0x20140623;\n\n    /**\n     * Constructs an instance of the DiskBasedCache at the specified directory.\n     * @param rootDirectory The root directory of the cache.\n     * @param maxCacheSizeInBytes The maximum size of the cache in bytes.\n     */\n    public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) {\n        mRootDirectory = rootDirectory;\n        mMaxCacheSizeInBytes = maxCacheSizeInBytes;\n    }\n\n    /**\n     * Constructs an instance of the DiskBasedCache at the specified directory using\n     * the default maximum cache size of 5MB.\n     * @param rootDirectory The root directory of the cache.\n     */\n    public DiskBasedCache(File rootDirectory) {\n        this(rootDirectory, DEFAULT_DISK_USAGE_BYTES);\n    }\n\n    /**\n     * Clears the cache. Deletes all cached files from disk.\n     */\n    @Override\n    public synchronized void clear() {\n        File[] files = mRootDirectory.listFiles();\n        if (files != null) {\n            for (File file : files) {\n                file.delete();\n            }\n        }\n        mEntries.clear();\n        mTotalSize = 0;\n        VolleyLog.d(\"Cache cleared.\");\n    }\n\n    /**\n     * Returns the cache entry with the specified key if it exists, null otherwise.\n     */\n    @Override\n    public synchronized Entry get(String key) {\n        CacheHeader entry = mEntries.get(key);\n        // if the entry does not exist, return.\n        if (entry == null) {\n            return null;\n        }\n\n        File file = getFileForKey(key);\n        CountingInputStream cis = null;\n        try {\n            cis = new CountingInputStream(new FileInputStream(file));\n            CacheHeader.readHeader(cis); // eat header\n            byte[] data = streamToBytes(cis, (int) (file.length() - cis.bytesRead));\n            return entry.toCacheEntry(data);\n        } catch (IOException e) {\n            VolleyLog.d(\"%s: %s\", file.getAbsolutePath(), e.toString());\n            remove(key);\n            return null;\n        } finally {\n            if (cis != null) {\n                try {\n                    cis.close();\n                } catch (IOException ioe) {\n                    return null;\n                }\n            }\n        }\n    }\n\n    /**\n     * Initializes the DiskBasedCache by scanning for all files currently in the\n     * specified root directory. Creates the root directory if necessary.\n     */\n    @Override\n    public synchronized void initialize() {\n        if (!mRootDirectory.exists()) {\n            if (!mRootDirectory.mkdirs()) {\n                VolleyLog.e(\"Unable to create cache dir %s\", mRootDirectory.getAbsolutePath());\n            }\n            return;\n        }\n\n        File[] files = mRootDirectory.listFiles();\n        if (files == null) {\n            return;\n        }\n        for (File file : files) {\n            FileInputStream fis = null;\n            try {\n                fis = new FileInputStream(file);\n                CacheHeader entry = CacheHeader.readHeader(fis);\n                entry.size = file.length();\n                putEntry(entry.key, entry);\n            } catch (IOException e) {\n                if (file != null) {\n                   file.delete();\n                }\n            } finally {\n                try {\n                    if (fis != null) {\n                        fis.close();\n                    }\n                } catch (IOException ignored) { }\n            }\n        }\n    }\n\n    /**\n     * Invalidates an entry in the cache.\n     * @param key Cache key\n     * @param fullExpire True to fully expire the entry, false to soft expire\n     */\n    @Override\n    public synchronized void invalidate(String key, boolean fullExpire) {\n        Entry entry = get(key);\n        if (entry != null) {\n            entry.softTtl = 0;\n            if (fullExpire) {\n                entry.ttl = 0;\n            }\n            put(key, entry);\n        }\n\n    }\n\n    /**\n     * Puts the entry with the specified key into the cache.\n     */\n    @Override\n    public synchronized void put(String key, Entry entry) {\n        pruneIfNeeded(entry.data.length);\n        File file = getFileForKey(key);\n        try {\n            FileOutputStream fos = new FileOutputStream(file);\n            CacheHeader e = new CacheHeader(key, entry);\n            boolean success = e.writeHeader(fos);\n            if (!success) {\n                fos.close();\n                VolleyLog.d(\"Failed to write header for %s\", file.getAbsolutePath());\n                throw new IOException();\n            }\n            fos.write(entry.data);\n            fos.close();\n            putEntry(key, e);\n            return;\n        } catch (IOException e) {\n        }\n        boolean deleted = file.delete();\n        if (!deleted) {\n            VolleyLog.d(\"Could not clean up file %s\", file.getAbsolutePath());\n        }\n    }\n\n    /**\n     * Removes the specified key from the cache if it exists.\n     */\n    @Override\n    public synchronized void remove(String key) {\n        boolean deleted = getFileForKey(key).delete();\n        removeEntry(key);\n        if (!deleted) {\n            VolleyLog.d(\"Could not delete cache entry for key=%s, filename=%s\",\n                    key, getFilenameForKey(key));\n        }\n    }\n\n    /**\n     * Creates a pseudo-unique filename for the specified cache key.\n     * @param key The key to generate a file name for.\n     * @return A pseudo-unique filename.\n     */\n    private String getFilenameForKey(String key) {\n        int firstHalfLength = key.length() / 2;\n        String localFilename = String.valueOf(key.substring(0, firstHalfLength).hashCode());\n        localFilename += String.valueOf(key.substring(firstHalfLength).hashCode());\n        return localFilename;\n    }\n\n    /**\n     * Returns a file object for the given cache key.\n     */\n    public File getFileForKey(String key) {\n        return new File(mRootDirectory, getFilenameForKey(key));\n    }\n\n    /**\n     * Prunes the cache to fit the amount of bytes specified.\n     * @param neededSpace The amount of bytes we are trying to fit into the cache.\n     */\n    private void pruneIfNeeded(int neededSpace) {\n        if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes) {\n            return;\n        }\n        if (VolleyLog.DEBUG) {\n            VolleyLog.v(\"Pruning old cache entries.\");\n        }\n\n        long before = mTotalSize;\n        int prunedFiles = 0;\n        long startTime = SystemClock.elapsedRealtime();\n\n        Iterator<Map.Entry<String, CacheHeader>> iterator = mEntries.entrySet().iterator();\n        while (iterator.hasNext()) {\n            Map.Entry<String, CacheHeader> entry = iterator.next();\n            CacheHeader e = entry.getValue();\n            boolean deleted = getFileForKey(e.key).delete();\n            if (deleted) {\n                mTotalSize -= e.size;\n            } else {\n               VolleyLog.d(\"Could not delete cache entry for key=%s, filename=%s\",\n                       e.key, getFilenameForKey(e.key));\n            }\n            iterator.remove();\n            prunedFiles++;\n\n            if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes * HYSTERESIS_FACTOR) {\n                break;\n            }\n        }\n\n        if (VolleyLog.DEBUG) {\n            VolleyLog.v(\"pruned %d files, %d bytes, %d ms\",\n                    prunedFiles, (mTotalSize - before), SystemClock.elapsedRealtime() - startTime);\n        }\n    }\n\n    /**\n     * Puts the entry with the specified key into the cache.\n     * @param key The key to identify the entry by.\n     * @param entry The entry to cache.\n     */\n    private void putEntry(String key, CacheHeader entry) {\n        if (!mEntries.containsKey(key)) {\n            mTotalSize += entry.size;\n        } else {\n            CacheHeader oldEntry = mEntries.get(key);\n            mTotalSize += (entry.size - oldEntry.size);\n        }\n        mEntries.put(key, entry);\n    }\n\n    /**\n     * Removes the entry identified by 'key' from the cache.\n     */\n    private void removeEntry(String key) {\n        CacheHeader entry = mEntries.get(key);\n        if (entry != null) {\n            mTotalSize -= entry.size;\n            mEntries.remove(key);\n        }\n    }\n\n    /**\n     * Reads the contents of an InputStream into a byte[].\n     * */\n    private static byte[] streamToBytes(InputStream in, int length) throws IOException {\n        byte[] bytes = new byte[length];\n        int count;\n        int pos = 0;\n        while (pos < length && ((count = in.read(bytes, pos, length - pos)) != -1)) {\n            pos += count;\n        }\n        if (pos != length) {\n            throw new IOException(\"Expected \" + length + \" bytes, read \" + pos + \" bytes\");\n        }\n        return bytes;\n    }\n\n    /**\n     * Handles holding onto the cache headers for an entry.\n     */\n    // Visible for testing.\n    static class CacheHeader {\n        /** The size of the data identified by this CacheHeader. (This is not\n         * serialized to disk. */\n        public long size;\n\n        /** The key that identifies the cache entry. */\n        public String key;\n\n        /** ETag for cache coherence. */\n        public String etag;\n\n        /** Date of this response as reported by the server. */\n        public long serverDate;\n\n        /** TTL for this record. */\n        public long ttl;\n\n        /** Soft TTL for this record. */\n        public long softTtl;\n\n        /** Headers from the response resulting in this cache entry. */\n        public Map<String, String> responseHeaders;\n\n        private CacheHeader() { }\n\n        /**\n         * Instantiates a new CacheHeader object\n         * @param key The key that identifies the cache entry\n         * @param entry The cache entry.\n         */\n        public CacheHeader(String key, Entry entry) {\n            this.key = key;\n            this.size = entry.data.length;\n            this.etag = entry.etag;\n            this.serverDate = entry.serverDate;\n            this.ttl = entry.ttl;\n            this.softTtl = entry.softTtl;\n            this.responseHeaders = entry.responseHeaders;\n        }\n\n        /**\n         * Reads the header off of an InputStream and returns a CacheHeader object.\n         * @param is The InputStream to read from.\n         * @throws IOException\n         */\n        public static CacheHeader readHeader(InputStream is) throws IOException {\n            CacheHeader entry = new CacheHeader();\n            int magic = readInt(is);\n            if (magic != CACHE_MAGIC) {\n                // don't bother deleting, it'll get pruned eventually\n                throw new IOException();\n            }\n            entry.key = readString(is);\n            entry.etag = readString(is);\n            if (entry.etag.equals(\"\")) {\n                entry.etag = null;\n            }\n            entry.serverDate = readLong(is);\n            entry.ttl = readLong(is);\n            entry.softTtl = readLong(is);\n            entry.responseHeaders = readStringStringMap(is);\n            return entry;\n        }\n\n        /**\n         * Creates a cache entry for the specified data.\n         */\n        public Entry toCacheEntry(byte[] data) {\n            Entry e = new Entry();\n            e.data = data;\n            e.etag = etag;\n            e.serverDate = serverDate;\n            e.ttl = ttl;\n            e.softTtl = softTtl;\n            e.responseHeaders = responseHeaders;\n            return e;\n        }\n\n\n        /**\n         * Writes the contents of this CacheHeader to the specified OutputStream.\n         */\n        public boolean writeHeader(OutputStream os) {\n            try {\n                writeInt(os, CACHE_MAGIC);\n                writeString(os, key);\n                writeString(os, etag == null ? \"\" : etag);\n                writeLong(os, serverDate);\n                writeLong(os, ttl);\n                writeLong(os, softTtl);\n                writeStringStringMap(responseHeaders, os);\n                os.flush();\n                return true;\n            } catch (IOException e) {\n                VolleyLog.d(\"%s\", e.toString());\n                return false;\n            }\n        }\n\n    }\n\n    private static class CountingInputStream extends FilterInputStream {\n        private int bytesRead = 0;\n\n        private CountingInputStream(InputStream in) {\n            super(in);\n        }\n\n        @Override\n        public int read() throws IOException {\n            int result = super.read();\n            if (result != -1) {\n                bytesRead++;\n            }\n            return result;\n        }\n\n        @Override\n        public int read(byte[] buffer, int offset, int count) throws IOException {\n            int result = super.read(buffer, offset, count);\n            if (result != -1) {\n                bytesRead += result;\n            }\n            return result;\n        }\n    }\n\n    /*\n     * Homebrewed simple serialization system used for reading and writing cache\n     * headers on disk. Once upon a time, this used the standard Java\n     * Object{Input,Output}Stream, but the default implementation relies heavily\n     * on reflection (even for standard types) and generates a ton of garbage.\n     */\n\n    /**\n     * Simple wrapper around {@link InputStream#read()} that throws EOFException\n     * instead of returning -1.\n     */\n    private static int read(InputStream is) throws IOException {\n        int b = is.read();\n        if (b == -1) {\n            throw new EOFException();\n        }\n        return b;\n    }\n\n    static void writeInt(OutputStream os, int n) throws IOException {\n        os.write((n >> 0) & 0xff);\n        os.write((n >> 8) & 0xff);\n        os.write((n >> 16) & 0xff);\n        os.write((n >> 24) & 0xff);\n    }\n\n    static int readInt(InputStream is) throws IOException {\n        int n = 0;\n        n |= (read(is) << 0);\n        n |= (read(is) << 8);\n        n |= (read(is) << 16);\n        n |= (read(is) << 24);\n        return n;\n    }\n\n    static void writeLong(OutputStream os, long n) throws IOException {\n        os.write((byte)(n >>> 0));\n        os.write((byte)(n >>> 8));\n        os.write((byte)(n >>> 16));\n        os.write((byte)(n >>> 24));\n        os.write((byte)(n >>> 32));\n        os.write((byte)(n >>> 40));\n        os.write((byte)(n >>> 48));\n        os.write((byte)(n >>> 56));\n    }\n\n    static long readLong(InputStream is) throws IOException {\n        long n = 0;\n        n |= ((read(is) & 0xFFL) << 0);\n        n |= ((read(is) & 0xFFL) << 8);\n        n |= ((read(is) & 0xFFL) << 16);\n        n |= ((read(is) & 0xFFL) << 24);\n        n |= ((read(is) & 0xFFL) << 32);\n        n |= ((read(is) & 0xFFL) << 40);\n        n |= ((read(is) & 0xFFL) << 48);\n        n |= ((read(is) & 0xFFL) << 56);\n        return n;\n    }\n\n    static void writeString(OutputStream os, String s) throws IOException {\n        byte[] b = s.getBytes(\"UTF-8\");\n        writeLong(os, b.length);\n        os.write(b, 0, b.length);\n    }\n\n    static String readString(InputStream is) throws IOException {\n        int n = (int) readLong(is);\n        byte[] b = streamToBytes(is, n);\n        return new String(b, \"UTF-8\");\n    }\n\n    static void writeStringStringMap(Map<String, String> map, OutputStream os) throws IOException {\n        if (map != null) {\n            writeInt(os, map.size());\n            for (Map.Entry<String, String> entry : map.entrySet()) {\n                writeString(os, entry.getKey());\n                writeString(os, entry.getValue());\n            }\n        } else {\n            writeInt(os, 0);\n        }\n    }\n\n    static Map<String, String> readStringStringMap(InputStream is) throws IOException {\n        int size = readInt(is);\n        Map<String, String> result = (size == 0)\n                ? Collections.<String, String>emptyMap()\n                : new HashMap<String, String>(size);\n        for (int i = 0; i < size; i++) {\n            String key = readString(is).intern();\n            String value = readString(is).intern();\n            result.put(key, value);\n        }\n        return result;\n    }\n\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/toolbox/HttpClientStack.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley.toolbox;\n\nimport com.android.volley.AuthFailureError;\nimport com.android.volley.Request;\nimport com.android.volley.Request.Method;\n\nimport org.apache.http.HttpEntity;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.NameValuePair;\nimport org.apache.http.client.HttpClient;\nimport org.apache.http.client.methods.HttpDelete;\nimport org.apache.http.client.methods.HttpEntityEnclosingRequestBase;\nimport org.apache.http.client.methods.HttpGet;\nimport org.apache.http.client.methods.HttpHead;\nimport org.apache.http.client.methods.HttpOptions;\nimport org.apache.http.client.methods.HttpPost;\nimport org.apache.http.client.methods.HttpPut;\nimport org.apache.http.client.methods.HttpTrace;\nimport org.apache.http.client.methods.HttpUriRequest;\nimport org.apache.http.entity.ByteArrayEntity;\nimport org.apache.http.message.BasicNameValuePair;\nimport org.apache.http.params.HttpConnectionParams;\nimport org.apache.http.params.HttpParams;\n\nimport java.io.IOException;\nimport java.net.URI;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * An HttpStack that performs request over an {@link HttpClient}.\n */\npublic class HttpClientStack implements HttpStack {\n    protected final HttpClient mClient;\n\n    private final static String HEADER_CONTENT_TYPE = \"Content-Type\";\n\n    public HttpClientStack(HttpClient client) {\n        mClient = client;\n    }\n\n    private static void addHeaders(HttpUriRequest httpRequest, Map<String, String> headers) {\n        for (String key : headers.keySet()) {\n            httpRequest.setHeader(key, headers.get(key));\n        }\n    }\n\n    @SuppressWarnings(\"unused\")\n    private static List<NameValuePair> getPostParameterPairs(Map<String, String> postParams) {\n        List<NameValuePair> result = new ArrayList<NameValuePair>(postParams.size());\n        for (String key : postParams.keySet()) {\n            result.add(new BasicNameValuePair(key, postParams.get(key)));\n        }\n        return result;\n    }\n\n    @Override\n    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)\n            throws IOException, AuthFailureError {\n        HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);\n        addHeaders(httpRequest, additionalHeaders);\n        addHeaders(httpRequest, request.getHeaders());\n        onPrepareRequest(httpRequest);\n        HttpParams httpParams = httpRequest.getParams();\n        int timeoutMs = request.getTimeoutMs();\n        // TODO: Reevaluate this connection timeout based on more wide-scale\n        // data collection and possibly different for wifi vs. 3G.\n        HttpConnectionParams.setConnectionTimeout(httpParams, 5000);\n        HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);\n        return mClient.execute(httpRequest);\n    }\n\n    /**\n     * Creates the appropriate subclass of HttpUriRequest for passed in request.\n     */\n    @SuppressWarnings(\"deprecation\")\n    /* protected */ static HttpUriRequest createHttpRequest(Request<?> request,\n            Map<String, String> additionalHeaders) throws AuthFailureError {\n        switch (request.getMethod()) {\n            case Method.DEPRECATED_GET_OR_POST: {\n                // This is the deprecated way that needs to be handled for backwards compatibility.\n                // If the request's post body is null, then the assumption is that the request is\n                // GET.  Otherwise, it is assumed that the request is a POST.\n                byte[] postBody = request.getPostBody();\n                if (postBody != null) {\n                    HttpPost postRequest = new HttpPost(request.getUrl());\n                    postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType());\n                    HttpEntity entity;\n                    entity = new ByteArrayEntity(postBody);\n                    postRequest.setEntity(entity);\n                    return postRequest;\n                } else {\n                    return new HttpGet(request.getUrl());\n                }\n            }\n            case Method.GET:\n                return new HttpGet(request.getUrl());\n            case Method.DELETE:\n                return new HttpDelete(request.getUrl());\n            case Method.POST: {\n                HttpPost postRequest = new HttpPost(request.getUrl());\n                postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());\n                setEntityIfNonEmptyBody(postRequest, request);\n                return postRequest;\n            }\n            case Method.PUT: {\n                HttpPut putRequest = new HttpPut(request.getUrl());\n                putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());\n                setEntityIfNonEmptyBody(putRequest, request);\n                return putRequest;\n            }\n            case Method.HEAD:\n                return new HttpHead(request.getUrl());\n            case Method.OPTIONS:\n                return new HttpOptions(request.getUrl());\n            case Method.TRACE:\n                return new HttpTrace(request.getUrl());\n            case Method.PATCH: {\n                HttpPatch patchRequest = new HttpPatch(request.getUrl());\n                patchRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());\n                setEntityIfNonEmptyBody(patchRequest, request);\n                return patchRequest;\n            }\n            default:\n                throw new IllegalStateException(\"Unknown request method.\");\n        }\n    }\n\n    private static void setEntityIfNonEmptyBody(HttpEntityEnclosingRequestBase httpRequest,\n            Request<?> request) throws AuthFailureError {\n        byte[] body = request.getBody();\n        if (body != null) {\n            HttpEntity entity = new ByteArrayEntity(body);\n            httpRequest.setEntity(entity);\n        }\n    }\n\n    /**\n     * Called before the request is executed using the underlying HttpClient.\n     *\n     * <p>Overwrite in subclasses to augment the request.</p>\n     */\n    protected void onPrepareRequest(HttpUriRequest request) throws IOException {\n        // Nothing.\n    }\n\n    /**\n     * The HttpPatch class does not exist in the Android framework, so this has been defined here.\n     */\n    public static final class HttpPatch extends HttpEntityEnclosingRequestBase {\n\n        public final static String METHOD_NAME = \"PATCH\";\n\n        public HttpPatch() {\n            super();\n        }\n\n        public HttpPatch(final URI uri) {\n            super();\n            setURI(uri);\n        }\n\n        /**\n         * @throws IllegalArgumentException if the uri is invalid.\n         */\n        public HttpPatch(final String uri) {\n            super();\n            setURI(URI.create(uri));\n        }\n\n        @Override\n        public String getMethod() {\n            return METHOD_NAME;\n        }\n\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/toolbox/HttpHeaderParser.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley.toolbox;\n\nimport com.android.volley.Cache;\nimport com.android.volley.NetworkResponse;\n\nimport org.apache.http.impl.cookie.DateParseException;\nimport org.apache.http.impl.cookie.DateUtils;\nimport org.apache.http.protocol.HTTP;\n\nimport java.util.Map;\n\n/**\n * Utility methods for parsing HTTP headers.\n */\npublic class HttpHeaderParser {\n\n    /**\n     * Extracts a {@link Cache.Entry} from a {@link NetworkResponse}.\n     *\n     * @param response The network response to parse headers from\n     * @return a cache entry for the given response, or null if the response is not cacheable.\n     */\n    public static Cache.Entry parseCacheHeaders(NetworkResponse response) {\n        long now = System.currentTimeMillis();\n\n        Map<String, String> headers = response.headers;\n\n        long serverDate = 0;\n        long serverExpires = 0;\n        long softExpire = 0;\n        long maxAge = 0;\n        boolean hasCacheControl = false;\n\n        String serverEtag = null;\n        String headerValue;\n\n        headerValue = headers.get(\"Date\");\n        if (headerValue != null) {\n            serverDate = parseDateAsEpoch(headerValue);\n        }\n\n        headerValue = headers.get(\"Cache-Control\");\n        if (headerValue != null) {\n            hasCacheControl = true;\n            String[] tokens = headerValue.split(\",\");\n            for (int i = 0; i < tokens.length; i++) {\n                String token = tokens[i].trim();\n                if (token.equals(\"no-cache\") || token.equals(\"no-store\")) {\n                    return null;\n                } else if (token.startsWith(\"max-age=\")) {\n                    try {\n                        maxAge = Long.parseLong(token.substring(8));\n                    } catch (Exception e) {\n                    }\n                } else if (token.equals(\"must-revalidate\") || token.equals(\"proxy-revalidate\")) {\n                    maxAge = 0;\n                }\n            }\n        }\n\n        headerValue = headers.get(\"Expires\");\n        if (headerValue != null) {\n            serverExpires = parseDateAsEpoch(headerValue);\n        }\n\n        serverEtag = headers.get(\"ETag\");\n\n        // Cache-Control takes precedence over an Expires header, even if both exist and Expires\n        // is more restrictive.\n        if (hasCacheControl) {\n            softExpire = now + maxAge * 1000;\n        } else if (serverDate > 0 && serverExpires >= serverDate) {\n            // Default semantic for Expire header in HTTP specification is softExpire.\n            softExpire = now + (serverExpires - serverDate);\n        }\n\n        Cache.Entry entry = new Cache.Entry();\n        entry.data = response.data;\n        entry.etag = serverEtag;\n        entry.softTtl = softExpire;\n        entry.ttl = entry.softTtl;\n        entry.serverDate = serverDate;\n        entry.responseHeaders = headers;\n\n        return entry;\n    }\n\n    /**\n     * Parse date in RFC1123 format, and return its value as epoch\n     */\n    public static long parseDateAsEpoch(String dateStr) {\n        try {\n            // Parse date in RFC1123 format if this header contains one\n            return DateUtils.parseDate(dateStr).getTime();\n        } catch (DateParseException e) {\n            // Date in invalid format, fallback to 0\n            return 0;\n        }\n    }\n\n    /**\n     * Returns the charset specified in the Content-Type of this header,\n     * or the HTTP default (ISO-8859-1) if none can be found.\n     */\n    public static String parseCharset(Map<String, String> headers) {\n        String contentType = headers.get(HTTP.CONTENT_TYPE);\n        if (contentType != null) {\n            String[] params = contentType.split(\";\");\n            for (int i = 1; i < params.length; i++) {\n                String[] pair = params[i].trim().split(\"=\");\n                if (pair.length == 2) {\n                    if (pair[0].equals(\"charset\")) {\n                        return pair[1];\n                    }\n                }\n            }\n        }\n\n        return HTTP.DEFAULT_CONTENT_CHARSET;\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/toolbox/HttpStack.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley.toolbox;\n\nimport com.android.volley.AuthFailureError;\nimport com.android.volley.Request;\n\nimport org.apache.http.HttpResponse;\n\nimport java.io.IOException;\nimport java.util.Map;\n\n/**\n * An HTTP stack abstraction.\n */\npublic interface HttpStack {\n    /**\n     * Performs an HTTP request with the given parameters.\n     *\n     * <p>A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise,\n     * and the Content-Type header is set to request.getPostBodyContentType().</p>\n     *\n     * @param request the request to perform\n     * @param additionalHeaders additional headers to be sent together with\n     *         {@link Request#getHeaders()}\n     * @return the HTTP response\n     */\n    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)\n        throws IOException, AuthFailureError;\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/toolbox/HurlStack.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley.toolbox;\n\nimport com.android.volley.AuthFailureError;\nimport com.android.volley.Request;\nimport com.android.volley.Request.Method;\n\nimport org.apache.http.Header;\nimport org.apache.http.HttpEntity;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.ProtocolVersion;\nimport org.apache.http.StatusLine;\nimport org.apache.http.entity.BasicHttpEntity;\nimport org.apache.http.message.BasicHeader;\nimport org.apache.http.message.BasicHttpResponse;\nimport org.apache.http.message.BasicStatusLine;\n\nimport java.io.DataOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport javax.net.ssl.HttpsURLConnection;\nimport javax.net.ssl.SSLSocketFactory;\n\n/**\n * An {@link HttpStack} based on {@link HttpURLConnection}.\n */\npublic class HurlStack implements HttpStack {\n\n    private static final String HEADER_CONTENT_TYPE = \"Content-Type\";\n\n    /**\n     * An interface for transforming URLs before use.\n     */\n    public interface UrlRewriter {\n        /**\n         * Returns a URL to use instead of the provided one, or null to indicate\n         * this URL should not be used at all.\n         */\n        public String rewriteUrl(String originalUrl);\n    }\n\n    private final UrlRewriter mUrlRewriter;\n    private final SSLSocketFactory mSslSocketFactory;\n\n    public HurlStack() {\n        this(null);\n    }\n\n    /**\n     * @param urlRewriter Rewriter to use for request URLs\n     */\n    public HurlStack(UrlRewriter urlRewriter) {\n        this(urlRewriter, null);\n    }\n\n    /**\n     * @param urlRewriter Rewriter to use for request URLs\n     * @param sslSocketFactory SSL factory to use for HTTPS connections\n     */\n    public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {\n        mUrlRewriter = urlRewriter;\n        mSslSocketFactory = sslSocketFactory;\n    }\n\n    @Override\n    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)\n            throws IOException, AuthFailureError {\n        String url = request.getUrl();\n        HashMap<String, String> map = new HashMap<String, String>();\n        map.putAll(request.getHeaders());\n        map.putAll(additionalHeaders);\n        if (mUrlRewriter != null) {\n            String rewritten = mUrlRewriter.rewriteUrl(url);\n            if (rewritten == null) {\n                throw new IOException(\"URL blocked by rewriter: \" + url);\n            }\n            url = rewritten;\n        }\n        URL parsedUrl = new URL(url);\n        HttpURLConnection connection = openConnection(parsedUrl, request);\n        for (String headerName : map.keySet()) {\n            connection.addRequestProperty(headerName, map.get(headerName));\n        }\n        setConnectionParametersForRequest(connection, request);\n        // Initialize HttpResponse with data from the HttpURLConnection.\n        ProtocolVersion protocolVersion = new ProtocolVersion(\"HTTP\", 1, 1);\n        int responseCode = connection.getResponseCode();\n        if (responseCode == -1) {\n            // -1 is returned by getResponseCode() if the response code could not be retrieved.\n            // Signal to the caller that something was wrong with the connection.\n            throw new IOException(\"Could not retrieve response code from HttpUrlConnection.\");\n        }\n        StatusLine responseStatus = new BasicStatusLine(protocolVersion,\n                connection.getResponseCode(), connection.getResponseMessage());\n        BasicHttpResponse response = new BasicHttpResponse(responseStatus);\n        response.setEntity(entityFromConnection(connection));\n        for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {\n            if (header.getKey() != null) {\n                Header h = new BasicHeader(header.getKey(), header.getValue().get(0));\n                response.addHeader(h);\n            }\n        }\n        return response;\n    }\n\n    /**\n     * Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}.\n     * @param connection\n     * @return an HttpEntity populated with data from <code>connection</code>.\n     */\n    private static HttpEntity entityFromConnection(HttpURLConnection connection) {\n        BasicHttpEntity entity = new BasicHttpEntity();\n        InputStream inputStream;\n        try {\n            inputStream = connection.getInputStream();\n        } catch (IOException ioe) {\n            inputStream = connection.getErrorStream();\n        }\n        entity.setContent(inputStream);\n        entity.setContentLength(connection.getContentLength());\n        entity.setContentEncoding(connection.getContentEncoding());\n        entity.setContentType(connection.getContentType());\n        return entity;\n    }\n\n    /**\n     * Create an {@link HttpURLConnection} for the specified {@code url}.\n     */\n    protected HttpURLConnection createConnection(URL url) throws IOException {\n        return (HttpURLConnection) url.openConnection();\n    }\n\n    /**\n     * Opens an {@link HttpURLConnection} with parameters.\n     * @param url\n     * @return an open connection\n     * @throws IOException\n     */\n    private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException {\n        HttpURLConnection connection = createConnection(url);\n\n        int timeoutMs = request.getTimeoutMs();\n        connection.setConnectTimeout(timeoutMs);\n        connection.setReadTimeout(timeoutMs);\n        connection.setUseCaches(false);\n        connection.setDoInput(true);\n\n        // use caller-provided custom SslSocketFactory, if any, for HTTPS\n        if (\"https\".equals(url.getProtocol()) && mSslSocketFactory != null) {\n            ((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory);\n        }\n\n        return connection;\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    /* package */ static void setConnectionParametersForRequest(HttpURLConnection connection,\n            Request<?> request) throws IOException, AuthFailureError {\n        switch (request.getMethod()) {\n            case Method.DEPRECATED_GET_OR_POST:\n                // This is the deprecated way that needs to be handled for backwards compatibility.\n                // If the request's post body is null, then the assumption is that the request is\n                // GET.  Otherwise, it is assumed that the request is a POST.\n                byte[] postBody = request.getPostBody();\n                if (postBody != null) {\n                    // Prepare output. There is no need to set Content-Length explicitly,\n                    // since this is handled by HttpURLConnection using the size of the prepared\n                    // output stream.\n                    connection.setDoOutput(true);\n                    connection.setRequestMethod(\"POST\");\n                    connection.addRequestProperty(HEADER_CONTENT_TYPE,\n                            request.getPostBodyContentType());\n                    DataOutputStream out = new DataOutputStream(connection.getOutputStream());\n                    out.write(postBody);\n                    out.close();\n                }\n                break;\n            case Method.GET:\n                // Not necessary to set the request method because connection defaults to GET but\n                // being explicit here.\n                connection.setRequestMethod(\"GET\");\n                break;\n            case Method.DELETE:\n                connection.setRequestMethod(\"DELETE\");\n                break;\n            case Method.POST:\n                connection.setRequestMethod(\"POST\");\n                addBodyIfExists(connection, request);\n                break;\n            case Method.PUT:\n                connection.setRequestMethod(\"PUT\");\n                addBodyIfExists(connection, request);\n                break;\n            case Method.HEAD:\n                connection.setRequestMethod(\"HEAD\");\n                break;\n            case Method.OPTIONS:\n                connection.setRequestMethod(\"OPTIONS\");\n                break;\n            case Method.TRACE:\n                connection.setRequestMethod(\"TRACE\");\n                break;\n            case Method.PATCH:\n                connection.setRequestMethod(\"PATCH\");\n                addBodyIfExists(connection, request);\n                break;\n            default:\n                throw new IllegalStateException(\"Unknown method type.\");\n        }\n    }\n\n    private static void addBodyIfExists(HttpURLConnection connection, Request<?> request)\n            throws IOException, AuthFailureError {\n        byte[] body = request.getBody();\n        if (body != null) {\n            connection.setDoOutput(true);\n            connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());\n            DataOutputStream out = new DataOutputStream(connection.getOutputStream());\n            out.write(body);\n            out.close();\n        }\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/toolbox/ImageLoader.java",
    "content": "/**\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.volley.toolbox;\n\nimport android.graphics.Bitmap;\nimport android.graphics.Bitmap.Config;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.widget.ImageView;\n\nimport com.android.volley.Request;\nimport com.android.volley.RequestQueue;\nimport com.android.volley.Response.ErrorListener;\nimport com.android.volley.Response.Listener;\nimport com.android.volley.VolleyError;\nimport com.android.volley.toolbox.ImageRequest;\n\nimport java.util.HashMap;\nimport java.util.LinkedList;\n\n/**\n * Helper that handles loading and caching images from remote URLs.\n *\n * The simple way to use this class is to call {@link ImageLoader#get(String, ImageListener)}\n * and to pass in the default image listener provided by\n * {@link ImageLoader#getImageListener(ImageView, int, int)}. Note that all function calls to\n * this class must be made from the main thead, and all responses will be delivered to the main\n * thread as well.\n */\npublic class ImageLoader {\n    /** RequestQueue for dispatching ImageRequests onto. */\n    private final RequestQueue mRequestQueue;\n\n    /** Amount of time to wait after first response arrives before delivering all responses. */\n    private int mBatchResponseDelayMs = 100;\n\n    /** The cache implementation to be used as an L1 cache before calling into volley. */\n    private final ImageCache mCache;\n\n    /**\n     * HashMap of Cache keys -> BatchedImageRequest used to track in-flight requests so\n     * that we can coalesce multiple requests to the same URL into a single network request.\n     */\n    private final HashMap<String, BatchedImageRequest> mInFlightRequests =\n            new HashMap<String, BatchedImageRequest>();\n\n    /** HashMap of the currently pending responses (waiting to be delivered). */\n    private final HashMap<String, BatchedImageRequest> mBatchedResponses =\n            new HashMap<String, BatchedImageRequest>();\n\n    /** Handler to the main thread. */\n    private final Handler mHandler = new Handler(Looper.getMainLooper());\n\n    /** Runnable for in-flight response delivery. */\n    private Runnable mRunnable;\n\n    /**\n     * Simple cache adapter interface. If provided to the ImageLoader, it\n     * will be used as an L1 cache before dispatch to Volley. Implementations\n     * must not block. Implementation with an LruCache is recommended.\n     */\n    public interface ImageCache {\n        public Bitmap getBitmap(String url);\n        public void putBitmap(String url, Bitmap bitmap);\n    }\n\n    /**\n     * Constructs a new ImageLoader.\n     * @param queue The RequestQueue to use for making image requests.\n     * @param imageCache The cache to use as an L1 cache.\n     */\n    public ImageLoader(RequestQueue queue, ImageCache imageCache) {\n        mRequestQueue = queue;\n        mCache = imageCache;\n    }\n\n    /**\n     * The default implementation of ImageListener which handles basic functionality\n     * of showing a default image until the network response is received, at which point\n     * it will switch to either the actual image or the error image.\n     * @param imageView The imageView that the listener is associated with.\n     * @param defaultImageResId Default image resource ID to use, or 0 if it doesn't exist.\n     * @param errorImageResId Error image resource ID to use, or 0 if it doesn't exist.\n     */\n    public static ImageListener getImageListener(final ImageView view,\n            final int defaultImageResId, final int errorImageResId) {\n        return new ImageListener() {\n            @Override\n            public void onErrorResponse(VolleyError error) {\n                if (errorImageResId != 0) {\n                    view.setImageResource(errorImageResId);\n                }\n            }\n\n            @Override\n            public void onResponse(ImageContainer response, boolean isImmediate) {\n                if (response.getBitmap() != null) {\n                    view.setImageBitmap(response.getBitmap());\n                } else if (defaultImageResId != 0) {\n                    view.setImageResource(defaultImageResId);\n                }\n            }\n        };\n    }\n\n    /**\n     * Interface for the response handlers on image requests.\n     *\n     * The call flow is this:\n     * 1. Upon being  attached to a request, onResponse(response, true) will\n     * be invoked to reflect any cached data that was already available. If the\n     * data was available, response.getBitmap() will be non-null.\n     *\n     * 2. After a network response returns, only one of the following cases will happen:\n     *   - onResponse(response, false) will be called if the image was loaded.\n     *   or\n     *   - onErrorResponse will be called if there was an error loading the image.\n     */\n    public interface ImageListener extends ErrorListener {\n        /**\n         * Listens for non-error changes to the loading of the image request.\n         *\n         * @param response Holds all information pertaining to the request, as well\n         * as the bitmap (if it is loaded).\n         * @param isImmediate True if this was called during ImageLoader.get() variants.\n         * This can be used to differentiate between a cached image loading and a network\n         * image loading in order to, for example, run an animation to fade in network loaded\n         * images.\n         */\n        public void onResponse(ImageContainer response, boolean isImmediate);\n    }\n\n    /**\n     * Checks if the item is available in the cache.\n     * @param requestUrl The url of the remote image\n     * @param maxWidth The maximum width of the returned image.\n     * @param maxHeight The maximum height of the returned image.\n     * @return True if the item exists in cache, false otherwise.\n     */\n    public boolean isCached(String requestUrl, int maxWidth, int maxHeight) {\n        throwIfNotOnMainThread();\n\n        String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);\n        return mCache.getBitmap(cacheKey) != null;\n    }\n\n    /**\n     * Returns an ImageContainer for the requested URL.\n     *\n     * The ImageContainer will contain either the specified default bitmap or the loaded bitmap.\n     * If the default was returned, the {@link ImageLoader} will be invoked when the\n     * request is fulfilled.\n     *\n     * @param requestUrl The URL of the image to be loaded.\n     * @param defaultImage Optional default image to return until the actual image is loaded.\n     */\n    public ImageContainer get(String requestUrl, final ImageListener listener) {\n        return get(requestUrl, listener, 0, 0);\n    }\n\n    /**\n     * Issues a bitmap request with the given URL if that image is not available\n     * in the cache, and returns a bitmap container that contains all of the data\n     * relating to the request (as well as the default image if the requested\n     * image is not available).\n     * @param requestUrl The url of the remote image\n     * @param imageListener The listener to call when the remote image is loaded\n     * @param maxWidth The maximum width of the returned image.\n     * @param maxHeight The maximum height of the returned image.\n     * @return A container object that contains all of the properties of the request, as well as\n     *     the currently available image (default if remote is not loaded).\n     */\n    public ImageContainer get(String requestUrl, ImageListener imageListener,\n            int maxWidth, int maxHeight) {\n        // only fulfill requests that were initiated from the main thread.\n        throwIfNotOnMainThread();\n\n        final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);\n\n        // Try to look up the request in the cache of remote images.\n        Bitmap cachedBitmap = mCache.getBitmap(cacheKey);\n        if (cachedBitmap != null) {\n            // Return the cached bitmap.\n            ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null);\n            imageListener.onResponse(container, true);\n            return container;\n        }\n\n        // The bitmap did not exist in the cache, fetch it!\n        ImageContainer imageContainer =\n                new ImageContainer(null, requestUrl, cacheKey, imageListener);\n\n        // Update the caller to let them know that they should use the default bitmap.\n        imageListener.onResponse(imageContainer, true);\n\n        // Check to see if a request is already in-flight.\n        BatchedImageRequest request = mInFlightRequests.get(cacheKey);\n        if (request != null) {\n            // If it is, add this request to the list of listeners.\n            request.addContainer(imageContainer);\n            return imageContainer;\n        }\n\n        // The request is not already in flight. Send the new request to the network and\n        // track it.\n        Request<?> newRequest =\n            new ImageRequest(requestUrl, new Listener<Bitmap>() {\n                @Override\n                public void onResponse(Bitmap response) {\n                    onGetImageSuccess(cacheKey, response);\n                }\n            }, maxWidth, maxHeight,\n            Config.RGB_565, new ErrorListener() {\n                @Override\n                public void onErrorResponse(VolleyError error) {\n                    onGetImageError(cacheKey, error);\n                }\n            });\n\n        mRequestQueue.add(newRequest);\n        mInFlightRequests.put(cacheKey,\n                new BatchedImageRequest(newRequest, imageContainer));\n        return imageContainer;\n    }\n\n    /**\n     * Sets the amount of time to wait after the first response arrives before delivering all\n     * responses. Batching can be disabled entirely by passing in 0.\n     * @param newBatchedResponseDelayMs The time in milliseconds to wait.\n     */\n    public void setBatchedResponseDelay(int newBatchedResponseDelayMs) {\n        mBatchResponseDelayMs = newBatchedResponseDelayMs;\n    }\n\n    /**\n     * Handler for when an image was successfully loaded.\n     * @param cacheKey The cache key that is associated with the image request.\n     * @param response The bitmap that was returned from the network.\n     */\n    private void onGetImageSuccess(String cacheKey, Bitmap response) {\n        // cache the image that was fetched.\n        mCache.putBitmap(cacheKey, response);\n\n        // remove the request from the list of in-flight requests.\n        BatchedImageRequest request = mInFlightRequests.remove(cacheKey);\n\n        if (request != null) {\n            // Update the response bitmap.\n            request.mResponseBitmap = response;\n\n            // Send the batched response\n            batchResponse(cacheKey, request);\n        }\n    }\n\n    /**\n     * Handler for when an image failed to load.\n     * @param cacheKey The cache key that is associated with the image request.\n     */\n    private void onGetImageError(String cacheKey, VolleyError error) {\n        // Notify the requesters that something failed via a null result.\n        // Remove this request from the list of in-flight requests.\n        BatchedImageRequest request = mInFlightRequests.remove(cacheKey);\n\n        if (request != null) {\n            // Set the error for this request\n            request.setError(error);\n\n            // Send the batched response\n            batchResponse(cacheKey, request);\n        }\n    }\n\n    /**\n     * Container object for all of the data surrounding an image request.\n     */\n    public class ImageContainer {\n        /**\n         * The most relevant bitmap for the container. If the image was in cache, the\n         * Holder to use for the final bitmap (the one that pairs to the requested URL).\n         */\n        private Bitmap mBitmap;\n\n        private final ImageListener mListener;\n\n        /** The cache key that was associated with the request */\n        private final String mCacheKey;\n\n        /** The request URL that was specified */\n        private final String mRequestUrl;\n\n        /**\n         * Constructs a BitmapContainer object.\n         * @param bitmap The final bitmap (if it exists).\n         * @param requestUrl The requested URL for this container.\n         * @param cacheKey The cache key that identifies the requested URL for this container.\n         */\n        public ImageContainer(Bitmap bitmap, String requestUrl,\n                String cacheKey, ImageListener listener) {\n            mBitmap = bitmap;\n            mRequestUrl = requestUrl;\n            mCacheKey = cacheKey;\n            mListener = listener;\n        }\n\n        /**\n         * Releases interest in the in-flight request (and cancels it if no one else is listening).\n         */\n        public void cancelRequest() {\n            if (mListener == null) {\n                return;\n            }\n\n            BatchedImageRequest request = mInFlightRequests.get(mCacheKey);\n            if (request != null) {\n                boolean canceled = request.removeContainerAndCancelIfNecessary(this);\n                if (canceled) {\n                    mInFlightRequests.remove(mCacheKey);\n                }\n            } else {\n                // check to see if it is already batched for delivery.\n                request = mBatchedResponses.get(mCacheKey);\n                if (request != null) {\n                    request.removeContainerAndCancelIfNecessary(this);\n                    if (request.mContainers.size() == 0) {\n                        mBatchedResponses.remove(mCacheKey);\n                    }\n                }\n            }\n        }\n\n        /**\n         * Returns the bitmap associated with the request URL if it has been loaded, null otherwise.\n         */\n        public Bitmap getBitmap() {\n            return mBitmap;\n        }\n\n        /**\n         * Returns the requested URL for this container.\n         */\n        public String getRequestUrl() {\n            return mRequestUrl;\n        }\n    }\n\n    /**\n     * Wrapper class used to map a Request to the set of active ImageContainer objects that are\n     * interested in its results.\n     */\n    private class BatchedImageRequest {\n        /** The request being tracked */\n        private final Request<?> mRequest;\n\n        /** The result of the request being tracked by this item */\n        private Bitmap mResponseBitmap;\n\n        /** Error if one occurred for this response */\n        private VolleyError mError;\n\n        /** List of all of the active ImageContainers that are interested in the request */\n        private final LinkedList<ImageContainer> mContainers = new LinkedList<ImageContainer>();\n\n        /**\n         * Constructs a new BatchedImageRequest object\n         * @param request The request being tracked\n         * @param container The ImageContainer of the person who initiated the request.\n         */\n        public BatchedImageRequest(Request<?> request, ImageContainer container) {\n            mRequest = request;\n            mContainers.add(container);\n        }\n\n        /**\n         * Set the error for this response\n         */\n        public void setError(VolleyError error) {\n            mError = error;\n        }\n\n        /**\n         * Get the error for this response\n         */\n        public VolleyError getError() {\n            return mError;\n        }\n\n        /**\n         * Adds another ImageContainer to the list of those interested in the results of\n         * the request.\n         */\n        public void addContainer(ImageContainer container) {\n            mContainers.add(container);\n        }\n\n        /**\n         * Detatches the bitmap container from the request and cancels the request if no one is\n         * left listening.\n         * @param container The container to remove from the list\n         * @return True if the request was canceled, false otherwise.\n         */\n        public boolean removeContainerAndCancelIfNecessary(ImageContainer container) {\n            mContainers.remove(container);\n            if (mContainers.size() == 0) {\n                mRequest.cancel();\n                return true;\n            }\n            return false;\n        }\n    }\n\n    /**\n     * Starts the runnable for batched delivery of responses if it is not already started.\n     * @param cacheKey The cacheKey of the response being delivered.\n     * @param request The BatchedImageRequest to be delivered.\n     * @param error The volley error associated with the request (if applicable).\n     */\n    private void batchResponse(String cacheKey, BatchedImageRequest request) {\n        mBatchedResponses.put(cacheKey, request);\n        // If we don't already have a batch delivery runnable in flight, make a new one.\n        // Note that this will be used to deliver responses to all callers in mBatchedResponses.\n        if (mRunnable == null) {\n            mRunnable = new Runnable() {\n                @Override\n                public void run() {\n                    for (BatchedImageRequest bir : mBatchedResponses.values()) {\n                        for (ImageContainer container : bir.mContainers) {\n                            // If one of the callers in the batched request canceled the request\n                            // after the response was received but before it was delivered,\n                            // skip them.\n                            if (container.mListener == null) {\n                                continue;\n                            }\n                            if (bir.getError() == null) {\n                                container.mBitmap = bir.mResponseBitmap;\n                                container.mListener.onResponse(container, false);\n                            } else {\n                                container.mListener.onErrorResponse(bir.getError());\n                            }\n                        }\n                    }\n                    mBatchedResponses.clear();\n                    mRunnable = null;\n                }\n\n            };\n            // Post the runnable.\n            mHandler.postDelayed(mRunnable, mBatchResponseDelayMs);\n        }\n    }\n\n    private void throwIfNotOnMainThread() {\n        if (Looper.myLooper() != Looper.getMainLooper()) {\n            throw new IllegalStateException(\"ImageLoader must be invoked from the main thread.\");\n        }\n    }\n    /**\n     * Creates a cache key for use with the L1 cache.\n     * @param url The URL of the request.\n     * @param maxWidth The max-width of the output.\n     * @param maxHeight The max-height of the output.\n     */\n    private static String getCacheKey(String url, int maxWidth, int maxHeight) {\n        return new StringBuilder(url.length() + 12).append(\"#W\").append(maxWidth)\n                .append(\"#H\").append(maxHeight).append(url).toString();\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/toolbox/ImageRequest.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley.toolbox;\n\nimport com.android.volley.DefaultRetryPolicy;\nimport com.android.volley.NetworkResponse;\nimport com.android.volley.ParseError;\nimport com.android.volley.Request;\nimport com.android.volley.Response;\nimport com.android.volley.VolleyLog;\n\nimport android.graphics.Bitmap;\nimport android.graphics.Bitmap.Config;\nimport android.graphics.BitmapFactory;\n\n/**\n * A canned request for getting an image at a given URL and calling\n * back with a decoded Bitmap.\n */\npublic class ImageRequest extends Request<Bitmap> {\n    /** Socket timeout in milliseconds for image requests */\n    private static final int IMAGE_TIMEOUT_MS = 1000;\n\n    /** Default number of retries for image requests */\n    private static final int IMAGE_MAX_RETRIES = 2;\n\n    /** Default backoff multiplier for image requests */\n    private static final float IMAGE_BACKOFF_MULT = 2f;\n\n    private final Response.Listener<Bitmap> mListener;\n    private final Config mDecodeConfig;\n    private final int mMaxWidth;\n    private final int mMaxHeight;\n\n    /** Decoding lock so that we don't decode more than one image at a time (to avoid OOM's) */\n    private static final Object sDecodeLock = new Object();\n\n    /**\n     * Creates a new image request, decoding to a maximum specified width and\n     * height. If both width and height are zero, the image will be decoded to\n     * its natural size. If one of the two is nonzero, that dimension will be\n     * clamped and the other one will be set to preserve the image's aspect\n     * ratio. If both width and height are nonzero, the image will be decoded to\n     * be fit in the rectangle of dimensions width x height while keeping its\n     * aspect ratio.\n     *\n     * @param url URL of the image\n     * @param listener Listener to receive the decoded bitmap\n     * @param maxWidth Maximum width to decode this bitmap to, or zero for none\n     * @param maxHeight Maximum height to decode this bitmap to, or zero for\n     *            none\n     * @param decodeConfig Format to decode the bitmap to\n     * @param errorListener Error listener, or null to ignore errors\n     */\n    public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight,\n            Config decodeConfig, Response.ErrorListener errorListener) {\n        super(Method.GET, url, errorListener);\n        setRetryPolicy(\n                new DefaultRetryPolicy(IMAGE_TIMEOUT_MS, IMAGE_MAX_RETRIES, IMAGE_BACKOFF_MULT));\n        mListener = listener;\n        mDecodeConfig = decodeConfig;\n        mMaxWidth = maxWidth;\n        mMaxHeight = maxHeight;\n    }\n\n    @Override\n    public Priority getPriority() {\n        return Priority.LOW;\n    }\n\n    /**\n     * Scales one side of a rectangle to fit aspect ratio.\n     *\n     * @param maxPrimary Maximum size of the primary dimension (i.e. width for\n     *        max width), or zero to maintain aspect ratio with secondary\n     *        dimension\n     * @param maxSecondary Maximum size of the secondary dimension, or zero to\n     *        maintain aspect ratio with primary dimension\n     * @param actualPrimary Actual size of the primary dimension\n     * @param actualSecondary Actual size of the secondary dimension\n     */\n    private static int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary,\n            int actualSecondary) {\n        // If no dominant value at all, just return the actual.\n        if (maxPrimary == 0 && maxSecondary == 0) {\n            return actualPrimary;\n        }\n\n        // If primary is unspecified, scale primary to match secondary's scaling ratio.\n        if (maxPrimary == 0) {\n            double ratio = (double) maxSecondary / (double) actualSecondary;\n            return (int) (actualPrimary * ratio);\n        }\n\n        if (maxSecondary == 0) {\n            return maxPrimary;\n        }\n\n        double ratio = (double) actualSecondary / (double) actualPrimary;\n        int resized = maxPrimary;\n        if (resized * ratio > maxSecondary) {\n            resized = (int) (maxSecondary / ratio);\n        }\n        return resized;\n    }\n\n    @Override\n    protected Response<Bitmap> parseNetworkResponse(NetworkResponse response) {\n        // Serialize all decode on a global lock to reduce concurrent heap usage.\n        synchronized (sDecodeLock) {\n            try {\n                return doParse(response);\n            } catch (OutOfMemoryError e) {\n                VolleyLog.e(\"Caught OOM for %d byte image, url=%s\", response.data.length, getUrl());\n                return Response.error(new ParseError(e));\n            }\n        }\n    }\n\n    /**\n     * The real guts of parseNetworkResponse. Broken out for readability.\n     */\n    private Response<Bitmap> doParse(NetworkResponse response) {\n        byte[] data = response.data;\n        BitmapFactory.Options decodeOptions = new BitmapFactory.Options();\n        Bitmap bitmap = null;\n        if (mMaxWidth == 0 && mMaxHeight == 0) {\n            decodeOptions.inPreferredConfig = mDecodeConfig;\n            bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);\n        } else {\n            // If we have to resize this image, first get the natural bounds.\n            decodeOptions.inJustDecodeBounds = true;\n            BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);\n            int actualWidth = decodeOptions.outWidth;\n            int actualHeight = decodeOptions.outHeight;\n\n            // Then compute the dimensions we would ideally like to decode to.\n            int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,\n                    actualWidth, actualHeight);\n            int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,\n                    actualHeight, actualWidth);\n\n            // Decode to the nearest power of two scaling factor.\n            decodeOptions.inJustDecodeBounds = false;\n            // TODO(ficus): Do we need this or is it okay since API 8 doesn't support it?\n            // decodeOptions.inPreferQualityOverSpeed = PREFER_QUALITY_OVER_SPEED;\n            decodeOptions.inSampleSize =\n                findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight);\n            Bitmap tempBitmap =\n                BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);\n\n            // If necessary, scale down to the maximal acceptable size.\n            if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth ||\n                    tempBitmap.getHeight() > desiredHeight)) {\n                bitmap = Bitmap.createScaledBitmap(tempBitmap,\n                        desiredWidth, desiredHeight, true);\n                tempBitmap.recycle();\n            } else {\n                bitmap = tempBitmap;\n            }\n        }\n\n        if (bitmap == null) {\n            return Response.error(new ParseError(response));\n        } else {\n            return Response.success(bitmap, HttpHeaderParser.parseCacheHeaders(response));\n        }\n    }\n\n    @Override\n    protected void deliverResponse(Bitmap response) {\n        mListener.onResponse(response);\n    }\n\n    /**\n     * Returns the largest power-of-two divisor for use in downscaling a bitmap\n     * that will not result in the scaling past the desired dimensions.\n     *\n     * @param actualWidth Actual width of the bitmap\n     * @param actualHeight Actual height of the bitmap\n     * @param desiredWidth Desired width of the bitmap\n     * @param desiredHeight Desired height of the bitmap\n     */\n    // Visible for testing.\n    static int findBestSampleSize(\n            int actualWidth, int actualHeight, int desiredWidth, int desiredHeight) {\n        double wr = (double) actualWidth / desiredWidth;\n        double hr = (double) actualHeight / desiredHeight;\n        double ratio = Math.min(wr, hr);\n        float n = 1.0f;\n        while ((n * 2) <= ratio) {\n            n *= 2;\n        }\n\n        return (int) n;\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/toolbox/JsonArrayRequest.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley.toolbox;\n\nimport com.android.volley.NetworkResponse;\nimport com.android.volley.ParseError;\nimport com.android.volley.Response;\nimport com.android.volley.Response.ErrorListener;\nimport com.android.volley.Response.Listener;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\n\nimport java.io.UnsupportedEncodingException;\n\n/**\n * A request for retrieving a {@link JSONArray} response body at a given URL.\n */\npublic class JsonArrayRequest extends JsonRequest<JSONArray> {\n\n    /**\n     * Creates a new request.\n     * @param url URL to fetch the JSON from\n     * @param listener Listener to receive the JSON response\n     * @param errorListener Error listener, or null to ignore errors.\n     */\n    public JsonArrayRequest(String url, Listener<JSONArray> listener, ErrorListener errorListener) {\n        super(Method.GET, url, null, listener, errorListener);\n    }\n\n    @Override\n    protected Response<JSONArray> parseNetworkResponse(NetworkResponse response) {\n        try {\n            String jsonString =\n                new String(response.data, /*HttpHeaderParser.parseCharset(response.headers)*/\"UTF-8\");\n            return Response.success(new JSONArray(jsonString),\n                    HttpHeaderParser.parseCacheHeaders(response));\n        } catch (UnsupportedEncodingException e) {\n            return Response.error(new ParseError(e));\n        } catch (JSONException je) {\n            return Response.error(new ParseError(je));\n        }\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/toolbox/JsonObjectRequest.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley.toolbox;\n\nimport com.android.volley.NetworkResponse;\nimport com.android.volley.ParseError;\nimport com.android.volley.Response;\nimport com.android.volley.Response.ErrorListener;\nimport com.android.volley.Response.Listener;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.io.UnsupportedEncodingException;\n\n/**\n * A request for retrieving a {@link JSONObject} response body at a given URL, allowing for an\n * optional {@link JSONObject} to be passed in as part of the request body.\n */\npublic class JsonObjectRequest extends JsonRequest<JSONObject> {\n\n    /**\n     * Creates a new request.\n     * @param method the HTTP method to use\n     * @param url URL to fetch the JSON from\n     * @param jsonRequest A {@link JSONObject} to post with the request. Null is allowed and\n     *   indicates no parameters will be posted along with request.\n     * @param listener Listener to receive the JSON response\n     * @param errorListener Error listener, or null to ignore errors.\n     */\n    public JsonObjectRequest(int method, String url, JSONObject jsonRequest,\n            Listener<JSONObject> listener, ErrorListener errorListener) {\n        super(method, url, (jsonRequest == null) ? null : jsonRequest.toString(), listener,\n                    errorListener);\n    }\n\n    /**\n     * Constructor which defaults to <code>GET</code> if <code>jsonRequest</code> is\n     * <code>null</code>, <code>POST</code> otherwise.\n     *\n     * @see #JsonObjectRequest(int, String, JSONObject, Listener, ErrorListener)\n     */\n    public JsonObjectRequest(String url, JSONObject jsonRequest, Listener<JSONObject> listener,\n            ErrorListener errorListener) {\n        this(jsonRequest == null ? Method.GET : Method.POST, url, jsonRequest,\n                listener, errorListener);\n    }\n\n    @Override\n    protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {\n        try {\n            String jsonString =\n                new String(response.data, /*HttpHeaderParser.parseCharset(response.headers)*/\"UTF-8\");\n            return Response.success(new JSONObject(jsonString),\n                    HttpHeaderParser.parseCacheHeaders(response));\n        } catch (UnsupportedEncodingException e) {\n            return Response.error(new ParseError(e));\n        } catch (JSONException je) {\n            return Response.error(new ParseError(je));\n        }\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/toolbox/JsonRequest.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley.toolbox;\n\nimport com.android.volley.NetworkResponse;\nimport com.android.volley.Request;\nimport com.android.volley.Response;\nimport com.android.volley.Response.ErrorListener;\nimport com.android.volley.Response.Listener;\nimport com.android.volley.VolleyLog;\n\nimport java.io.UnsupportedEncodingException;\n\n/**\n * A request for retrieving a T type response body at a given URL that also\n * optionally sends along a JSON body in the request specified.\n *\n * @param <T> JSON type of response expected\n */\npublic abstract class JsonRequest<T> extends Request<T> {\n    /** Charset for request. */\n    private static final String PROTOCOL_CHARSET = \"utf-8\";\n\n    /** Content type for request. */\n    private static final String PROTOCOL_CONTENT_TYPE =\n        String.format(\"application/json; charset=%s\", PROTOCOL_CHARSET);\n\n    private final Listener<T> mListener;\n    private final String mRequestBody;\n\n    /**\n     * Deprecated constructor for a JsonRequest which defaults to GET unless {@link #getPostBody()}\n     * or {@link #getPostParams()} is overridden (which defaults to POST).\n     *\n     * @deprecated Use {@link #JsonRequest(int, String, String, Listener, ErrorListener)}.\n     */\n    public JsonRequest(String url, String requestBody, Listener<T> listener,\n            ErrorListener errorListener) {\n        this(Method.DEPRECATED_GET_OR_POST, url, requestBody, listener, errorListener);\n    }\n\n    public JsonRequest(int method, String url, String requestBody, Listener<T> listener,\n            ErrorListener errorListener) {\n        super(method, url, errorListener);\n        mListener = listener;\n        mRequestBody = requestBody;\n    }\n\n    @Override\n    protected void deliverResponse(T response) {\n        mListener.onResponse(response);\n    }\n\n    @Override\n    abstract protected Response<T> parseNetworkResponse(NetworkResponse response);\n\n    /**\n     * @deprecated Use {@link #getBodyContentType()}.\n     */\n    @Override\n    public String getPostBodyContentType() {\n        return getBodyContentType();\n    }\n\n    /**\n     * @deprecated Use {@link #getBody()}.\n     */\n    @Override\n    public byte[] getPostBody() {\n        return getBody();\n    }\n\n    @Override\n    public String getBodyContentType() {\n        return PROTOCOL_CONTENT_TYPE;\n    }\n\n    @Override\n    public byte[] getBody() {\n        try {\n            return mRequestBody == null ? null : mRequestBody.getBytes(PROTOCOL_CHARSET);\n        } catch (UnsupportedEncodingException uee) {\n            VolleyLog.wtf(\"Unsupported Encoding while trying to get the bytes of %s using %s\",\n                    mRequestBody, PROTOCOL_CHARSET);\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/toolbox/NetworkImageView.java",
    "content": "/**\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.android.volley.toolbox;\n\nimport android.content.Context;\nimport android.text.TextUtils;\nimport android.util.AttributeSet;\nimport android.view.ViewGroup.LayoutParams;\nimport android.widget.ImageView;\n\nimport com.android.volley.VolleyError;\nimport com.android.volley.toolbox.ImageLoader.ImageContainer;\nimport com.android.volley.toolbox.ImageLoader.ImageListener;\n\n/**\n * Handles fetching an image from a URL as well as the life-cycle of the\n * associated request.\n */\npublic class NetworkImageView extends ImageView {\n    /** The URL of the network image to load */\n    private String mUrl;\n\n    /**\n     * Resource ID of the image to be used as a placeholder until the network image is loaded.\n     */\n    private int mDefaultImageId;\n\n    /**\n     * Resource ID of the image to be used if the network response fails.\n     */\n    private int mErrorImageId;\n\n    /** Local copy of the ImageLoader. */\n    private ImageLoader mImageLoader;\n\n    /** Current ImageContainer. (either in-flight or finished) */\n    private ImageContainer mImageContainer;\n\n    public NetworkImageView(Context context) {\n        this(context, null);\n    }\n\n    public NetworkImageView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public NetworkImageView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n    }\n\n    /**\n     * Sets URL of the image that should be loaded into this view. Note that calling this will\n     * immediately either set the cached image (if available) or the default image specified by\n     * {@link NetworkImageView#setDefaultImageResId(int)} on the view.\n     *\n     * NOTE: If applicable, {@link NetworkImageView#setDefaultImageResId(int)} and\n     * {@link NetworkImageView#setErrorImageResId(int)} should be called prior to calling\n     * this function.\n     *\n     * @param url The URL that should be loaded into this ImageView.\n     * @param imageLoader ImageLoader that will be used to make the request.\n     */\n    public void setImageUrl(String url, ImageLoader imageLoader) {\n        mUrl = url;\n        mImageLoader = imageLoader;\n        // The URL has potentially changed. See if we need to load it.\n        loadImageIfNecessary(false);\n    }\n\n    /**\n     * Sets the default image resource ID to be used for this view until the attempt to load it\n     * completes.\n     */\n    public void setDefaultImageResId(int defaultImage) {\n        mDefaultImageId = defaultImage;\n    }\n\n    /**\n     * Sets the error image resource ID to be used for this view in the event that the image\n     * requested fails to load.\n     */\n    public void setErrorImageResId(int errorImage) {\n        mErrorImageId = errorImage;\n    }\n\n    /**\n     * Loads the image for the view if it isn't already loaded.\n     * @param isInLayoutPass True if this was invoked from a layout pass, false otherwise.\n     */\n    void loadImageIfNecessary(final boolean isInLayoutPass) {\n        int width = getWidth();\n        int height = getHeight();\n\n        boolean wrapWidth = false, wrapHeight = false;\n        if (getLayoutParams() != null) {\n            wrapWidth = getLayoutParams().width == LayoutParams.WRAP_CONTENT;\n            wrapHeight = getLayoutParams().height == LayoutParams.WRAP_CONTENT;\n        }\n\n        // if the view's bounds aren't known yet, and this is not a wrap-content/wrap-content\n        // view, hold off on loading the image.\n        boolean isFullyWrapContent = wrapWidth && wrapHeight;\n        if (width == 0 && height == 0 && !isFullyWrapContent) {\n            return;\n        }\n\n        // if the URL to be loaded in this view is empty, cancel any old requests and clear the\n        // currently loaded image.\n        if (TextUtils.isEmpty(mUrl)) {\n            if (mImageContainer != null) {\n                mImageContainer.cancelRequest();\n                mImageContainer = null;\n            }\n            setDefaultImageOrNull();\n            return;\n        }\n\n        // if there was an old request in this view, check if it needs to be canceled.\n        if (mImageContainer != null && mImageContainer.getRequestUrl() != null) {\n            if (mImageContainer.getRequestUrl().equals(mUrl)) {\n                // if the request is from the same URL, return.\n                return;\n            } else {\n                // if there is a pre-existing request, cancel it if it's fetching a different URL.\n                mImageContainer.cancelRequest();\n                setDefaultImageOrNull();\n            }\n        }\n\n        // Calculate the max image width / height to use while ignoring WRAP_CONTENT dimens.\n        int maxWidth = wrapWidth ? 0 : width;\n        int maxHeight = wrapHeight ? 0 : height;\n\n        // The pre-existing content of this view didn't match the current URL. Load the new image\n        // from the network.\n        ImageContainer newContainer = mImageLoader.get(mUrl,\n                new ImageListener() {\n                    @Override\n                    public void onErrorResponse(VolleyError error) {\n                        if (mErrorImageId != 0) {\n                            setImageResource(mErrorImageId);\n                        }\n                    }\n\n                    @Override\n                    public void onResponse(final ImageContainer response, boolean isImmediate) {\n                        // If this was an immediate response that was delivered inside of a layout\n                        // pass do not set the image immediately as it will trigger a requestLayout\n                        // inside of a layout. Instead, defer setting the image by posting back to\n                        // the main thread.\n                        if (isImmediate && isInLayoutPass) {\n                            post(new Runnable() {\n                                @Override\n                                public void run() {\n                                    onResponse(response, false);\n                                }\n                            });\n                            return;\n                        }\n\n                        if (response.getBitmap() != null) {\n                            setImageBitmap(response.getBitmap());\n                        } else if (mDefaultImageId != 0) {\n                            setImageResource(mDefaultImageId);\n                        }\n                    }\n                }, maxWidth, maxHeight);\n\n        // update the ImageContainer to be the new bitmap container.\n        mImageContainer = newContainer;\n    }\n\n    private void setDefaultImageOrNull() {\n        if(mDefaultImageId != 0) {\n            setImageResource(mDefaultImageId);\n        }\n        else {\n            setImageBitmap(null);\n        }\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n        super.onLayout(changed, left, top, right, bottom);\n        loadImageIfNecessary(true);\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        if (mImageContainer != null) {\n            // If the view was bound to an image request, cancel it and clear\n            // out the image from the view.\n            mImageContainer.cancelRequest();\n            setImageBitmap(null);\n            // also clear out the container so we can reload the image if necessary.\n            mImageContainer = null;\n        }\n        super.onDetachedFromWindow();\n    }\n\n    @Override\n    protected void drawableStateChanged() {\n        super.drawableStateChanged();\n        invalidate();\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/toolbox/NoCache.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley.toolbox;\n\nimport com.android.volley.Cache;\n\n/**\n * A cache that doesn't.\n */\npublic class NoCache implements Cache {\n    @Override\n    public void clear() {\n    }\n\n    @Override\n    public Entry get(String key) {\n        return null;\n    }\n\n    @Override\n    public void put(String key, Entry entry) {\n    }\n\n    @Override\n    public void invalidate(String key, boolean fullExpire) {\n    }\n\n    @Override\n    public void remove(String key) {\n    }\n\n    @Override\n    public void initialize() {\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/toolbox/PoolingByteArrayOutputStream.java",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley.toolbox;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\n\n/**\n * A variation of {@link java.io.ByteArrayOutputStream} that uses a pool of byte[] buffers instead\n * of always allocating them fresh, saving on heap churn.\n */\npublic class PoolingByteArrayOutputStream extends ByteArrayOutputStream {\n    /**\n     * If the {@link #PoolingByteArrayOutputStream(ByteArrayPool)} constructor is called, this is\n     * the default size to which the underlying byte array is initialized.\n     */\n    private static final int DEFAULT_SIZE = 256;\n\n    private final ByteArrayPool mPool;\n\n    /**\n     * Constructs a new PoolingByteArrayOutputStream with a default size. If more bytes are written\n     * to this instance, the underlying byte array will expand.\n     */\n    public PoolingByteArrayOutputStream(ByteArrayPool pool) {\n        this(pool, DEFAULT_SIZE);\n    }\n\n    /**\n     * Constructs a new {@code ByteArrayOutputStream} with a default size of {@code size} bytes. If\n     * more than {@code size} bytes are written to this instance, the underlying byte array will\n     * expand.\n     *\n     * @param size initial size for the underlying byte array. The value will be pinned to a default\n     *        minimum size.\n     */\n    public PoolingByteArrayOutputStream(ByteArrayPool pool, int size) {\n        mPool = pool;\n        buf = mPool.getBuf(Math.max(size, DEFAULT_SIZE));\n    }\n\n    @Override\n    public void close() throws IOException {\n        mPool.returnBuf(buf);\n        buf = null;\n        super.close();\n    }\n\n    @Override\n    public void finalize() {\n        mPool.returnBuf(buf);\n    }\n\n    /**\n     * Ensures there is enough space in the buffer for the given number of additional bytes.\n     */\n    private void expand(int i) {\n        /* Can the buffer handle @i more bytes, if not expand it */\n        if (count + i <= buf.length) {\n            return;\n        }\n        byte[] newbuf = mPool.getBuf((count + i) * 2);\n        System.arraycopy(buf, 0, newbuf, 0, count);\n        mPool.returnBuf(buf);\n        buf = newbuf;\n    }\n\n    @Override\n    public synchronized void write(byte[] buffer, int offset, int len) {\n        expand(len);\n        super.write(buffer, offset, len);\n    }\n\n    @Override\n    public synchronized void write(int oneByte) {\n        expand(1);\n        super.write(oneByte);\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/toolbox/RequestFuture.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley.toolbox;\n\nimport com.android.volley.Request;\nimport com.android.volley.Response;\nimport com.android.volley.VolleyError;\n\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\n\n/**\n * A Future that represents a Volley request.\n *\n * Used by providing as your response and error listeners. For example:\n * <pre>\n * RequestFuture&lt;JSONObject&gt; future = RequestFuture.newFuture();\n * MyRequest request = new MyRequest(URL, future, future);\n *\n * // If you want to be able to cancel the request:\n * future.setRequest(requestQueue.add(request));\n *\n * // Otherwise:\n * requestQueue.add(request);\n *\n * try {\n *   JSONObject response = future.get();\n *   // do something with response\n * } catch (InterruptedException e) {\n *   // handle the error\n * } catch (ExecutionException e) {\n *   // handle the error\n * }\n * </pre>\n *\n * @param <T> The type of parsed response this future expects.\n */\npublic class RequestFuture<T> implements Future<T>, Response.Listener<T>,\n       Response.ErrorListener {\n    private Request<?> mRequest;\n    private boolean mResultReceived = false;\n    private T mResult;\n    private VolleyError mException;\n\n    public static <E> RequestFuture<E> newFuture() {\n        return new RequestFuture<E>();\n    }\n\n    private RequestFuture() {}\n\n    public void setRequest(Request<?> request) {\n        mRequest = request;\n    }\n\n    @Override\n    public synchronized boolean cancel(boolean mayInterruptIfRunning) {\n        if (mRequest == null) {\n            return false;\n        }\n\n        if (!isDone()) {\n            mRequest.cancel();\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    @Override\n    public T get() throws InterruptedException, ExecutionException {\n        try {\n            return doGet(null);\n        } catch (TimeoutException e) {\n            throw new AssertionError(e);\n        }\n    }\n\n    @Override\n    public T get(long timeout, TimeUnit unit)\n            throws InterruptedException, ExecutionException, TimeoutException {\n        return doGet(TimeUnit.MILLISECONDS.convert(timeout, unit));\n    }\n\n    private synchronized T doGet(Long timeoutMs)\n            throws InterruptedException, ExecutionException, TimeoutException {\n        if (mException != null) {\n            throw new ExecutionException(mException);\n        }\n\n        if (mResultReceived) {\n            return mResult;\n        }\n\n        if (timeoutMs == null) {\n            wait(0);\n        } else if (timeoutMs > 0) {\n            wait(timeoutMs);\n        }\n\n        if (mException != null) {\n            throw new ExecutionException(mException);\n        }\n\n        if (!mResultReceived) {\n            throw new TimeoutException();\n        }\n\n        return mResult;\n    }\n\n    @Override\n    public boolean isCancelled() {\n        if (mRequest == null) {\n            return false;\n        }\n        return mRequest.isCanceled();\n    }\n\n    @Override\n    public synchronized boolean isDone() {\n        return mResultReceived || mException != null || isCancelled();\n    }\n\n    @Override\n    public synchronized void onResponse(T response) {\n        mResultReceived = true;\n        mResult = response;\n        notifyAll();\n    }\n\n    @Override\n    public synchronized void onErrorResponse(VolleyError error) {\n        mException = error;\n        notifyAll();\n    }\n}\n\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/toolbox/StringRequest.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley.toolbox;\n\nimport com.android.volley.NetworkResponse;\nimport com.android.volley.Request;\nimport com.android.volley.Response;\nimport com.android.volley.Response.ErrorListener;\nimport com.android.volley.Response.Listener;\n\nimport java.io.UnsupportedEncodingException;\n\n/**\n * A canned request for retrieving the response body at a given URL as a String.\n */\npublic class StringRequest extends Request<String> {\n    private final Listener<String> mListener;\n\n    /**\n     * Creates a new request with the given method.\n     *\n     * @param method the request {@link Method} to use\n     * @param url URL to fetch the string at\n     * @param listener Listener to receive the String response\n     * @param errorListener Error listener, or null to ignore errors\n     */\n    public StringRequest(int method, String url, Listener<String> listener,\n            ErrorListener errorListener) {\n        super(method, url, errorListener);\n        mListener = listener;\n    }\n\n    /**\n     * Creates a new GET request.\n     *\n     * @param url URL to fetch the string at\n     * @param listener Listener to receive the String response\n     * @param errorListener Error listener, or null to ignore errors\n     */\n    public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {\n        this(Method.GET, url, listener, errorListener);\n    }\n\n    @Override\n    protected void deliverResponse(String response) {\n        mListener.onResponse(response);\n    }\n\n    @Override\n    protected Response<String> parseNetworkResponse(NetworkResponse response) {\n        String parsed;\n        try {\n            parsed = new String(response.data, /*HttpHeaderParser.parseCharset(response.headers)*/\"UTF-8\");\n        } catch (UnsupportedEncodingException e) {\n            parsed = new String(response.data);\n        }\n        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/android/volley/toolbox/Volley.java",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.volley.toolbox;\n\nimport android.content.Context;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager.NameNotFoundException;\nimport android.net.http.AndroidHttpClient;\nimport android.os.Build;\n\nimport com.android.volley.Network;\nimport com.android.volley.RequestQueue;\n\nimport java.io.File;\n\npublic class Volley {\n\n    /** Default on-disk cache directory. */\n    private static final String DEFAULT_CACHE_DIR = \"volley\";\n\n    /**\n     * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.\n     *\n     * @param context A {@link Context} to use for creating the cache dir.\n     * @param stack An {@link HttpStack} to use for the network, or null for default.\n     * @return A started {@link RequestQueue} instance.\n     */\n    public static RequestQueue newRequestQueue(Context context, HttpStack stack) {\n        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);\n\n        String userAgent = \"volley/0\";\n        try {\n            String packageName = context.getPackageName();\n            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);\n            userAgent = packageName + \"/\" + info.versionCode;\n        } catch (NameNotFoundException e) {\n        }\n\n        if (stack == null) {\n            if (Build.VERSION.SDK_INT >= 9) {\n                stack = new HurlStack();\n            } else {\n                // Prior to Gingerbread, HttpUrlConnection was unreliable.\n                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html\n                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));\n            }\n        }\n\n        Network network = new BasicNetwork(stack);\n\n        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);\n        queue.start();\n\n        return queue;\n    }\n\n    /**\n     * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.\n     *\n     * @param context A {@link Context} to use for creating the cache dir.\n     * @return A started {@link RequestQueue} instance.\n     */\n    public static RequestQueue newRequestQueue(Context context) {\n        return newRequestQueue(context, null);\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/viewpagerindicator/CirclePageIndicator.java",
    "content": "/*\n * Copyright (C) 2011 Patrik Akerfeldt\n * Copyright (C) 2011 Jake Wharton\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.viewpagerindicator;\n\nimport static android.graphics.Paint.ANTI_ALIAS_FLAG;\nimport static android.widget.LinearLayout.HORIZONTAL;\nimport static android.widget.LinearLayout.VERTICAL;\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.Paint.Style;\nimport android.graphics.drawable.Drawable;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.support.v4.view.MotionEventCompat;\nimport android.support.v4.view.ViewConfigurationCompat;\nimport android.support.v4.view.ViewPager;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewConfiguration;\n\nimport com.way.yahoo.R;\n\n/**\n * Draws circles (one for each view). The current view position is filled and\n * others are only stroked.\n */\npublic class CirclePageIndicator extends View implements PageIndicator {\n    private static final int INVALID_POINTER = -1;\n\n    private float mRadius;\n    private final Paint mPaintPageFill = new Paint(ANTI_ALIAS_FLAG);\n    private final Paint mPaintStroke = new Paint(ANTI_ALIAS_FLAG);\n    private final Paint mPaintFill = new Paint(ANTI_ALIAS_FLAG);\n    private ViewPager mViewPager;\n    private ViewPager.OnPageChangeListener mListener;\n    private int mCurrentPage;\n    private int mSnapPage;\n    private float mPageOffset;\n    private int mScrollState;\n    private int mOrientation;\n    private boolean mCentered;\n    private boolean mSnap;\n\n    private int mTouchSlop;\n    private float mLastMotionX = -1;\n    private int mActivePointerId = INVALID_POINTER;\n    private boolean mIsDragging;\n\n\n    public CirclePageIndicator(Context context) {\n        this(context, null);\n    }\n\n    public CirclePageIndicator(Context context, AttributeSet attrs) {\n        this(context, attrs, R.attr.vpiCirclePageIndicatorStyle);\n    }\n\n    public CirclePageIndicator(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        if (isInEditMode()) return;\n\n        //Load defaults from resources\n        final Resources res = getResources();\n        final int defaultPageColor = res.getColor(R.color.default_circle_indicator_page_color);\n        final int defaultFillColor = res.getColor(R.color.default_circle_indicator_fill_color);\n        final int defaultOrientation = res.getInteger(R.integer.default_circle_indicator_orientation);\n        final int defaultStrokeColor = res.getColor(R.color.default_circle_indicator_stroke_color);\n        final float defaultStrokeWidth = res.getDimension(R.dimen.default_circle_indicator_stroke_width);\n        final float defaultRadius = res.getDimension(R.dimen.default_circle_indicator_radius);\n        final boolean defaultCentered = res.getBoolean(R.bool.default_circle_indicator_centered);\n        final boolean defaultSnap = res.getBoolean(R.bool.default_circle_indicator_snap);\n\n        //Retrieve styles attributes\n        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CirclePageIndicator, defStyle, 0);\n\n        mCentered = a.getBoolean(R.styleable.CirclePageIndicator_centered, defaultCentered);\n        mOrientation = a.getInt(R.styleable.CirclePageIndicator_android_orientation, defaultOrientation);\n        mPaintPageFill.setStyle(Style.FILL);\n        mPaintPageFill.setColor(a.getColor(R.styleable.CirclePageIndicator_pageColor, defaultPageColor));\n        mPaintStroke.setStyle(Style.STROKE);\n        mPaintStroke.setColor(a.getColor(R.styleable.CirclePageIndicator_strokeColor, defaultStrokeColor));\n        mPaintStroke.setStrokeWidth(a.getDimension(R.styleable.CirclePageIndicator_strokeWidth, defaultStrokeWidth));\n        mPaintFill.setStyle(Style.FILL);\n        mPaintFill.setColor(a.getColor(R.styleable.CirclePageIndicator_fillColor, defaultFillColor));\n        mRadius = a.getDimension(R.styleable.CirclePageIndicator_radius, defaultRadius);\n        mSnap = a.getBoolean(R.styleable.CirclePageIndicator_snap, defaultSnap);\n\n        Drawable background = a.getDrawable(R.styleable.CirclePageIndicator_android_background);\n        if (background != null) {\n          setBackgroundDrawable(background);\n        }\n\n        a.recycle();\n\n        final ViewConfiguration configuration = ViewConfiguration.get(context);\n        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);\n    }\n\n\n    public void setCentered(boolean centered) {\n        mCentered = centered;\n        invalidate();\n    }\n\n    public boolean isCentered() {\n        return mCentered;\n    }\n\n    public void setPageColor(int pageColor) {\n        mPaintPageFill.setColor(pageColor);\n        invalidate();\n    }\n\n    public int getPageColor() {\n        return mPaintPageFill.getColor();\n    }\n\n    public void setFillColor(int fillColor) {\n        mPaintFill.setColor(fillColor);\n        invalidate();\n    }\n\n    public int getFillColor() {\n        return mPaintFill.getColor();\n    }\n\n    public void setOrientation(int orientation) {\n        switch (orientation) {\n            case HORIZONTAL:\n            case VERTICAL:\n                mOrientation = orientation;\n                requestLayout();\n                break;\n\n            default:\n                throw new IllegalArgumentException(\"Orientation must be either HORIZONTAL or VERTICAL.\");\n        }\n    }\n\n    public int getOrientation() {\n        return mOrientation;\n    }\n\n    public void setStrokeColor(int strokeColor) {\n        mPaintStroke.setColor(strokeColor);\n        invalidate();\n    }\n\n    public int getStrokeColor() {\n        return mPaintStroke.getColor();\n    }\n\n    public void setStrokeWidth(float strokeWidth) {\n        mPaintStroke.setStrokeWidth(strokeWidth);\n        invalidate();\n    }\n\n    public float getStrokeWidth() {\n        return mPaintStroke.getStrokeWidth();\n    }\n\n    public void setRadius(float radius) {\n        mRadius = radius;\n        invalidate();\n    }\n\n    public float getRadius() {\n        return mRadius;\n    }\n\n    public void setSnap(boolean snap) {\n        mSnap = snap;\n        invalidate();\n    }\n\n    public boolean isSnap() {\n        return mSnap;\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n\n        if (mViewPager == null) {\n            return;\n        }\n        final int count = mViewPager.getAdapter().getCount();\n        if (count == 0) {\n            return;\n        }\n\n        if (mCurrentPage >= count) {\n            setCurrentItem(count - 1);\n            return;\n        }\n\n        int longSize;\n        int longPaddingBefore;\n        int longPaddingAfter;\n        int shortPaddingBefore;\n        if (mOrientation == HORIZONTAL) {\n            longSize = getWidth();\n            longPaddingBefore = getPaddingLeft();\n            longPaddingAfter = getPaddingRight();\n            shortPaddingBefore = getPaddingTop();\n        } else {\n            longSize = getHeight();\n            longPaddingBefore = getPaddingTop();\n            longPaddingAfter = getPaddingBottom();\n            shortPaddingBefore = getPaddingLeft();\n        }\n\n        final float threeRadius = mRadius * 3;\n        final float shortOffset = shortPaddingBefore + mRadius;\n        float longOffset = longPaddingBefore + mRadius;\n        if (mCentered) {\n            longOffset += ((longSize - longPaddingBefore - longPaddingAfter) / 2.0f) - ((count * threeRadius) / 2.0f);\n        }\n\n        float dX;\n        float dY;\n\n        float pageFillRadius = mRadius;\n        if (mPaintStroke.getStrokeWidth() > 0) {\n            pageFillRadius -= mPaintStroke.getStrokeWidth() / 2.0f;\n        }\n\n        //Draw stroked circles\n        for (int iLoop = 0; iLoop < count; iLoop++) {\n            float drawLong = longOffset + (iLoop * threeRadius);\n            if (mOrientation == HORIZONTAL) {\n                dX = drawLong;\n                dY = shortOffset;\n            } else {\n                dX = shortOffset;\n                dY = drawLong;\n            }\n            // Only paint fill if not completely transparent\n            if (mPaintPageFill.getAlpha() > 0) {\n                canvas.drawCircle(dX, dY, pageFillRadius, mPaintPageFill);\n            }\n\n            // Only paint stroke if a stroke width was non-zero\n            if (pageFillRadius != mRadius) {\n                canvas.drawCircle(dX, dY, mRadius, mPaintStroke);\n            }\n        }\n\n        //Draw the filled circle according to the current scroll\n        float cx = (mSnap ? mSnapPage : mCurrentPage) * threeRadius;\n        if (!mSnap) {\n            cx += mPageOffset * threeRadius;\n        }\n        if (mOrientation == HORIZONTAL) {\n            dX = longOffset + cx;\n            dY = shortOffset;\n        } else {\n            dX = shortOffset;\n            dY = longOffset + cx;\n        }\n        canvas.drawCircle(dX, dY, mRadius, mPaintFill);\n    }\n\n    public boolean onTouchEvent(android.view.MotionEvent ev) {\n        if (super.onTouchEvent(ev)) {\n            return true;\n        }\n        if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) {\n            return false;\n        }\n\n        final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;\n        switch (action) {\n            case MotionEvent.ACTION_DOWN:\n                mActivePointerId = MotionEventCompat.getPointerId(ev, 0);\n                mLastMotionX = ev.getX();\n                break;\n\n            case MotionEvent.ACTION_MOVE: {\n                final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);\n                final float x = MotionEventCompat.getX(ev, activePointerIndex);\n                final float deltaX = x - mLastMotionX;\n\n                if (!mIsDragging) {\n                    if (Math.abs(deltaX) > mTouchSlop) {\n                        mIsDragging = true;\n                    }\n                }\n\n                if (mIsDragging) {\n                    mLastMotionX = x;\n                    if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) {\n                        mViewPager.fakeDragBy(deltaX);\n                    }\n                }\n\n                break;\n            }\n\n            case MotionEvent.ACTION_CANCEL:\n            case MotionEvent.ACTION_UP:\n                if (!mIsDragging) {\n                    final int count = mViewPager.getAdapter().getCount();\n                    final int width = getWidth();\n                    final float halfWidth = width / 2f;\n                    final float sixthWidth = width / 6f;\n\n                    if ((mCurrentPage > 0) && (ev.getX() < halfWidth - sixthWidth)) {\n                        if (action != MotionEvent.ACTION_CANCEL) {\n                            mViewPager.setCurrentItem(mCurrentPage - 1);\n                        }\n                        return true;\n                    } else if ((mCurrentPage < count - 1) && (ev.getX() > halfWidth + sixthWidth)) {\n                        if (action != MotionEvent.ACTION_CANCEL) {\n                            mViewPager.setCurrentItem(mCurrentPage + 1);\n                        }\n                        return true;\n                    }\n                }\n\n                mIsDragging = false;\n                mActivePointerId = INVALID_POINTER;\n                if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag();\n                break;\n\n            case MotionEventCompat.ACTION_POINTER_DOWN: {\n                final int index = MotionEventCompat.getActionIndex(ev);\n                mLastMotionX = MotionEventCompat.getX(ev, index);\n                mActivePointerId = MotionEventCompat.getPointerId(ev, index);\n                break;\n            }\n\n            case MotionEventCompat.ACTION_POINTER_UP:\n                final int pointerIndex = MotionEventCompat.getActionIndex(ev);\n                final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);\n                if (pointerId == mActivePointerId) {\n                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;\n                    mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);\n                }\n                mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId));\n                break;\n        }\n\n        return true;\n    }\n\n    @Override\n    public void setViewPager(ViewPager view) {\n        if (mViewPager == view) {\n            return;\n        }\n        if (mViewPager != null) {\n            mViewPager.setOnPageChangeListener(null);\n        }\n        if (view.getAdapter() == null) {\n            throw new IllegalStateException(\"ViewPager does not have adapter instance.\");\n        }\n        mViewPager = view;\n        mViewPager.setOnPageChangeListener(this);\n        invalidate();\n    }\n\n    @Override\n    public void setViewPager(ViewPager view, int initialPosition) {\n        setViewPager(view);\n        setCurrentItem(initialPosition);\n    }\n\n    @Override\n    public void setCurrentItem(int item) {\n        if (mViewPager == null) {\n            throw new IllegalStateException(\"ViewPager has not been bound.\");\n        }\n        mViewPager.setCurrentItem(item);\n        mCurrentPage = item;\n        invalidate();\n    }\n\n    @Override\n    public void notifyDataSetChanged() {\n        invalidate();\n    }\n\n    @Override\n    public void onPageScrollStateChanged(int state) {\n        mScrollState = state;\n\n        if (mListener != null) {\n            mListener.onPageScrollStateChanged(state);\n        }\n    }\n\n    @Override\n    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {\n        mCurrentPage = position;\n        mPageOffset = positionOffset;\n        invalidate();\n\n        if (mListener != null) {\n            mListener.onPageScrolled(position, positionOffset, positionOffsetPixels);\n        }\n    }\n\n    @Override\n    public void onPageSelected(int position) {\n        if (mSnap || mScrollState == ViewPager.SCROLL_STATE_IDLE) {\n            mCurrentPage = position;\n            mSnapPage = position;\n            invalidate();\n        }\n\n        if (mListener != null) {\n            mListener.onPageSelected(position);\n        }\n    }\n\n    @Override\n    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {\n        mListener = listener;\n    }\n\n    /*\n     * (non-Javadoc)\n     *\n     * @see android.view.View#onMeasure(int, int)\n     */\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        if (mOrientation == HORIZONTAL) {\n            setMeasuredDimension(measureLong(widthMeasureSpec), measureShort(heightMeasureSpec));\n        } else {\n            setMeasuredDimension(measureShort(widthMeasureSpec), measureLong(heightMeasureSpec));\n        }\n    }\n\n    /**\n     * Determines the width of this view\n     *\n     * @param measureSpec\n     *            A measureSpec packed into an int\n     * @return The width of the view, honoring constraints from measureSpec\n     */\n    private int measureLong(int measureSpec) {\n        int result;\n        int specMode = MeasureSpec.getMode(measureSpec);\n        int specSize = MeasureSpec.getSize(measureSpec);\n\n        if ((specMode == MeasureSpec.EXACTLY) || (mViewPager == null)) {\n            //We were told how big to be\n            result = specSize;\n        } else {\n            //Calculate the width according the views count\n            final int count = mViewPager.getAdapter().getCount();\n            result = (int)(getPaddingLeft() + getPaddingRight()\n                    + (count * 2 * mRadius) + (count - 1) * mRadius + 1);\n            //Respect AT_MOST value if that was what is called for by measureSpec\n            if (specMode == MeasureSpec.AT_MOST) {\n                result = Math.min(result, specSize);\n            }\n        }\n        return result;\n    }\n\n    /**\n     * Determines the height of this view\n     *\n     * @param measureSpec\n     *            A measureSpec packed into an int\n     * @return The height of the view, honoring constraints from measureSpec\n     */\n    private int measureShort(int measureSpec) {\n        int result;\n        int specMode = MeasureSpec.getMode(measureSpec);\n        int specSize = MeasureSpec.getSize(measureSpec);\n\n        if (specMode == MeasureSpec.EXACTLY) {\n            //We were told how big to be\n            result = specSize;\n        } else {\n            //Measure the height\n            result = (int)(2 * mRadius + getPaddingTop() + getPaddingBottom() + 1);\n            //Respect AT_MOST value if that was what is called for by measureSpec\n            if (specMode == MeasureSpec.AT_MOST) {\n                result = Math.min(result, specSize);\n            }\n        }\n        return result;\n    }\n\n    @Override\n    public void onRestoreInstanceState(Parcelable state) {\n        SavedState savedState = (SavedState)state;\n        super.onRestoreInstanceState(savedState.getSuperState());\n        mCurrentPage = savedState.currentPage;\n        mSnapPage = savedState.currentPage;\n        requestLayout();\n    }\n\n    @Override\n    public Parcelable onSaveInstanceState() {\n        Parcelable superState = super.onSaveInstanceState();\n        SavedState savedState = new SavedState(superState);\n        savedState.currentPage = mCurrentPage;\n        return savedState;\n    }\n\n    static class SavedState extends BaseSavedState {\n        int currentPage;\n\n        public SavedState(Parcelable superState) {\n            super(superState);\n        }\n\n        private SavedState(Parcel in) {\n            super(in);\n            currentPage = in.readInt();\n        }\n\n        @Override\n        public void writeToParcel(Parcel dest, int flags) {\n            super.writeToParcel(dest, flags);\n            dest.writeInt(currentPage);\n        }\n\n        @SuppressWarnings(\"UnusedDeclaration\")\n        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {\n            @Override\n            public SavedState createFromParcel(Parcel in) {\n                return new SavedState(in);\n            }\n\n            @Override\n            public SavedState[] newArray(int size) {\n                return new SavedState[size];\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/viewpagerindicator/IconPageIndicator.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n * Copyright (C) 2012 Jake Wharton\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.viewpagerindicator;\n\nimport static android.view.ViewGroup.LayoutParams.FILL_PARENT;\nimport static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;\nimport android.content.Context;\nimport android.support.v4.view.PagerAdapter;\nimport android.support.v4.view.ViewPager;\nimport android.support.v4.view.ViewPager.OnPageChangeListener;\nimport android.util.AttributeSet;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.widget.HorizontalScrollView;\nimport android.widget.ImageView;\n\nimport com.way.yahoo.R;\n\n/**\n * This widget implements the dynamic action bar tab behavior that can change\n * across different configurations or circumstances.\n */\npublic class IconPageIndicator extends HorizontalScrollView implements PageIndicator {\n    private final IcsLinearLayout mIconsLayout;\n\n    private ViewPager mViewPager;\n    private OnPageChangeListener mListener;\n    private Runnable mIconSelector;\n    private int mSelectedIndex;\n\n    public IconPageIndicator(Context context) {\n        this(context, null);\n    }\n\n    public IconPageIndicator(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        setHorizontalScrollBarEnabled(false);\n\n        mIconsLayout = new IcsLinearLayout(context, R.attr.vpiIconPageIndicatorStyle);\n        addView(mIconsLayout, new LayoutParams(WRAP_CONTENT, FILL_PARENT, Gravity.CENTER));\n    }\n\n    private void animateToIcon(final int position) {\n        final View iconView = mIconsLayout.getChildAt(position);\n        if (mIconSelector != null) {\n            removeCallbacks(mIconSelector);\n        }\n        mIconSelector = new Runnable() {\n            public void run() {\n                final int scrollPos = iconView.getLeft() - (getWidth() - iconView.getWidth()) / 2;\n                smoothScrollTo(scrollPos, 0);\n                mIconSelector = null;\n            }\n        };\n        post(mIconSelector);\n    }\n\n    @Override\n    public void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        if (mIconSelector != null) {\n            // Re-post the selector we saved\n            post(mIconSelector);\n        }\n    }\n\n    @Override\n    public void onDetachedFromWindow() {\n        super.onDetachedFromWindow();\n        if (mIconSelector != null) {\n            removeCallbacks(mIconSelector);\n        }\n    }\n\n    @Override\n    public void onPageScrollStateChanged(int arg0) {\n        if (mListener != null) {\n            mListener.onPageScrollStateChanged(arg0);\n        }\n    }\n\n    @Override\n    public void onPageScrolled(int arg0, float arg1, int arg2) {\n        if (mListener != null) {\n            mListener.onPageScrolled(arg0, arg1, arg2);\n        }\n    }\n\n    @Override\n    public void onPageSelected(int arg0) {\n        setCurrentItem(arg0);\n        if (mListener != null) {\n            mListener.onPageSelected(arg0);\n        }\n    }\n\n    @Override\n    public void setViewPager(ViewPager view) {\n        if (mViewPager == view) {\n            return;\n        }\n        if (mViewPager != null) {\n            mViewPager.setOnPageChangeListener(null);\n        }\n        PagerAdapter adapter = view.getAdapter();\n        if (adapter == null) {\n            throw new IllegalStateException(\"ViewPager does not have adapter instance.\");\n        }\n        mViewPager = view;\n        view.setOnPageChangeListener(this);\n        notifyDataSetChanged();\n    }\n\n    public void notifyDataSetChanged() {\n        mIconsLayout.removeAllViews();\n        IconPagerAdapter iconAdapter = (IconPagerAdapter) mViewPager.getAdapter();\n        int count = iconAdapter.getCount();\n        for (int i = 0; i < count; i++) {\n            ImageView view = new ImageView(getContext(), null, R.attr.vpiIconPageIndicatorStyle);\n            view.setImageResource(iconAdapter.getIconResId(i));\n            mIconsLayout.addView(view);\n        }\n        if (mSelectedIndex > count) {\n            mSelectedIndex = count - 1;\n        }\n        setCurrentItem(mSelectedIndex);\n        requestLayout();\n    }\n\n    @Override\n    public void setViewPager(ViewPager view, int initialPosition) {\n        setViewPager(view);\n        setCurrentItem(initialPosition);\n    }\n\n    @Override\n    public void setCurrentItem(int item) {\n        if (mViewPager == null) {\n            throw new IllegalStateException(\"ViewPager has not been bound.\");\n        }\n        mSelectedIndex = item;\n        mViewPager.setCurrentItem(item);\n\n        int tabCount = mIconsLayout.getChildCount();\n        for (int i = 0; i < tabCount; i++) {\n            View child = mIconsLayout.getChildAt(i);\n            boolean isSelected = (i == item);\n            child.setSelected(isSelected);\n            if (isSelected) {\n                animateToIcon(item);\n            }\n        }\n    }\n\n    @Override\n    public void setOnPageChangeListener(OnPageChangeListener listener) {\n        mListener = listener;\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/viewpagerindicator/IconPagerAdapter.java",
    "content": "package com.viewpagerindicator;\n\npublic interface IconPagerAdapter {\n    /**\n     * Get icon representing the page at {@code index} in the adapter.\n     */\n    int getIconResId(int index);\n\n    // From PagerAdapter\n    int getCount();\n}\n"
  },
  {
    "path": "WayHoo/src/com/viewpagerindicator/IcsLinearLayout.java",
    "content": "package com.viewpagerindicator;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.drawable.Drawable;\nimport android.view.View;\nimport android.widget.LinearLayout;\n\n/**\n * A simple extension of a regular linear layout that supports the divider API\n * of Android 4.0+. The dividers are added adjacent to the children by changing\n * their layout params. If you need to rely on the margins which fall in the\n * same orientation as the layout you should wrap the child in a simple\n * {@link android.widget.FrameLayout} so it can receive the margin.\n */\nclass IcsLinearLayout extends LinearLayout {\n    private static final int[] LL = new int[] {\n        /* 0 */ android.R.attr.divider,\n        /* 1 */ android.R.attr.showDividers,\n        /* 2 */ android.R.attr.dividerPadding,\n    };\n    private static final int LL_DIVIDER = 0;\n    private static final int LL_SHOW_DIVIDER = 1;\n    private static final int LL_DIVIDER_PADDING = 2;\n\n    private Drawable mDivider;\n    private int mDividerWidth;\n    private int mDividerHeight;\n    private int mShowDividers;\n    private int mDividerPadding;\n\n\n    public IcsLinearLayout(Context context, int themeAttr) {\n        super(context);\n\n        TypedArray a = context.obtainStyledAttributes(null, LL, themeAttr, 0);\n        setDividerDrawable(a.getDrawable(IcsLinearLayout.LL_DIVIDER));\n        mDividerPadding = a.getDimensionPixelSize(LL_DIVIDER_PADDING, 0);\n        mShowDividers = a.getInteger(LL_SHOW_DIVIDER, SHOW_DIVIDER_NONE);\n        a.recycle();\n    }\n\n    public void setDividerDrawable(Drawable divider) {\n        if (divider == mDivider) {\n            return;\n        }\n        mDivider = divider;\n        if (divider != null) {\n            mDividerWidth = divider.getIntrinsicWidth();\n            mDividerHeight = divider.getIntrinsicHeight();\n        } else {\n            mDividerWidth = 0;\n            mDividerHeight = 0;\n        }\n        setWillNotDraw(divider == null);\n        requestLayout();\n    }\n\n    @Override\n    protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {\n        final int index = indexOfChild(child);\n        final int orientation = getOrientation();\n        final LayoutParams params = (LayoutParams) child.getLayoutParams();\n        if (hasDividerBeforeChildAt(index)) {\n            if (orientation == VERTICAL) {\n                //Account for the divider by pushing everything up\n                params.topMargin = mDividerHeight;\n            } else {\n                //Account for the divider by pushing everything left\n                params.leftMargin = mDividerWidth;\n            }\n        }\n\n        final int count = getChildCount();\n        if (index == count - 1) {\n            if (hasDividerBeforeChildAt(count)) {\n                if (orientation == VERTICAL) {\n                    params.bottomMargin = mDividerHeight;\n                } else {\n                    params.rightMargin = mDividerWidth;\n                }\n            }\n        }\n        super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        if (mDivider != null) {\n            if (getOrientation() == VERTICAL) {\n                drawDividersVertical(canvas);\n            } else {\n                drawDividersHorizontal(canvas);\n            }\n        }\n        super.onDraw(canvas);\n    }\n\n    private void drawDividersVertical(Canvas canvas) {\n        final int count = getChildCount();\n        for (int i = 0; i < count; i++) {\n            final View child = getChildAt(i);\n\n            if (child != null && child.getVisibility() != GONE) {\n                if (hasDividerBeforeChildAt(i)) {\n                    final android.widget.LinearLayout.LayoutParams lp = (android.widget.LinearLayout.LayoutParams) child.getLayoutParams();\n                    final int top = child.getTop() - lp.topMargin/* - mDividerHeight*/;\n                    drawHorizontalDivider(canvas, top);\n                }\n            }\n        }\n\n        if (hasDividerBeforeChildAt(count)) {\n            final View child = getChildAt(count - 1);\n            int bottom = 0;\n            if (child == null) {\n                bottom = getHeight() - getPaddingBottom() - mDividerHeight;\n            } else {\n                //final LayoutParams lp = (LayoutParams) child.getLayoutParams();\n                bottom = child.getBottom()/* + lp.bottomMargin*/;\n            }\n            drawHorizontalDivider(canvas, bottom);\n        }\n    }\n\n    private void drawDividersHorizontal(Canvas canvas) {\n        final int count = getChildCount();\n        for (int i = 0; i < count; i++) {\n            final View child = getChildAt(i);\n\n            if (child != null && child.getVisibility() != GONE) {\n                if (hasDividerBeforeChildAt(i)) {\n                    final android.widget.LinearLayout.LayoutParams lp = (android.widget.LinearLayout.LayoutParams) child.getLayoutParams();\n                    final int left = child.getLeft() - lp.leftMargin/* - mDividerWidth*/;\n                    drawVerticalDivider(canvas, left);\n                }\n            }\n        }\n\n        if (hasDividerBeforeChildAt(count)) {\n            final View child = getChildAt(count - 1);\n            int right = 0;\n            if (child == null) {\n                right = getWidth() - getPaddingRight() - mDividerWidth;\n            } else {\n                //final LayoutParams lp = (LayoutParams) child.getLayoutParams();\n                right = child.getRight()/* + lp.rightMargin*/;\n            }\n            drawVerticalDivider(canvas, right);\n        }\n    }\n\n    private void drawHorizontalDivider(Canvas canvas, int top) {\n        mDivider.setBounds(getPaddingLeft() + mDividerPadding, top,\n                getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight);\n        mDivider.draw(canvas);\n    }\n\n    private void drawVerticalDivider(Canvas canvas, int left) {\n        mDivider.setBounds(left, getPaddingTop() + mDividerPadding,\n                left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding);\n        mDivider.draw(canvas);\n    }\n\n    private boolean hasDividerBeforeChildAt(int childIndex) {\n        if (childIndex == 0 || childIndex == getChildCount()) {\n          return false;\n        }\n        if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) {\n            boolean hasVisibleViewBefore = false;\n            for (int i = childIndex - 1; i >= 0; i--) {\n                if (getChildAt(i).getVisibility() != GONE) {\n                    hasVisibleViewBefore = true;\n                    break;\n                }\n            }\n            return hasVisibleViewBefore;\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/viewpagerindicator/LinePageIndicator.java",
    "content": "/*\n * Copyright (C) 2012 Jake Wharton\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.viewpagerindicator;\n\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.drawable.Drawable;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.support.v4.view.MotionEventCompat;\nimport android.support.v4.view.ViewConfigurationCompat;\nimport android.support.v4.view.ViewPager;\nimport android.util.AttributeSet;\nimport android.util.FloatMath;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewConfiguration;\n\nimport com.way.yahoo.R;\n\n/**\n * Draws a line for each page. The current page line is colored differently\n * than the unselected page lines.\n */\npublic class LinePageIndicator extends View implements PageIndicator {\n    private static final int INVALID_POINTER = -1;\n\n    private final Paint mPaintUnselected = new Paint(Paint.ANTI_ALIAS_FLAG);\n    private final Paint mPaintSelected = new Paint(Paint.ANTI_ALIAS_FLAG);\n    private ViewPager mViewPager;\n    private ViewPager.OnPageChangeListener mListener;\n    private int mCurrentPage;\n    private boolean mCentered;\n    private float mLineWidth;\n    private float mGapWidth;\n\n    private int mTouchSlop;\n    private float mLastMotionX = -1;\n    private int mActivePointerId = INVALID_POINTER;\n    private boolean mIsDragging;\n\n\n    public LinePageIndicator(Context context) {\n        this(context, null);\n    }\n\n    public LinePageIndicator(Context context, AttributeSet attrs) {\n        this(context, attrs, R.attr.vpiLinePageIndicatorStyle);\n    }\n\n    public LinePageIndicator(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        if (isInEditMode()) return;\n\n        final Resources res = getResources();\n\n        //Load defaults from resources\n        final int defaultSelectedColor = res.getColor(R.color.default_line_indicator_selected_color);\n        final int defaultUnselectedColor = res.getColor(R.color.default_line_indicator_unselected_color);\n        final float defaultLineWidth = res.getDimension(R.dimen.default_line_indicator_line_width);\n        final float defaultGapWidth = res.getDimension(R.dimen.default_line_indicator_gap_width);\n        final float defaultStrokeWidth = res.getDimension(R.dimen.default_line_indicator_stroke_width);\n        final boolean defaultCentered = res.getBoolean(R.bool.default_line_indicator_centered);\n\n        //Retrieve styles attributes\n        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LinePageIndicator, defStyle, 0);\n\n        mCentered = a.getBoolean(R.styleable.LinePageIndicator_centered, defaultCentered);\n        mLineWidth = a.getDimension(R.styleable.LinePageIndicator_lineWidth, defaultLineWidth);\n        mGapWidth = a.getDimension(R.styleable.LinePageIndicator_gapWidth, defaultGapWidth);\n        setStrokeWidth(a.getDimension(R.styleable.LinePageIndicator_strokeWidth, defaultStrokeWidth));\n        mPaintUnselected.setColor(a.getColor(R.styleable.LinePageIndicator_unselectedColor, defaultUnselectedColor));\n        mPaintSelected.setColor(a.getColor(R.styleable.LinePageIndicator_selectedColor, defaultSelectedColor));\n\n        Drawable background = a.getDrawable(R.styleable.LinePageIndicator_android_background);\n        if (background != null) {\n          setBackgroundDrawable(background);\n        }\n\n        a.recycle();\n\n        final ViewConfiguration configuration = ViewConfiguration.get(context);\n        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);\n    }\n\n\n    public void setCentered(boolean centered) {\n        mCentered = centered;\n        invalidate();\n    }\n\n    public boolean isCentered() {\n        return mCentered;\n    }\n\n    public void setUnselectedColor(int unselectedColor) {\n        mPaintUnselected.setColor(unselectedColor);\n        invalidate();\n    }\n\n    public int getUnselectedColor() {\n        return mPaintUnselected.getColor();\n    }\n\n    public void setSelectedColor(int selectedColor) {\n        mPaintSelected.setColor(selectedColor);\n        invalidate();\n    }\n\n    public int getSelectedColor() {\n        return mPaintSelected.getColor();\n    }\n\n    public void setLineWidth(float lineWidth) {\n        mLineWidth = lineWidth;\n        invalidate();\n    }\n\n    public float getLineWidth() {\n        return mLineWidth;\n    }\n\n    public void setStrokeWidth(float lineHeight) {\n        mPaintSelected.setStrokeWidth(lineHeight);\n        mPaintUnselected.setStrokeWidth(lineHeight);\n        invalidate();\n    }\n\n    public float getStrokeWidth() {\n        return mPaintSelected.getStrokeWidth();\n    }\n\n    public void setGapWidth(float gapWidth) {\n        mGapWidth = gapWidth;\n        invalidate();\n    }\n\n    public float getGapWidth() {\n        return mGapWidth;\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n\n        if (mViewPager == null) {\n            return;\n        }\n        final int count = mViewPager.getAdapter().getCount();\n        if (count == 0) {\n            return;\n        }\n\n        if (mCurrentPage >= count) {\n            setCurrentItem(count - 1);\n            return;\n        }\n\n        final float lineWidthAndGap = mLineWidth + mGapWidth;\n        final float indicatorWidth = (count * lineWidthAndGap) - mGapWidth;\n        final float paddingTop = getPaddingTop();\n        final float paddingLeft = getPaddingLeft();\n        final float paddingRight = getPaddingRight();\n\n        float verticalOffset = paddingTop + ((getHeight() - paddingTop - getPaddingBottom()) / 2.0f);\n        float horizontalOffset = paddingLeft;\n        if (mCentered) {\n            horizontalOffset += ((getWidth() - paddingLeft - paddingRight) / 2.0f) - (indicatorWidth / 2.0f);\n        }\n\n        //Draw stroked circles\n        for (int i = 0; i < count; i++) {\n            float dx1 = horizontalOffset + (i * lineWidthAndGap);\n            float dx2 = dx1 + mLineWidth;\n            canvas.drawLine(dx1, verticalOffset, dx2, verticalOffset, (i == mCurrentPage) ? mPaintSelected : mPaintUnselected);\n        }\n    }\n\n    public boolean onTouchEvent(android.view.MotionEvent ev) {\n        if (super.onTouchEvent(ev)) {\n            return true;\n        }\n        if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) {\n            return false;\n        }\n\n        final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;\n        switch (action) {\n            case MotionEvent.ACTION_DOWN:\n                mActivePointerId = MotionEventCompat.getPointerId(ev, 0);\n                mLastMotionX = ev.getX();\n                break;\n\n            case MotionEvent.ACTION_MOVE: {\n                final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);\n                final float x = MotionEventCompat.getX(ev, activePointerIndex);\n                final float deltaX = x - mLastMotionX;\n\n                if (!mIsDragging) {\n                    if (Math.abs(deltaX) > mTouchSlop) {\n                        mIsDragging = true;\n                    }\n                }\n\n                if (mIsDragging) {\n                    mLastMotionX = x;\n                    if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) {\n                        mViewPager.fakeDragBy(deltaX);\n                    }\n                }\n\n                break;\n            }\n\n            case MotionEvent.ACTION_CANCEL:\n            case MotionEvent.ACTION_UP:\n                if (!mIsDragging) {\n                    final int count = mViewPager.getAdapter().getCount();\n                    final int width = getWidth();\n                    final float halfWidth = width / 2f;\n                    final float sixthWidth = width / 6f;\n\n                    if ((mCurrentPage > 0) && (ev.getX() < halfWidth - sixthWidth)) {\n                        if (action != MotionEvent.ACTION_CANCEL) {\n                            mViewPager.setCurrentItem(mCurrentPage - 1);\n                        }\n                        return true;\n                    } else if ((mCurrentPage < count - 1) && (ev.getX() > halfWidth + sixthWidth)) {\n                        if (action != MotionEvent.ACTION_CANCEL) {\n                            mViewPager.setCurrentItem(mCurrentPage + 1);\n                        }\n                        return true;\n                    }\n                }\n\n                mIsDragging = false;\n                mActivePointerId = INVALID_POINTER;\n                if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag();\n                break;\n\n            case MotionEventCompat.ACTION_POINTER_DOWN: {\n                final int index = MotionEventCompat.getActionIndex(ev);\n                mLastMotionX = MotionEventCompat.getX(ev, index);\n                mActivePointerId = MotionEventCompat.getPointerId(ev, index);\n                break;\n            }\n\n            case MotionEventCompat.ACTION_POINTER_UP:\n                final int pointerIndex = MotionEventCompat.getActionIndex(ev);\n                final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);\n                if (pointerId == mActivePointerId) {\n                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;\n                    mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);\n                }\n                mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId));\n                break;\n        }\n\n        return true;\n    }\n\n    @Override\n    public void setViewPager(ViewPager viewPager) {\n        if (mViewPager == viewPager) {\n            return;\n        }\n        if (mViewPager != null) {\n            //Clear us from the old pager.\n            mViewPager.setOnPageChangeListener(null);\n        }\n        if (viewPager.getAdapter() == null) {\n            throw new IllegalStateException(\"ViewPager does not have adapter instance.\");\n        }\n        mViewPager = viewPager;\n        mViewPager.setOnPageChangeListener(this);\n        invalidate();\n    }\n\n    @Override\n    public void setViewPager(ViewPager view, int initialPosition) {\n        setViewPager(view);\n        setCurrentItem(initialPosition);\n    }\n\n    @Override\n    public void setCurrentItem(int item) {\n        if (mViewPager == null) {\n            throw new IllegalStateException(\"ViewPager has not been bound.\");\n        }\n        mViewPager.setCurrentItem(item);\n        mCurrentPage = item;\n        invalidate();\n    }\n\n    @Override\n    public void notifyDataSetChanged() {\n        invalidate();\n    }\n\n    @Override\n    public void onPageScrollStateChanged(int state) {\n        if (mListener != null) {\n            mListener.onPageScrollStateChanged(state);\n        }\n    }\n\n    @Override\n    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {\n        if (mListener != null) {\n            mListener.onPageScrolled(position, positionOffset, positionOffsetPixels);\n        }\n    }\n\n    @Override\n    public void onPageSelected(int position) {\n        mCurrentPage = position;\n        invalidate();\n\n        if (mListener != null) {\n            mListener.onPageSelected(position);\n        }\n    }\n\n    @Override\n    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {\n        mListener = listener;\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));\n    }\n\n    /**\n     * Determines the width of this view\n     *\n     * @param measureSpec\n     *            A measureSpec packed into an int\n     * @return The width of the view, honoring constraints from measureSpec\n     */\n    private int measureWidth(int measureSpec) {\n        float result;\n        int specMode = MeasureSpec.getMode(measureSpec);\n        int specSize = MeasureSpec.getSize(measureSpec);\n\n        if ((specMode == MeasureSpec.EXACTLY) || (mViewPager == null)) {\n            //We were told how big to be\n            result = specSize;\n        } else {\n            //Calculate the width according the views count\n            final int count = mViewPager.getAdapter().getCount();\n            result = getPaddingLeft() + getPaddingRight() + (count * mLineWidth) + ((count - 1) * mGapWidth);\n            //Respect AT_MOST value if that was what is called for by measureSpec\n            if (specMode == MeasureSpec.AT_MOST) {\n                result = Math.min(result, specSize);\n            }\n        }\n        return (int)FloatMath.ceil(result);\n    }\n\n    /**\n     * Determines the height of this view\n     *\n     * @param measureSpec\n     *            A measureSpec packed into an int\n     * @return The height of the view, honoring constraints from measureSpec\n     */\n    private int measureHeight(int measureSpec) {\n        float result;\n        int specMode = MeasureSpec.getMode(measureSpec);\n        int specSize = MeasureSpec.getSize(measureSpec);\n\n        if (specMode == MeasureSpec.EXACTLY) {\n            //We were told how big to be\n            result = specSize;\n        } else {\n            //Measure the height\n            result = mPaintSelected.getStrokeWidth() + getPaddingTop() + getPaddingBottom();\n            //Respect AT_MOST value if that was what is called for by measureSpec\n            if (specMode == MeasureSpec.AT_MOST) {\n                result = Math.min(result, specSize);\n            }\n        }\n        return (int)FloatMath.ceil(result);\n    }\n\n    @Override\n    public void onRestoreInstanceState(Parcelable state) {\n        SavedState savedState = (SavedState)state;\n        super.onRestoreInstanceState(savedState.getSuperState());\n        mCurrentPage = savedState.currentPage;\n        requestLayout();\n    }\n\n    @Override\n    public Parcelable onSaveInstanceState() {\n        Parcelable superState = super.onSaveInstanceState();\n        SavedState savedState = new SavedState(superState);\n        savedState.currentPage = mCurrentPage;\n        return savedState;\n    }\n\n    static class SavedState extends BaseSavedState {\n        int currentPage;\n\n        public SavedState(Parcelable superState) {\n            super(superState);\n        }\n\n        private SavedState(Parcel in) {\n            super(in);\n            currentPage = in.readInt();\n        }\n\n        @Override\n        public void writeToParcel(Parcel dest, int flags) {\n            super.writeToParcel(dest, flags);\n            dest.writeInt(currentPage);\n        }\n\n        @SuppressWarnings(\"UnusedDeclaration\")\n        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {\n            @Override\n            public SavedState createFromParcel(Parcel in) {\n                return new SavedState(in);\n            }\n\n            @Override\n            public SavedState[] newArray(int size) {\n                return new SavedState[size];\n            }\n        };\n    }\n}"
  },
  {
    "path": "WayHoo/src/com/viewpagerindicator/PageIndicator.java",
    "content": "/*\n * Copyright (C) 2011 Patrik Akerfeldt\n * Copyright (C) 2011 Jake Wharton\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.viewpagerindicator;\n\nimport android.support.v4.view.ViewPager;\n\n/**\n * A PageIndicator is responsible to show an visual indicator on the total views\n * number and the current visible view.\n */\npublic interface PageIndicator extends ViewPager.OnPageChangeListener {\n    /**\n     * Bind the indicator to a ViewPager.\n     *\n     * @param view\n     */\n    void setViewPager(ViewPager view);\n\n    /**\n     * Bind the indicator to a ViewPager.\n     *\n     * @param view\n     * @param initialPosition\n     */\n    void setViewPager(ViewPager view, int initialPosition);\n\n    /**\n     * <p>Set the current page of both the ViewPager and indicator.</p>\n     *\n     * <p>This <strong>must</strong> be used if you need to set the page before\n     * the views are drawn on screen (e.g., default start page).</p>\n     *\n     * @param item\n     */\n    void setCurrentItem(int item);\n\n    /**\n     * Set a page change listener which will receive forwarded events.\n     *\n     * @param listener\n     */\n    void setOnPageChangeListener(ViewPager.OnPageChangeListener listener);\n\n    /**\n     * Notify the indicator that the fragment list has changed.\n     */\n    void notifyDataSetChanged();\n}\n"
  },
  {
    "path": "WayHoo/src/com/viewpagerindicator/TabPageIndicator.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n * Copyright (C) 2011 Jake Wharton\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.viewpagerindicator;\n\nimport static android.view.ViewGroup.LayoutParams.MATCH_PARENT;\nimport static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;\nimport android.content.Context;\nimport android.support.v4.view.PagerAdapter;\nimport android.support.v4.view.ViewPager;\nimport android.support.v4.view.ViewPager.OnPageChangeListener;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.HorizontalScrollView;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\n\nimport com.way.yahoo.R;\n\n/**\n * This widget implements the dynamic action bar tab behavior that can change\n * across different configurations or circumstances.\n */\npublic class TabPageIndicator extends HorizontalScrollView implements PageIndicator {\n    /** Title text used when no title is provided by the adapter. */\n    private static final CharSequence EMPTY_TITLE = \"\";\n\n    /**\n     * Interface for a callback when the selected tab has been reselected.\n     */\n    public interface OnTabReselectedListener {\n        /**\n         * Callback when the selected tab has been reselected.\n         *\n         * @param position Position of the current center item.\n         */\n        void onTabReselected(int position);\n    }\n\n    private Runnable mTabSelector;\n\n    private final OnClickListener mTabClickListener = new OnClickListener() {\n        public void onClick(View view) {\n            TabView tabView = (TabView)view;\n            final int oldSelected = mViewPager.getCurrentItem();\n            final int newSelected = tabView.getIndex();\n            mViewPager.setCurrentItem(newSelected);\n            if (oldSelected == newSelected && mTabReselectedListener != null) {\n                mTabReselectedListener.onTabReselected(newSelected);\n            }\n        }\n    };\n\n    private final IcsLinearLayout mTabLayout;\n\n    private ViewPager mViewPager;\n    private ViewPager.OnPageChangeListener mListener;\n\n    private int mMaxTabWidth;\n    private int mSelectedTabIndex;\n\n    private OnTabReselectedListener mTabReselectedListener;\n\n    public TabPageIndicator(Context context) {\n        this(context, null);\n    }\n\n    public TabPageIndicator(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        setHorizontalScrollBarEnabled(false);\n\n        mTabLayout = new IcsLinearLayout(context, R.attr.vpiTabPageIndicatorStyle);\n        addView(mTabLayout, new ViewGroup.LayoutParams(WRAP_CONTENT, MATCH_PARENT));\n    }\n\n    public void setOnTabReselectedListener(OnTabReselectedListener listener) {\n        mTabReselectedListener = listener;\n    }\n\n    @Override\n    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);\n        final boolean lockedExpanded = widthMode == MeasureSpec.EXACTLY;\n        setFillViewport(lockedExpanded);\n\n        final int childCount = mTabLayout.getChildCount();\n        if (childCount > 1 && (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) {\n            if (childCount > 2) {\n                mMaxTabWidth = (int)(MeasureSpec.getSize(widthMeasureSpec) * 0.4f);\n            } else {\n                mMaxTabWidth = MeasureSpec.getSize(widthMeasureSpec) / 2;\n            }\n        } else {\n            mMaxTabWidth = -1;\n        }\n\n        final int oldWidth = getMeasuredWidth();\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        final int newWidth = getMeasuredWidth();\n\n        if (lockedExpanded && oldWidth != newWidth) {\n            // Recenter the tab display if we're at a new (scrollable) size.\n            setCurrentItem(mSelectedTabIndex);\n        }\n    }\n\n    private void animateToTab(final int position) {\n        final View tabView = mTabLayout.getChildAt(position);\n        if (mTabSelector != null) {\n            removeCallbacks(mTabSelector);\n        }\n        mTabSelector = new Runnable() {\n            public void run() {\n                final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2;\n                smoothScrollTo(scrollPos, 0);\n                mTabSelector = null;\n            }\n        };\n        post(mTabSelector);\n    }\n\n    @Override\n    public void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        if (mTabSelector != null) {\n            // Re-post the selector we saved\n            post(mTabSelector);\n        }\n    }\n\n    @Override\n    public void onDetachedFromWindow() {\n        super.onDetachedFromWindow();\n        if (mTabSelector != null) {\n            removeCallbacks(mTabSelector);\n        }\n    }\n\n    private void addTab(int index, CharSequence text, int iconResId) {\n        final TabView tabView = new TabView(getContext());\n        tabView.mIndex = index;\n        tabView.setFocusable(true);\n        tabView.setOnClickListener(mTabClickListener);\n        tabView.setText(text);\n\n        if (iconResId != 0) {\n            tabView.setCompoundDrawablesWithIntrinsicBounds(iconResId, 0, 0, 0);\n        }\n\n        mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0, MATCH_PARENT, 1));\n    }\n\n    @Override\n    public void onPageScrollStateChanged(int arg0) {\n        if (mListener != null) {\n            mListener.onPageScrollStateChanged(arg0);\n        }\n    }\n\n    @Override\n    public void onPageScrolled(int arg0, float arg1, int arg2) {\n        if (mListener != null) {\n            mListener.onPageScrolled(arg0, arg1, arg2);\n        }\n    }\n\n    @Override\n    public void onPageSelected(int arg0) {\n        setCurrentItem(arg0);\n        if (mListener != null) {\n            mListener.onPageSelected(arg0);\n        }\n    }\n\n    @Override\n    public void setViewPager(ViewPager view) {\n        if (mViewPager == view) {\n            return;\n        }\n        if (mViewPager != null) {\n            mViewPager.setOnPageChangeListener(null);\n        }\n        final PagerAdapter adapter = view.getAdapter();\n        if (adapter == null) {\n            throw new IllegalStateException(\"ViewPager does not have adapter instance.\");\n        }\n        mViewPager = view;\n        view.setOnPageChangeListener(this);\n        notifyDataSetChanged();\n    }\n\n    public void notifyDataSetChanged() {\n        mTabLayout.removeAllViews();\n        PagerAdapter adapter = mViewPager.getAdapter();\n        IconPagerAdapter iconAdapter = null;\n        if (adapter instanceof IconPagerAdapter) {\n            iconAdapter = (IconPagerAdapter)adapter;\n        }\n        final int count = adapter.getCount();\n        for (int i = 0; i < count; i++) {\n            CharSequence title = adapter.getPageTitle(i);\n            if (title == null) {\n                title = EMPTY_TITLE;\n            }\n            int iconResId = 0;\n            if (iconAdapter != null) {\n                iconResId = iconAdapter.getIconResId(i);\n            }\n            addTab(i, title, iconResId);\n        }\n        if (mSelectedTabIndex > count) {\n            mSelectedTabIndex = count - 1;\n        }\n        setCurrentItem(mSelectedTabIndex);\n        requestLayout();\n    }\n\n    @Override\n    public void setViewPager(ViewPager view, int initialPosition) {\n        setViewPager(view);\n        setCurrentItem(initialPosition);\n    }\n\n    @Override\n    public void setCurrentItem(int item) {\n        if (mViewPager == null) {\n            throw new IllegalStateException(\"ViewPager has not been bound.\");\n        }\n        mSelectedTabIndex = item;\n        mViewPager.setCurrentItem(item);\n\n        final int tabCount = mTabLayout.getChildCount();\n        for (int i = 0; i < tabCount; i++) {\n            final View child = mTabLayout.getChildAt(i);\n            final boolean isSelected = (i == item);\n            child.setSelected(isSelected);\n            if (isSelected) {\n                animateToTab(item);\n            }\n        }\n    }\n\n    @Override\n    public void setOnPageChangeListener(OnPageChangeListener listener) {\n        mListener = listener;\n    }\n\n    private class TabView extends TextView {\n        private int mIndex;\n\n        public TabView(Context context) {\n            super(context, null, R.attr.vpiTabPageIndicatorStyle);\n        }\n\n        @Override\n        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n            super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n\n            // Re-measure if we went beyond our maximum size.\n            if (mMaxTabWidth > 0 && getMeasuredWidth() > mMaxTabWidth) {\n                super.onMeasure(MeasureSpec.makeMeasureSpec(mMaxTabWidth, MeasureSpec.EXACTLY),\n                        heightMeasureSpec);\n            }\n        }\n\n        public int getIndex() {\n            return mIndex;\n        }\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/viewpagerindicator/TitlePageIndicator.java",
    "content": "/*\n * Copyright (C) 2011 Jake Wharton\n * Copyright (C) 2011 Patrik Akerfeldt\n * Copyright (C) 2011 Francisco Figueiredo Jr.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.viewpagerindicator;\n\nimport java.util.ArrayList;\n\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.Path;\nimport android.graphics.Rect;\nimport android.graphics.Typeface;\nimport android.graphics.drawable.Drawable;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.support.v4.view.MotionEventCompat;\nimport android.support.v4.view.ViewConfigurationCompat;\nimport android.support.v4.view.ViewPager;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewConfiguration;\n\nimport com.way.yahoo.R;\n\n/**\n * A TitlePageIndicator is a PageIndicator which displays the title of left view\n * (if exist), the title of the current select view (centered) and the title of\n * the right view (if exist). When the user scrolls the ViewPager then titles are\n * also scrolled.\n */\npublic class TitlePageIndicator extends View implements PageIndicator {\n    /**\n     * Percentage indicating what percentage of the screen width away from\n     * center should the underline be fully faded. A value of 0.25 means that\n     * halfway between the center of the screen and an edge.\n     */\n    private static final float SELECTION_FADE_PERCENTAGE = 0.25f;\n\n    /**\n     * Percentage indicating what percentage of the screen width away from\n     * center should the selected text bold turn off. A value of 0.05 means\n     * that 10% between the center and an edge.\n     */\n    private static final float BOLD_FADE_PERCENTAGE = 0.05f;\n\n    /**\n     * Title text used when no title is provided by the adapter.\n     */\n    private static final String EMPTY_TITLE = \"\";\n\n    /**\n     * Interface for a callback when the center item has been clicked.\n     */\n    public interface OnCenterItemClickListener {\n        /**\n         * Callback when the center item has been clicked.\n         *\n         * @param position Position of the current center item.\n         */\n        void onCenterItemClick(int position);\n    }\n\n    public enum IndicatorStyle {\n        None(0), Triangle(1), Underline(2);\n\n        public final int value;\n\n        private IndicatorStyle(int value) {\n            this.value = value;\n        }\n\n        public static IndicatorStyle fromValue(int value) {\n            for (IndicatorStyle style : IndicatorStyle.values()) {\n                if (style.value == value) {\n                    return style;\n                }\n            }\n            return null;\n        }\n    }\n\n    public enum LinePosition {\n        Bottom(0), Top(1);\n\n        public final int value;\n\n        private LinePosition(int value) {\n            this.value = value;\n        }\n\n        public static LinePosition fromValue(int value) {\n            for (LinePosition position : LinePosition.values()) {\n                if (position.value == value) {\n                    return position;\n                }\n            }\n            return null;\n        }\n    }\n\n    private ViewPager mViewPager;\n    private ViewPager.OnPageChangeListener mListener;\n    private int mCurrentPage = -1;\n    private float mPageOffset;\n    private int mScrollState;\n    private final Paint mPaintText = new Paint();\n    private boolean mBoldText;\n    private int mColorText;\n    private int mColorSelected;\n    private Path mPath = new Path();\n    private final Rect mBounds = new Rect();\n    private final Paint mPaintFooterLine = new Paint();\n    private IndicatorStyle mFooterIndicatorStyle;\n    private LinePosition mLinePosition;\n    private final Paint mPaintFooterIndicator = new Paint();\n    private float mFooterIndicatorHeight;\n    private float mFooterIndicatorUnderlinePadding;\n    private float mFooterPadding;\n    private float mTitlePadding;\n    private float mTopPadding;\n    /** Left and right side padding for not active view titles. */\n    private float mClipPadding;\n    private float mFooterLineHeight;\n\n    private static final int INVALID_POINTER = -1;\n\n    private int mTouchSlop;\n    private float mLastMotionX = -1;\n    private int mActivePointerId = INVALID_POINTER;\n    private boolean mIsDragging;\n\n    private OnCenterItemClickListener mCenterItemClickListener;\n\n\n    public TitlePageIndicator(Context context) {\n        this(context, null);\n    }\n\n    public TitlePageIndicator(Context context, AttributeSet attrs) {\n        this(context, attrs, R.attr.vpiTitlePageIndicatorStyle);\n    }\n\n    public TitlePageIndicator(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        if (isInEditMode()) return;\n\n        //Load defaults from resources\n        final Resources res = getResources();\n        final int defaultFooterColor = res.getColor(R.color.default_title_indicator_footer_color);\n        final float defaultFooterLineHeight = res.getDimension(R.dimen.default_title_indicator_footer_line_height);\n        final int defaultFooterIndicatorStyle = res.getInteger(R.integer.default_title_indicator_footer_indicator_style);\n        final float defaultFooterIndicatorHeight = res.getDimension(R.dimen.default_title_indicator_footer_indicator_height);\n        final float defaultFooterIndicatorUnderlinePadding = res.getDimension(R.dimen.default_title_indicator_footer_indicator_underline_padding);\n        final float defaultFooterPadding = res.getDimension(R.dimen.default_title_indicator_footer_padding);\n        final int defaultLinePosition = res.getInteger(R.integer.default_title_indicator_line_position);\n        final int defaultSelectedColor = res.getColor(R.color.default_title_indicator_selected_color);\n        final boolean defaultSelectedBold = res.getBoolean(R.bool.default_title_indicator_selected_bold);\n        final int defaultTextColor = res.getColor(R.color.default_title_indicator_text_color);\n        final float defaultTextSize = res.getDimension(R.dimen.default_title_indicator_text_size);\n        final float defaultTitlePadding = res.getDimension(R.dimen.default_title_indicator_title_padding);\n        final float defaultClipPadding = res.getDimension(R.dimen.default_title_indicator_clip_padding);\n        final float defaultTopPadding = res.getDimension(R.dimen.default_title_indicator_top_padding);\n\n        //Retrieve styles attributes\n        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TitlePageIndicator, defStyle, 0);\n\n        //Retrieve the colors to be used for this view and apply them.\n        mFooterLineHeight = a.getDimension(R.styleable.TitlePageIndicator_footerLineHeight, defaultFooterLineHeight);\n        mFooterIndicatorStyle = IndicatorStyle.fromValue(a.getInteger(R.styleable.TitlePageIndicator_footerIndicatorStyle, defaultFooterIndicatorStyle));\n        mFooterIndicatorHeight = a.getDimension(R.styleable.TitlePageIndicator_footerIndicatorHeight, defaultFooterIndicatorHeight);\n        mFooterIndicatorUnderlinePadding = a.getDimension(R.styleable.TitlePageIndicator_footerIndicatorUnderlinePadding, defaultFooterIndicatorUnderlinePadding);\n        mFooterPadding = a.getDimension(R.styleable.TitlePageIndicator_footerPadding, defaultFooterPadding);\n        mLinePosition = LinePosition.fromValue(a.getInteger(R.styleable.TitlePageIndicator_linePosition, defaultLinePosition));\n        mTopPadding = a.getDimension(R.styleable.TitlePageIndicator_topPadding, defaultTopPadding);\n        mTitlePadding = a.getDimension(R.styleable.TitlePageIndicator_titlePadding, defaultTitlePadding);\n        mClipPadding = a.getDimension(R.styleable.TitlePageIndicator_clipPadding, defaultClipPadding);\n        mColorSelected = a.getColor(R.styleable.TitlePageIndicator_selectedColor, defaultSelectedColor);\n        mColorText = a.getColor(R.styleable.TitlePageIndicator_android_textColor, defaultTextColor);\n        mBoldText = a.getBoolean(R.styleable.TitlePageIndicator_selectedBold, defaultSelectedBold);\n\n        final float textSize = a.getDimension(R.styleable.TitlePageIndicator_android_textSize, defaultTextSize);\n        final int footerColor = a.getColor(R.styleable.TitlePageIndicator_footerColor, defaultFooterColor);\n        mPaintText.setTextSize(textSize);\n        mPaintText.setAntiAlias(true);\n        mPaintFooterLine.setStyle(Paint.Style.FILL_AND_STROKE);\n        mPaintFooterLine.setStrokeWidth(mFooterLineHeight);\n        mPaintFooterLine.setColor(footerColor);\n        mPaintFooterIndicator.setStyle(Paint.Style.FILL_AND_STROKE);\n        mPaintFooterIndicator.setColor(footerColor);\n\n        Drawable background = a.getDrawable(R.styleable.TitlePageIndicator_android_background);\n        if (background != null) {\n          setBackgroundDrawable(background);\n        }\n\n        a.recycle();\n\n        final ViewConfiguration configuration = ViewConfiguration.get(context);\n        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);\n    }\n\n\n    public int getFooterColor() {\n        return mPaintFooterLine.getColor();\n    }\n\n    public void setFooterColor(int footerColor) {\n        mPaintFooterLine.setColor(footerColor);\n        mPaintFooterIndicator.setColor(footerColor);\n        invalidate();\n    }\n\n    public float getFooterLineHeight() {\n        return mFooterLineHeight;\n    }\n\n    public void setFooterLineHeight(float footerLineHeight) {\n        mFooterLineHeight = footerLineHeight;\n        mPaintFooterLine.setStrokeWidth(mFooterLineHeight);\n        invalidate();\n    }\n\n    public float getFooterIndicatorHeight() {\n        return mFooterIndicatorHeight;\n    }\n\n    public void setFooterIndicatorHeight(float footerTriangleHeight) {\n        mFooterIndicatorHeight = footerTriangleHeight;\n        invalidate();\n    }\n\n    public float getFooterIndicatorPadding() {\n        return mFooterPadding;\n    }\n\n    public void setFooterIndicatorPadding(float footerIndicatorPadding) {\n        mFooterPadding = footerIndicatorPadding;\n        invalidate();\n    }\n\n    public IndicatorStyle getFooterIndicatorStyle() {\n        return mFooterIndicatorStyle;\n    }\n\n    public void setFooterIndicatorStyle(IndicatorStyle indicatorStyle) {\n        mFooterIndicatorStyle = indicatorStyle;\n        invalidate();\n    }\n\n    public LinePosition getLinePosition() {\n        return mLinePosition;\n    }\n\n    public void setLinePosition(LinePosition linePosition) {\n        mLinePosition = linePosition;\n        invalidate();\n    }\n\n    public int getSelectedColor() {\n        return mColorSelected;\n    }\n\n    public void setSelectedColor(int selectedColor) {\n        mColorSelected = selectedColor;\n        invalidate();\n    }\n\n    public boolean isSelectedBold() {\n        return mBoldText;\n    }\n\n    public void setSelectedBold(boolean selectedBold) {\n        mBoldText = selectedBold;\n        invalidate();\n    }\n\n    public int getTextColor() {\n        return mColorText;\n    }\n\n    public void setTextColor(int textColor) {\n        mPaintText.setColor(textColor);\n        mColorText = textColor;\n        invalidate();\n    }\n\n    public float getTextSize() {\n        return mPaintText.getTextSize();\n    }\n\n    public void setTextSize(float textSize) {\n        mPaintText.setTextSize(textSize);\n        invalidate();\n    }\n\n    public float getTitlePadding() {\n        return this.mTitlePadding;\n    }\n\n    public void setTitlePadding(float titlePadding) {\n        mTitlePadding = titlePadding;\n        invalidate();\n    }\n\n    public float getTopPadding() {\n        return this.mTopPadding;\n    }\n\n    public void setTopPadding(float topPadding) {\n        mTopPadding = topPadding;\n        invalidate();\n    }\n\n    public float getClipPadding() {\n        return this.mClipPadding;\n    }\n\n    public void setClipPadding(float clipPadding) {\n        mClipPadding = clipPadding;\n        invalidate();\n    }\n\n    public void setTypeface(Typeface typeface) {\n        mPaintText.setTypeface(typeface);\n        invalidate();\n    }\n\n    public Typeface getTypeface() {\n        return mPaintText.getTypeface();\n    }\n\n    /*\n     * (non-Javadoc)\n     *\n     * @see android.view.View#onDraw(android.graphics.Canvas)\n     */\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n\n        if (mViewPager == null) {\n            return;\n        }\n        final int count = mViewPager.getAdapter().getCount();\n        if (count == 0) {\n            return;\n        }\n\n        // mCurrentPage is -1 on first start and after orientation changed. If so, retrieve the correct index from viewpager.\n        if (mCurrentPage == -1 && mViewPager != null) {\n            mCurrentPage = mViewPager.getCurrentItem();\n        }\n\n        //Calculate views bounds\n        ArrayList<Rect> bounds = calculateAllBounds(mPaintText);\n        final int boundsSize = bounds.size();\n\n        //Make sure we're on a page that still exists\n        if (mCurrentPage >= boundsSize) {\n            setCurrentItem(boundsSize - 1);\n            return;\n        }\n\n        final int countMinusOne = count - 1;\n        final float halfWidth = getWidth() / 2f;\n        final int left = getLeft();\n        final float leftClip = left + mClipPadding;\n        final int width = getWidth();\n        int height = getHeight();\n        final int right = left + width;\n        final float rightClip = right - mClipPadding;\n\n        int page = mCurrentPage;\n        float offsetPercent;\n        if (mPageOffset <= 0.5) {\n            offsetPercent = mPageOffset;\n        } else {\n            page += 1;\n            offsetPercent = 1 - mPageOffset;\n        }\n        final boolean currentSelected = (offsetPercent <= SELECTION_FADE_PERCENTAGE);\n        final boolean currentBold = (offsetPercent <= BOLD_FADE_PERCENTAGE);\n        final float selectedPercent = (SELECTION_FADE_PERCENTAGE - offsetPercent) / SELECTION_FADE_PERCENTAGE;\n\n        //Verify if the current view must be clipped to the screen\n        Rect curPageBound = bounds.get(mCurrentPage);\n        float curPageWidth = curPageBound.right - curPageBound.left;\n        if (curPageBound.left < leftClip) {\n            //Try to clip to the screen (left side)\n            clipViewOnTheLeft(curPageBound, curPageWidth, left);\n        }\n        if (curPageBound.right > rightClip) {\n            //Try to clip to the screen (right side)\n            clipViewOnTheRight(curPageBound, curPageWidth, right);\n        }\n\n        //Left views starting from the current position\n        if (mCurrentPage > 0) {\n            for (int i = mCurrentPage - 1; i >= 0; i--) {\n                Rect bound = bounds.get(i);\n                //Is left side is outside the screen\n                if (bound.left < leftClip) {\n                    int w = bound.right - bound.left;\n                    //Try to clip to the screen (left side)\n                    clipViewOnTheLeft(bound, w, left);\n                    //Except if there's an intersection with the right view\n                    Rect rightBound = bounds.get(i + 1);\n                    //Intersection\n                    if (bound.right + mTitlePadding > rightBound.left) {\n                        bound.left = (int) (rightBound.left - w - mTitlePadding);\n                        bound.right = bound.left + w;\n                    }\n                }\n            }\n        }\n        //Right views starting from the current position\n        if (mCurrentPage < countMinusOne) {\n            for (int i = mCurrentPage + 1 ; i < count; i++) {\n                Rect bound = bounds.get(i);\n                //If right side is outside the screen\n                if (bound.right > rightClip) {\n                    int w = bound.right - bound.left;\n                    //Try to clip to the screen (right side)\n                    clipViewOnTheRight(bound, w, right);\n                    //Except if there's an intersection with the left view\n                    Rect leftBound = bounds.get(i - 1);\n                    //Intersection\n                    if (bound.left - mTitlePadding < leftBound.right) {\n                        bound.left = (int) (leftBound.right + mTitlePadding);\n                        bound.right = bound.left + w;\n                    }\n                }\n            }\n        }\n\n        //Now draw views\n        int colorTextAlpha = mColorText >>> 24;\n        for (int i = 0; i < count; i++) {\n            //Get the title\n            Rect bound = bounds.get(i);\n            //Only if one side is visible\n            if ((bound.left > left && bound.left < right) || (bound.right > left && bound.right < right)) {\n                final boolean currentPage = (i == page);\n                final CharSequence pageTitle = getTitle(i);\n\n                //Only set bold if we are within bounds\n                mPaintText.setFakeBoldText(currentPage && currentBold && mBoldText);\n\n                //Draw text as unselected\n                mPaintText.setColor(mColorText);\n                if(currentPage && currentSelected) {\n                    //Fade out/in unselected text as the selected text fades in/out\n                    mPaintText.setAlpha(colorTextAlpha - (int)(colorTextAlpha * selectedPercent));\n                }\n\n                //Except if there's an intersection with the right view\n                if (i < boundsSize - 1)  {\n                    Rect rightBound = bounds.get(i + 1);\n                    //Intersection\n                    if (bound.right + mTitlePadding > rightBound.left) {\n                        int w = bound.right - bound.left;\n                        bound.left = (int) (rightBound.left - w - mTitlePadding);\n                        bound.right = bound.left + w;\n                    }\n                }\n                canvas.drawText(pageTitle, 0, pageTitle.length(), bound.left, bound.bottom + mTopPadding, mPaintText);\n\n                //If we are within the selected bounds draw the selected text\n                if (currentPage && currentSelected) {\n                    mPaintText.setColor(mColorSelected);\n                    mPaintText.setAlpha((int)((mColorSelected >>> 24) * selectedPercent));\n                    canvas.drawText(pageTitle, 0, pageTitle.length(), bound.left, bound.bottom + mTopPadding, mPaintText);\n                }\n            }\n        }\n\n        //If we want the line on the top change height to zero and invert the line height to trick the drawing code\n        float footerLineHeight = mFooterLineHeight;\n        float footerIndicatorLineHeight = mFooterIndicatorHeight;\n        if (mLinePosition == LinePosition.Top) {\n            height = 0;\n            footerLineHeight = -footerLineHeight;\n            footerIndicatorLineHeight = -footerIndicatorLineHeight;\n        }\n\n        //Draw the footer line\n        mPath.reset();\n        mPath.moveTo(0, height - footerLineHeight / 2f);\n        mPath.lineTo(width, height - footerLineHeight / 2f);\n        mPath.close();\n        canvas.drawPath(mPath, mPaintFooterLine);\n\n        float heightMinusLine = height - footerLineHeight;\n        switch (mFooterIndicatorStyle) {\n            case Triangle:\n                mPath.reset();\n                mPath.moveTo(halfWidth, heightMinusLine - footerIndicatorLineHeight);\n                mPath.lineTo(halfWidth + footerIndicatorLineHeight, heightMinusLine);\n                mPath.lineTo(halfWidth - footerIndicatorLineHeight, heightMinusLine);\n                mPath.close();\n                canvas.drawPath(mPath, mPaintFooterIndicator);\n                break;\n\n            case Underline:\n                if (!currentSelected || page >= boundsSize) {\n                    break;\n                }\n\n                Rect underlineBounds = bounds.get(page);\n                final float rightPlusPadding = underlineBounds.right + mFooterIndicatorUnderlinePadding;\n                final float leftMinusPadding = underlineBounds.left - mFooterIndicatorUnderlinePadding;\n                final float heightMinusLineMinusIndicator = heightMinusLine - footerIndicatorLineHeight;\n\n                mPath.reset();\n                mPath.moveTo(leftMinusPadding, heightMinusLine);\n                mPath.lineTo(rightPlusPadding, heightMinusLine);\n                mPath.lineTo(rightPlusPadding, heightMinusLineMinusIndicator);\n                mPath.lineTo(leftMinusPadding, heightMinusLineMinusIndicator);\n                mPath.close();\n\n                mPaintFooterIndicator.setAlpha((int)(0xFF * selectedPercent));\n                canvas.drawPath(mPath, mPaintFooterIndicator);\n                mPaintFooterIndicator.setAlpha(0xFF);\n                break;\n        }\n    }\n\n    public boolean onTouchEvent(android.view.MotionEvent ev) {\n        if (super.onTouchEvent(ev)) {\n            return true;\n        }\n        if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) {\n            return false;\n        }\n\n        final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;\n        switch (action) {\n            case MotionEvent.ACTION_DOWN:\n                mActivePointerId = MotionEventCompat.getPointerId(ev, 0);\n                mLastMotionX = ev.getX();\n                break;\n\n            case MotionEvent.ACTION_MOVE: {\n                final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);\n                final float x = MotionEventCompat.getX(ev, activePointerIndex);\n                final float deltaX = x - mLastMotionX;\n\n                if (!mIsDragging) {\n                    if (Math.abs(deltaX) > mTouchSlop) {\n                        mIsDragging = true;\n                    }\n                }\n\n                if (mIsDragging) {\n                    mLastMotionX = x;\n                    if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) {\n                        mViewPager.fakeDragBy(deltaX);\n                    }\n                }\n\n                break;\n            }\n\n            case MotionEvent.ACTION_CANCEL:\n            case MotionEvent.ACTION_UP:\n                if (!mIsDragging) {\n                    final int count = mViewPager.getAdapter().getCount();\n                    final int width = getWidth();\n                    final float halfWidth = width / 2f;\n                    final float sixthWidth = width / 6f;\n                    final float leftThird = halfWidth - sixthWidth;\n                    final float rightThird = halfWidth + sixthWidth;\n                    final float eventX = ev.getX();\n\n                    if (eventX < leftThird) {\n                        if (mCurrentPage > 0) {\n                            if (action != MotionEvent.ACTION_CANCEL) {\n                                mViewPager.setCurrentItem(mCurrentPage - 1);\n                            }\n                            return true;\n                        }\n                    } else if (eventX > rightThird) {\n                        if (mCurrentPage < count - 1) {\n                            if (action != MotionEvent.ACTION_CANCEL) {\n                                mViewPager.setCurrentItem(mCurrentPage + 1);\n                            }\n                            return true;\n                        }\n                    } else {\n                        //Middle third\n                        if (mCenterItemClickListener != null && action != MotionEvent.ACTION_CANCEL) {\n                            mCenterItemClickListener.onCenterItemClick(mCurrentPage);\n                        }\n                    }\n                }\n\n                mIsDragging = false;\n                mActivePointerId = INVALID_POINTER;\n                if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag();\n                break;\n\n            case MotionEventCompat.ACTION_POINTER_DOWN: {\n                final int index = MotionEventCompat.getActionIndex(ev);\n                mLastMotionX = MotionEventCompat.getX(ev, index);\n                mActivePointerId = MotionEventCompat.getPointerId(ev, index);\n                break;\n            }\n\n            case MotionEventCompat.ACTION_POINTER_UP:\n                final int pointerIndex = MotionEventCompat.getActionIndex(ev);\n                final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);\n                if (pointerId == mActivePointerId) {\n                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;\n                    mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);\n                }\n                mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId));\n                break;\n        }\n\n        return true;\n    }\n\n    /**\n     * Set bounds for the right textView including clip padding.\n     *\n     * @param curViewBound\n     *            current bounds.\n     * @param curViewWidth\n     *            width of the view.\n     */\n    private void clipViewOnTheRight(Rect curViewBound, float curViewWidth, int right) {\n        curViewBound.right = (int) (right - mClipPadding);\n        curViewBound.left = (int) (curViewBound.right - curViewWidth);\n    }\n\n    /**\n     * Set bounds for the left textView including clip padding.\n     *\n     * @param curViewBound\n     *            current bounds.\n     * @param curViewWidth\n     *            width of the view.\n     */\n    private void clipViewOnTheLeft(Rect curViewBound, float curViewWidth, int left) {\n        curViewBound.left = (int) (left + mClipPadding);\n        curViewBound.right = (int) (mClipPadding + curViewWidth);\n    }\n\n    /**\n     * Calculate views bounds and scroll them according to the current index\n     *\n     * @param paint\n     * @return\n     */\n    private ArrayList<Rect> calculateAllBounds(Paint paint) {\n        ArrayList<Rect> list = new ArrayList<Rect>();\n        //For each views (If no values then add a fake one)\n        final int count = mViewPager.getAdapter().getCount();\n        final int width = getWidth();\n        final int halfWidth = width / 2;\n        for (int i = 0; i < count; i++) {\n            Rect bounds = calcBounds(i, paint);\n            int w = bounds.right - bounds.left;\n            int h = bounds.bottom - bounds.top;\n            bounds.left = (int)(halfWidth - (w / 2f) + ((i - mCurrentPage - mPageOffset) * width));\n            bounds.right = bounds.left + w;\n            bounds.top = 0;\n            bounds.bottom = h;\n            list.add(bounds);\n        }\n\n        return list;\n    }\n\n    /**\n     * Calculate the bounds for a view's title\n     *\n     * @param index\n     * @param paint\n     * @return\n     */\n    private Rect calcBounds(int index, Paint paint) {\n        //Calculate the text bounds\n        Rect bounds = new Rect();\n        CharSequence title = getTitle(index);\n        bounds.right = (int) paint.measureText(title, 0, title.length());\n        bounds.bottom = (int) (paint.descent() - paint.ascent());\n        return bounds;\n    }\n\n    @Override\n    public void setViewPager(ViewPager view) {\n        if (mViewPager == view) {\n            return;\n        }\n        if (mViewPager != null) {\n            mViewPager.setOnPageChangeListener(null);\n        }\n        if (view.getAdapter() == null) {\n            throw new IllegalStateException(\"ViewPager does not have adapter instance.\");\n        }\n        mViewPager = view;\n        mViewPager.setOnPageChangeListener(this);\n        invalidate();\n    }\n\n    @Override\n    public void setViewPager(ViewPager view, int initialPosition) {\n        setViewPager(view);\n        setCurrentItem(initialPosition);\n    }\n\n    @Override\n    public void notifyDataSetChanged() {\n        invalidate();\n    }\n\n    /**\n     * Set a callback listener for the center item click.\n     *\n     * @param listener Callback instance.\n     */\n    public void setOnCenterItemClickListener(OnCenterItemClickListener listener) {\n        mCenterItemClickListener = listener;\n    }\n\n    @Override\n    public void setCurrentItem(int item) {\n        if (mViewPager == null) {\n            throw new IllegalStateException(\"ViewPager has not been bound.\");\n        }\n        mViewPager.setCurrentItem(item);\n        mCurrentPage = item;\n        invalidate();\n    }\n\n    @Override\n    public void onPageScrollStateChanged(int state) {\n        mScrollState = state;\n\n        if (mListener != null) {\n            mListener.onPageScrollStateChanged(state);\n        }\n    }\n\n    @Override\n    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {\n        mCurrentPage = position;\n        mPageOffset = positionOffset;\n        invalidate();\n\n        if (mListener != null) {\n            mListener.onPageScrolled(position, positionOffset, positionOffsetPixels);\n        }\n    }\n\n    @Override\n    public void onPageSelected(int position) {\n        if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {\n            mCurrentPage = position;\n            invalidate();\n        }\n\n        if (mListener != null) {\n            mListener.onPageSelected(position);\n        }\n    }\n\n    @Override\n    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {\n        mListener = listener;\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        //Measure our width in whatever mode specified\n        final int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);\n\n        //Determine our height\n        float height;\n        final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);\n        if (heightSpecMode == MeasureSpec.EXACTLY) {\n            //We were told how big to be\n            height = MeasureSpec.getSize(heightMeasureSpec);\n        } else {\n            //Calculate the text bounds\n            mBounds.setEmpty();\n            mBounds.bottom = (int) (mPaintText.descent() - mPaintText.ascent());\n            height = mBounds.bottom - mBounds.top + mFooterLineHeight + mFooterPadding + mTopPadding;\n            if (mFooterIndicatorStyle != IndicatorStyle.None) {\n                height += mFooterIndicatorHeight;\n            }\n        }\n        final int measuredHeight = (int)height;\n\n        setMeasuredDimension(measuredWidth, measuredHeight);\n    }\n\n    @Override\n    public void onRestoreInstanceState(Parcelable state) {\n        SavedState savedState = (SavedState)state;\n        super.onRestoreInstanceState(savedState.getSuperState());\n        mCurrentPage = savedState.currentPage;\n        requestLayout();\n    }\n\n    @Override\n    public Parcelable onSaveInstanceState() {\n        Parcelable superState = super.onSaveInstanceState();\n        SavedState savedState = new SavedState(superState);\n        savedState.currentPage = mCurrentPage;\n        return savedState;\n    }\n\n    static class SavedState extends BaseSavedState {\n        int currentPage;\n\n        public SavedState(Parcelable superState) {\n            super(superState);\n        }\n\n        private SavedState(Parcel in) {\n            super(in);\n            currentPage = in.readInt();\n        }\n\n        @Override\n        public void writeToParcel(Parcel dest, int flags) {\n            super.writeToParcel(dest, flags);\n            dest.writeInt(currentPage);\n        }\n\n        @SuppressWarnings(\"UnusedDeclaration\")\n        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {\n            @Override\n            public SavedState createFromParcel(Parcel in) {\n                return new SavedState(in);\n            }\n\n            @Override\n            public SavedState[] newArray(int size) {\n                return new SavedState[size];\n            }\n        };\n    }\n\n    private CharSequence getTitle(int i) {\n        CharSequence title = mViewPager.getAdapter().getPageTitle(i);\n        if (title == null) {\n            title = EMPTY_TITLE;\n        }\n        return title;\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/viewpagerindicator/UnderlinePageIndicator.java",
    "content": "/*\n * Copyright (C) 2012 Jake Wharton\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.viewpagerindicator;\n\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.drawable.Drawable;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.support.v4.view.MotionEventCompat;\nimport android.support.v4.view.ViewConfigurationCompat;\nimport android.support.v4.view.ViewPager;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewConfiguration;\n\nimport com.way.yahoo.R;\n\n/**\n * Draws a line for each page. The current page line is colored differently\n * than the unselected page lines.\n */\npublic class UnderlinePageIndicator extends View implements PageIndicator {\n    private static final int INVALID_POINTER = -1;\n    private static final int FADE_FRAME_MS = 30;\n\n    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n\n    private boolean mFades;\n    private int mFadeDelay;\n    private int mFadeLength;\n    private int mFadeBy;\n\n    private ViewPager mViewPager;\n    private ViewPager.OnPageChangeListener mListener;\n    private int mScrollState;\n    private int mCurrentPage;\n    private float mPositionOffset;\n\n    private int mTouchSlop;\n    private float mLastMotionX = -1;\n    private int mActivePointerId = INVALID_POINTER;\n    private boolean mIsDragging;\n\n    private final Runnable mFadeRunnable = new Runnable() {\n      @Override public void run() {\n        if (!mFades) return;\n\n        final int alpha = Math.max(mPaint.getAlpha() - mFadeBy, 0);\n        mPaint.setAlpha(alpha);\n        invalidate();\n        if (alpha > 0) {\n          postDelayed(this, FADE_FRAME_MS);\n        }\n      }\n    };\n\n    public UnderlinePageIndicator(Context context) {\n        this(context, null);\n    }\n\n    public UnderlinePageIndicator(Context context, AttributeSet attrs) {\n        this(context, attrs, R.attr.vpiUnderlinePageIndicatorStyle);\n    }\n\n    public UnderlinePageIndicator(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        if (isInEditMode()) return;\n\n        final Resources res = getResources();\n\n        //Load defaults from resources\n        final boolean defaultFades = res.getBoolean(R.bool.default_underline_indicator_fades);\n        final int defaultFadeDelay = res.getInteger(R.integer.default_underline_indicator_fade_delay);\n        final int defaultFadeLength = res.getInteger(R.integer.default_underline_indicator_fade_length);\n        final int defaultSelectedColor = res.getColor(R.color.default_underline_indicator_selected_color);\n\n        //Retrieve styles attributes\n        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.UnderlinePageIndicator, defStyle, 0);\n\n        setFades(a.getBoolean(R.styleable.UnderlinePageIndicator_fades, defaultFades));\n        setSelectedColor(a.getColor(R.styleable.UnderlinePageIndicator_selectedColor, defaultSelectedColor));\n        setFadeDelay(a.getInteger(R.styleable.UnderlinePageIndicator_fadeDelay, defaultFadeDelay));\n        setFadeLength(a.getInteger(R.styleable.UnderlinePageIndicator_fadeLength, defaultFadeLength));\n\n        Drawable background = a.getDrawable(R.styleable.UnderlinePageIndicator_android_background);\n        if (background != null) {\n          setBackgroundDrawable(background);\n        }\n\n        a.recycle();\n\n        final ViewConfiguration configuration = ViewConfiguration.get(context);\n        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);\n    }\n\n    public boolean getFades() {\n        return mFades;\n    }\n\n    public void setFades(boolean fades) {\n        if (fades != mFades) {\n            mFades = fades;\n            if (fades) {\n                post(mFadeRunnable);\n            } else {\n                removeCallbacks(mFadeRunnable);\n                mPaint.setAlpha(0xFF);\n                invalidate();\n            }\n        }\n    }\n\n    public int getFadeDelay() {\n        return mFadeDelay;\n    }\n\n    public void setFadeDelay(int fadeDelay) {\n        mFadeDelay = fadeDelay;\n    }\n\n    public int getFadeLength() {\n        return mFadeLength;\n    }\n\n    public void setFadeLength(int fadeLength) {\n        mFadeLength = fadeLength;\n        mFadeBy = 0xFF / (mFadeLength / FADE_FRAME_MS);\n    }\n\n    public int getSelectedColor() {\n        return mPaint.getColor();\n    }\n\n    public void setSelectedColor(int selectedColor) {\n        mPaint.setColor(selectedColor);\n        invalidate();\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n\n        if (mViewPager == null) {\n            return;\n        }\n        final int count = mViewPager.getAdapter().getCount();\n        if (count == 0) {\n            return;\n        }\n\n        if (mCurrentPage >= count) {\n            setCurrentItem(count - 1);\n            return;\n        }\n\n        final int paddingLeft = getPaddingLeft();\n        final float pageWidth = (getWidth() - paddingLeft - getPaddingRight()) / (1f * count);\n        final float left = paddingLeft + pageWidth * (mCurrentPage + mPositionOffset);\n        final float right = left + pageWidth;\n        final float top = getPaddingTop();\n        final float bottom = getHeight() - getPaddingBottom();\n        canvas.drawRect(left, top, right, bottom, mPaint);\n    }\n\n    public boolean onTouchEvent(MotionEvent ev) {\n        if (super.onTouchEvent(ev)) {\n            return true;\n        }\n        if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) {\n            return false;\n        }\n\n        final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;\n        switch (action) {\n            case MotionEvent.ACTION_DOWN:\n                mActivePointerId = MotionEventCompat.getPointerId(ev, 0);\n                mLastMotionX = ev.getX();\n                break;\n\n            case MotionEvent.ACTION_MOVE: {\n                final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);\n                final float x = MotionEventCompat.getX(ev, activePointerIndex);\n                final float deltaX = x - mLastMotionX;\n\n                if (!mIsDragging) {\n                    if (Math.abs(deltaX) > mTouchSlop) {\n                        mIsDragging = true;\n                    }\n                }\n\n                if (mIsDragging) {\n                    mLastMotionX = x;\n                    if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) {\n                        mViewPager.fakeDragBy(deltaX);\n                    }\n                }\n\n                break;\n            }\n\n            case MotionEvent.ACTION_CANCEL:\n            case MotionEvent.ACTION_UP:\n                if (!mIsDragging) {\n                    final int count = mViewPager.getAdapter().getCount();\n                    final int width = getWidth();\n                    final float halfWidth = width / 2f;\n                    final float sixthWidth = width / 6f;\n\n                    if ((mCurrentPage > 0) && (ev.getX() < halfWidth - sixthWidth)) {\n                        if (action != MotionEvent.ACTION_CANCEL) {\n                            mViewPager.setCurrentItem(mCurrentPage - 1);\n                        }\n                        return true;\n                    } else if ((mCurrentPage < count - 1) && (ev.getX() > halfWidth + sixthWidth)) {\n                        if (action != MotionEvent.ACTION_CANCEL) {\n                            mViewPager.setCurrentItem(mCurrentPage + 1);\n                        }\n                        return true;\n                    }\n                }\n\n                mIsDragging = false;\n                mActivePointerId = INVALID_POINTER;\n                if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag();\n                break;\n\n            case MotionEventCompat.ACTION_POINTER_DOWN: {\n                final int index = MotionEventCompat.getActionIndex(ev);\n                mLastMotionX = MotionEventCompat.getX(ev, index);\n                mActivePointerId = MotionEventCompat.getPointerId(ev, index);\n                break;\n            }\n\n            case MotionEventCompat.ACTION_POINTER_UP:\n                final int pointerIndex = MotionEventCompat.getActionIndex(ev);\n                final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);\n                if (pointerId == mActivePointerId) {\n                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;\n                    mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);\n                }\n                mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId));\n                break;\n        }\n\n        return true;\n    }\n\n    @Override\n    public void setViewPager(ViewPager viewPager) {\n        if (mViewPager == viewPager) {\n            return;\n        }\n        if (mViewPager != null) {\n            //Clear us from the old pager.\n            mViewPager.setOnPageChangeListener(null);\n        }\n        if (viewPager.getAdapter() == null) {\n            throw new IllegalStateException(\"ViewPager does not have adapter instance.\");\n        }\n        mViewPager = viewPager;\n        mViewPager.setOnPageChangeListener(this);\n        invalidate();\n        post(new Runnable() {\n            @Override public void run() {\n                if (mFades) {\n                    post(mFadeRunnable);\n                }\n            }\n        });\n    }\n\n    @Override\n    public void setViewPager(ViewPager view, int initialPosition) {\n        setViewPager(view);\n        setCurrentItem(initialPosition);\n    }\n\n    @Override\n    public void setCurrentItem(int item) {\n        if (mViewPager == null) {\n            throw new IllegalStateException(\"ViewPager has not been bound.\");\n        }\n        mViewPager.setCurrentItem(item);\n        mCurrentPage = item;\n        invalidate();\n    }\n\n    @Override\n    public void notifyDataSetChanged() {\n        invalidate();\n    }\n\n    @Override\n    public void onPageScrollStateChanged(int state) {\n        mScrollState = state;\n\n        if (mListener != null) {\n            mListener.onPageScrollStateChanged(state);\n        }\n    }\n\n    @Override\n    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {\n        mCurrentPage = position;\n        mPositionOffset = positionOffset;\n        if (mFades) {\n            if (positionOffsetPixels > 0) {\n                removeCallbacks(mFadeRunnable);\n                mPaint.setAlpha(0xFF);\n            } else if (mScrollState != ViewPager.SCROLL_STATE_DRAGGING) {\n                postDelayed(mFadeRunnable, mFadeDelay);\n            }\n        }\n        invalidate();\n\n        if (mListener != null) {\n            mListener.onPageScrolled(position, positionOffset, positionOffsetPixels);\n        }\n    }\n\n    @Override\n    public void onPageSelected(int position) {\n        if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {\n            mCurrentPage = position;\n            mPositionOffset = 0;\n            invalidate();\n            mFadeRunnable.run();\n        }\n        if (mListener != null) {\n            mListener.onPageSelected(position);\n        }\n    }\n\n    @Override\n    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {\n        mListener = listener;\n    }\n\n    @Override\n    public void onRestoreInstanceState(Parcelable state) {\n        SavedState savedState = (SavedState)state;\n        super.onRestoreInstanceState(savedState.getSuperState());\n        mCurrentPage = savedState.currentPage;\n        requestLayout();\n    }\n\n    @Override\n    public Parcelable onSaveInstanceState() {\n        Parcelable superState = super.onSaveInstanceState();\n        SavedState savedState = new SavedState(superState);\n        savedState.currentPage = mCurrentPage;\n        return savedState;\n    }\n\n    static class SavedState extends BaseSavedState {\n        int currentPage;\n\n        public SavedState(Parcelable superState) {\n            super(superState);\n        }\n\n        private SavedState(Parcel in) {\n            super(in);\n            currentPage = in.readInt();\n        }\n\n        @Override\n        public void writeToParcel(Parcel dest, int flags) {\n            super.writeToParcel(dest, flags);\n            dest.writeInt(currentPage);\n        }\n\n        @SuppressWarnings(\"UnusedDeclaration\")\n        public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {\n            @Override\n            public SavedState createFromParcel(Parcel in) {\n                return new SavedState(in);\n            }\n\n            @Override\n            public SavedState[] newArray(int size) {\n                return new SavedState[size];\n            }\n        };\n    }\n}"
  },
  {
    "path": "WayHoo/src/com/way/adapter/FragmentPagerAdapter.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.way.adapter;\n\nimport com.way.fragment.WeatherFragment;\n\nimport android.app.Fragment;\nimport android.app.FragmentManager;\nimport android.app.FragmentTransaction;\nimport android.os.Parcelable;\nimport android.support.v4.app.FragmentStatePagerAdapter;\nimport android.support.v4.view.PagerAdapter;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewGroup;\n\n/**\n * Implementation of {@link android.support.v4.view.PagerAdapter} that\n * represents each page as a {@link android.app.Fragment} that is persistently\n * kept in the fragment manager as long as the user can return to the page.\n *\n * <p>This version of the pager is best for use when there are a handful of\n * typically more static fragments to be paged through, such as a set of tabs.\n * The fragment of each page the user visits will be kept in memory, though its\n * view hierarchy may be destroyed when not visible.  This can result in using\n * a significant amount of memory since fragment instances can hold on to an\n * arbitrary amount of state.  For larger sets of pages, consider\n * {@link FragmentStatePagerAdapter}.\n *\n * <p>When using FragmentPagerAdapter the host ViewPager must have a\n * valid ID set.</p>\n *\n * <p>Subclasses only need to implement {@link #getItem(int)}\n * and {@link #getCount()} to have a working adapter.\n *\n * <p>Here is an example implementation of a pager containing fragments of\n * lists:\n *\n * {@sample development/samples/Support13Demos/src/com/example/android/supportv13/app/FragmentPagerSupport.java\n *      complete}\n *\n * <p>The <code>R.layout.fragment_pager</code> resource of the top-level fragment is:\n *\n * {@sample development/samples/Support13Demos/res/layout/fragment_pager.xml\n *      complete}\n *\n * <p>The <code>R.layout.fragment_pager_list</code> resource containing each\n * individual fragment's layout is:\n *\n * {@sample development/samples/Support13Demos/res/layout/fragment_pager_list.xml\n *      complete}\n */\npublic abstract class FragmentPagerAdapter extends PagerAdapter {\n    private static final String TAG = \"FragmentPagerAdapter\";\n    private static final boolean DEBUG = true;\n\n\tprotected final FragmentManager mFragmentManager;\n\tprotected FragmentTransaction mCurTransaction = null;\n    private Fragment mCurrentPrimaryItem = null;\n\n    public FragmentPagerAdapter(FragmentManager fm) {\n        mFragmentManager = fm;\n    }\n\n    /**\n     * Return the Fragment associated with a specified position.\n     */\n    public abstract Fragment getItem(int position);\n\n    @Override\n    public void startUpdate(ViewGroup container) {\n    }\n\n    @Override\n    public Object instantiateItem(ViewGroup container, int position) {\n        if (mCurTransaction == null) {\n            mCurTransaction = mFragmentManager.beginTransaction();\n        }\n\n        final long itemId = getItemId(position);\n\n        // Do we already have this fragment?\n\t\tString name = makeFragmentName(position);\n        Fragment fragment = mFragmentManager.findFragmentByTag(name);\n        if (fragment != null) {\n            if (DEBUG) Log.v(TAG, \"Attaching item #\" + itemId + \": f=\" + fragment);\n//\t\t\tmCurTransaction.attach(fragment);\n\t\t\tfreshUI(fragment);\n        } else {\n            fragment = getItem(position);\n            if (DEBUG) Log.v(TAG, \"Adding item #\" + itemId + \": f=\" + fragment);\n\t\t\tmCurTransaction.add(container.getId(), fragment, makeFragmentName(position));\n        }\n        if (fragment != mCurrentPrimaryItem) {\n\t\t\tfragment.setMenuVisibility(false);\n            fragment.setUserVisibleHint(false);\n        }\n\n        return fragment;\n    }\n\tprotected void freshUI(Fragment fragment) {\n\t\tif (fragment instanceof WeatherFragment)\n\t\t\t((WeatherFragment) fragment).refreshUI();\n\t}\n\n    @Override\n    public void destroyItem(ViewGroup container, int position, Object object) {\n        if (mCurTransaction == null) {\n            mCurTransaction = mFragmentManager.beginTransaction();\n        }\n        if (DEBUG) Log.v(TAG, \"Detaching item #\" + getItemId(position) + \": f=\" + object\n                + \" v=\" + ((Fragment)object).getView());\n//\t\tmCurTransaction.detach((Fragment) object);\n\t\tif (object instanceof WeatherFragment)\n\t\t\t((WeatherFragment) object).releaseImageViewByIds();\n    }\n\n    @Override\n    public void setPrimaryItem(ViewGroup container, int position, Object object) {\n        Fragment fragment = (Fragment)object;\n        if (fragment != mCurrentPrimaryItem) {\n            if (mCurrentPrimaryItem != null) {\n\t\t\t\tmCurrentPrimaryItem.setMenuVisibility(false);\n                mCurrentPrimaryItem.setUserVisibleHint(false);\n            }\n            if (fragment != null) {\n\t\t\t\tfragment.setMenuVisibility(true);\n                fragment.setUserVisibleHint(true);\n            }\n            mCurrentPrimaryItem = fragment;\n        }\n    }\n\n    @Override\n    public void finishUpdate(ViewGroup container) {\n        if (mCurTransaction != null) {\n            mCurTransaction.commitAllowingStateLoss();\n            mCurTransaction = null;\n            mFragmentManager.executePendingTransactions();\n        }\n    }\n\n    @Override\n    public boolean isViewFromObject(View view, Object object) {\n        return ((Fragment)object).getView() == view;\n    }\n\n    @Override\n    public Parcelable saveState() {\n        return null;\n    }\n\n    @Override\n    public void restoreState(Parcelable state, ClassLoader loader) {\n    }\n\n    /**\n     * Return a unique identifier for the item at the given position.\n     *\n     * <p>The default implementation returns the given position.\n     * Subclasses should override this method if the positions of items can change.</p>\n     *\n     * @param position Position within this adapter\n     * @return Unique identifier for the item at position\n     */\n    public long getItemId(int position) {\n        return position;\n    }\n\n\tprotected abstract String makeFragmentName(int position);\n\n}"
  },
  {
    "path": "WayHoo/src/com/way/adapter/ParallaxPagerTransformer.java",
    "content": "package com.way.adapter;\n\nimport android.os.Build;\nimport android.support.v4.view.ViewPager;\nimport android.view.View;\n\npublic class ParallaxPagerTransformer implements ViewPager.PageTransformer {\n\n\tprivate int id;\n\tprivate int border = 0;\n\tprivate float speed = 0.2f;\n\n\tpublic ParallaxPagerTransformer(int id) {\n\t\tthis.id = id;\n\t}\n\n\t@Override\n\tpublic void transformPage(View view, float position) {\n\n\t\tView parallaxView = view.findViewById(id);\n\n\t\tif (parallaxView != null\n\t\t\t\t&& Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) {\n\t\t\tif (position > -1 && position < 1) {\n\t\t\t\tfloat width = parallaxView.getWidth();\n\t\t\t\tparallaxView.setTranslationX(-(position * width * speed));\n//\t\t\t\tfloat sc = ((float) view.getWidth() - border) / view.getWidth();\n//\t\t\t\tif (position == 0) {\n//\t\t\t\t\tview.setScaleX(1);\n//\t\t\t\t\tview.setScaleY(1);\n//\t\t\t\t} else {\n//\t\t\t\t\tview.setScaleX(sc);\n//\t\t\t\t\tview.setScaleY(sc);\n//\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void setBorder(int px) {\n\t\tborder = px;\n\t}\n\n\tpublic void setSpeed(float speed) {\n\t\tthis.speed = speed;\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/adapter/QueryCityAdapter.java",
    "content": "package com.way.adapter;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport android.content.Context;\nimport android.text.TextUtils;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.BaseAdapter;\nimport android.widget.Filterable;\nimport android.widget.TextView;\n\nimport com.way.beans.City;\nimport com.way.common.util.L;\nimport com.way.yahoo.R;\n\npublic class QueryCityAdapter extends BaseAdapter implements Filterable {\n\n\tprivate List<City> mAllCities;\n\tprivate List<City> mResultCities;\n\tprivate LayoutInflater mInflater;\n\tprivate Context mContext;\n\n\tpublic QueryCityAdapter(Context context, List<City> allCities) {\n\t\tmContext = context;\n\t\tmAllCities = allCities;\n\t\tmResultCities = new ArrayList<City>();\n\t\tmInflater = LayoutInflater.from(mContext);\n\t}\n\n\t@Override\n\tpublic int getCount() {\n\t\treturn mResultCities == null ? 0 : mResultCities.size();\n\t}\n\n\t@Override\n\tpublic City getItem(int position) {\n\t\treturn mResultCities.get(position);\n\t}\n\n\t@Override\n\tpublic long getItemId(int position) {\n\t\treturn position;\n\t}\n\n\t@Override\n\tpublic View getView(int position, View convertView, ViewGroup parent) {\n\t\tCity city = getItem(position);\n\t\tViewHolder holder;\n\t\tif (convertView == null) {\n\t\t\tconvertView = mInflater.inflate(R.layout.city_query_list_item, parent,\n\t\t\t\t\tfalse);\n\t\t\tholder = new ViewHolder();\n\t\t\tholder.nameTv = (TextView) convertView\n\t\t\t\t\t.findViewById(R.id.citylst_name);\n\t\t\tholder.cityTv = (TextView) convertView\n\t\t\t\t\t.findViewById(R.id.citylst_city);\n\t\t\tholder.provinceTv = (TextView) convertView\n\t\t\t\t\t.findViewById(R.id.citylst_province);\n\t\t\tconvertView.setTag(holder);\n\t\t} else {\n\t\t\tholder = (ViewHolder) convertView.getTag();\n\t\t}\n\n\t\tholder.provinceTv.setText(city.getProvince());\n\t\tholder.cityTv.setText(city.getCity());\n\t\tholder.nameTv.setText(city.getName());\n\t\treturn convertView;\n\t}\n\n\tstatic class ViewHolder {\n\t\tTextView provinceTv;\n\t\tTextView cityTv;\n\t\tTextView nameTv;\n\t}\n\n\t@Override\n\tpublic android.widget.Filter getFilter() {\n\t\tandroid.widget.Filter filter = new android.widget.Filter() {\n\t\t\tprotected void publishResults(CharSequence constraint,\n\t\t\t\t\tFilterResults results) {\n\t\t\t\tmResultCities = (ArrayList<City>) results.values;\n\t\t\t\tif (results.count > 0) {\n\t\t\t\t\tnotifyDataSetChanged();\n\t\t\t\t} else {\n\t\t\t\t\tnotifyDataSetInvalidated();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tprotected FilterResults performFiltering(CharSequence s) {\n\t\t\t\tL.i(\"liweiping\", \"performFiltering s = \" + s);\n\n\t\t\t\tFilterResults results = new FilterResults();\n\t\t\t\tArrayList<City> queryResultCities = new ArrayList<City>();\n\t\t\t\tresults.values = queryResultCities;\n\t\t\t\tresults.count = queryResultCities.size();\n\n\t\t\t\tif (TextUtils.isEmpty(s))\n\t\t\t\t\treturn results;\n\n\t\t\t\tString str = s.toString();\n\t\t\t\tfor (City allCity : mAllCities) {\n\t\t\t\t\t// 匹配全拼、首字母、中文城市名、区号、邮政编码\n\t\t\t\t\tif (allCity.getPinyin().startsWith(str)\n\t\t\t\t\t\t\t|| allCity.getPy().startsWith(str)\n\t\t\t\t\t\t\t|| allCity.getName().startsWith(str)\n\t\t\t\t\t\t\t|| allCity.getPhoneCode().startsWith(str)\n\t\t\t\t\t\t\t|| allCity.getAreaCode().startsWith(str))\n\t\t\t\t\t\tqueryResultCities.add(allCity);\n\t\t\t\t}\n\t\t\t\tresults.values = queryResultCities;\n\t\t\t\tresults.count = queryResultCities.size();\n\t\t\t\treturn results;\n\t\t\t}\n\t\t};\n\t\treturn filter;\n\t}\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/adapter/SideMenuAdapter.java",
    "content": "package com.way.adapter;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport android.content.Context;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.BaseAdapter;\nimport android.widget.TextView;\n\nimport com.way.beans.Category;\nimport com.way.beans.City;\nimport com.way.beans.Item;\nimport com.way.yahoo.R;\n\npublic class SideMenuAdapter extends BaseAdapter {\n\tprivate static final int ITEM_TYPE = 0;\n\tprivate static final int CATEGORY_TYPE = 1;\n\tprivate List<Object> mItems;\n\tprivate LayoutInflater mLayoutInflater;\n\n\tpublic SideMenuAdapter(Context context) {\n\t\tmItems = new ArrayList<Object>();\n\t\tmLayoutInflater = LayoutInflater.from(context);\n\t}\n\n\tpublic void addContent(List<City> tmpCities) {\n\t\tmItems.clear();\n\t\tmItems.add(new Category(\"城市管理\"));\n\t\tmItems.add(new Item(Item.INFINITE_ID, \"编辑地点\", R.drawable.editloc));\n\t\tfor (int i = 0; i < tmpCities.size(); i++)\n\t\t\tmItems.add(new Item(i, tmpCities.get(i).getName(), R.drawable.loc));\n\t\tmItems.add(new Category(\"工具\"));\n\t\t// mItems.add(new Item(Item.SETTING_ID, \"设置\",\n\t\t// R.drawable.sidebar_icon_settings_dark));\n\t\tmItems.add(new Item(Item.FEEDBACK_ID, \"意见与建议\",\n\t\t\t\tR.drawable.sidebar_icon_send_feedback_dark));\n\t\t// mItems.add(new Item(Item.SHARE_ID, \"分享\",\n\t\t// R.drawable.sidebar_icon_share_dark));\n\t\tmItems.add(new Item(Item.ABOUT_ID, \"关于\",\n\t\t\t\tR.drawable.sidebar_icon_rate_dark));\n\t\tnotifyDataSetChanged();\n\t}\n\n\t@Override\n\tpublic int getCount() {\n\t\treturn mItems.size();\n\t}\n\n\t@Override\n\tpublic Object getItem(int position) {\n\t\treturn mItems.get(position);\n\t}\n\n\t@Override\n\tpublic long getItemId(int position) {\n\t\treturn position;\n\t}\n\n\t@Override\n\tpublic int getItemViewType(int position) {\n\t\treturn getItem(position) instanceof Item ? ITEM_TYPE : CATEGORY_TYPE;\n\t}\n\n\t@Override\n\tpublic int getViewTypeCount() {\n\t\treturn 2;\n\t}\n\n\t@Override\n\tpublic boolean isEnabled(int position) {\n\t\treturn getItem(position) instanceof Item;\n\t}\n\n\t@Override\n\tpublic boolean areAllItemsEnabled() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic View getView(int position, View convertView, ViewGroup parent) {\n\t\tViewHolder viewHolder;\n\t\tint type = getItemViewType(position);\n\t\tif (convertView == null\n\t\t\t\t|| convertView.getTag(R.drawable.ic_launcher + type) == null) {\n\t\t\tswitch (type) {\n\t\t\tcase ITEM_TYPE:\n\t\t\t\tconvertView = mLayoutInflater.inflate(\n\t\t\t\t\t\tR.layout.sidemenu_list_item_item, parent, false);\n\t\t\t\tbreak;\n\t\t\tcase CATEGORY_TYPE:\n\t\t\t\tconvertView = mLayoutInflater.inflate(\n\t\t\t\t\t\tR.layout.sidemenu_list_item_category, parent, false);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tviewHolder = buildHolder(convertView);\n\t\t\tconvertView.setTag(R.drawable.ic_launcher + type, viewHolder);\n\t\t} else {\n\t\t\tviewHolder = (ViewHolder) convertView.getTag(R.drawable.ic_launcher\n\t\t\t\t\t+ type);\n\t\t}\n\t\tbindViewData(viewHolder, position);\n\t\treturn convertView;\n\t}\n\n\tprivate ViewHolder buildHolder(View convertView) {\n\t\tViewHolder holder = new ViewHolder();\n\t\tholder.titleView = (TextView) convertView;\n\t\treturn holder;\n\t}\n\n\tprivate void bindViewData(ViewHolder holder, final int position) {\n\t\tObject item = getItem(position);\n\t\tswitch (getItemViewType(position)) {\n\t\tcase ITEM_TYPE:\n\t\t\tholder.titleView.setText(((Item) item).mTitleStr);\n\t\t\tholder.titleView.setCompoundDrawablesWithIntrinsicBounds(\n\t\t\t\t\t((Item) item).mIconRes, 0, 0, 0);\n\t\t\tbreak;\n\t\tcase CATEGORY_TYPE:\n\t\t\tholder.titleView.setText(((Category) item).mTitleStr);\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate static final class ViewHolder {\n\t\tTextView titleView;\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/adapter/WeatherListAdapter.java",
    "content": "package com.way.adapter;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\n\nimport android.content.Context;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.BaseAdapter;\n\nimport com.way.ui.view.WeatherBaseView;\nimport com.way.weather.plugin.bean.AQI;\nimport com.way.weather.plugin.bean.WeatherInfo;\nimport com.way.weather.plugin.spider.WeatherSpider;\nimport com.way.yahoo.R;\n\npublic class WeatherListAdapter extends BaseAdapter {\n\tprivate final HashMap<Integer, WeatherBaseView> mWeatherBaseViews = new HashMap<Integer, WeatherBaseView>();\n\tpublic static final int FORECAST_TYPE = 0;\n\tpublic static final int WEATHER_DETAILS_TYPE = 1;\n\tpublic static final int AQI_TYPE = 2;\n\tpublic static final int INDEX_TYPE = 3;\n\n\tprivate LayoutInflater mLayoutInflater;\n\tprivate List<Integer> mTypes;\n\tprivate WeatherInfo mWeatherInfo;\n\n\tpublic WeatherListAdapter(Context context) {\n\t\tmTypes = new ArrayList<Integer>();\n\t\tmLayoutInflater = LayoutInflater.from(context);\n\t}\n\n\tpublic void initViews() {\n\t\tif(!mWeatherBaseViews.isEmpty())\n\t\t\treturn;\n\t\tWeatherBaseView convertView = (WeatherBaseView) mLayoutInflater\n\t\t\t\t.inflate(R.layout.weather_forecast, null);\n\t\tmWeatherBaseViews.put(FORECAST_TYPE, convertView);\n\t\tconvertView = (WeatherBaseView) mLayoutInflater.inflate(\n\t\t\t\tR.layout.weather_details, null);\n\t\tmWeatherBaseViews.put(WEATHER_DETAILS_TYPE, convertView);\n\t\tconvertView = (WeatherBaseView) mLayoutInflater.inflate(\n\t\t\t\tR.layout.weather_aqi, null);\n\t\tmWeatherBaseViews.put(AQI_TYPE, convertView);\n\t\tconvertView = (WeatherBaseView) mLayoutInflater.inflate(\n\t\t\t\tR.layout.weather_index, null);\n\t\tmWeatherBaseViews.put(INDEX_TYPE, convertView);\n\t}\n\n\tpublic void setWeather(WeatherInfo weatherInfo) {\n\t\tif (WeatherSpider.isEmpty(weatherInfo))\n\t\t\treturn;\n\t\tmWeatherInfo = weatherInfo;\n\t\tAQI aqi = weatherInfo.getAqi();\n\t\tmTypes.clear();\n\t\tmTypes.add(FORECAST_TYPE);\n\t\tmTypes.add(WEATHER_DETAILS_TYPE);\n\t\tif (!WeatherSpider.isEmpty(aqi) && aqi.getAqi() >= 0)\n\t\t\tmTypes.add(AQI_TYPE);\n\t\tmTypes.add(INDEX_TYPE);\n\n\t\tnotifyDataSetChanged();\n\t}\n\n\t@Override\n\tpublic int getCount() {\n\t\treturn mTypes.size();\n\t}\n\n\t@Override\n\tpublic Object getItem(int position) {\n\t\treturn mTypes.get(position);\n\t}\n\n\t@Override\n\tpublic long getItemId(int position) {\n\t\treturn position;\n\t}\n\n\t@Override\n\tpublic int getViewTypeCount() {\n\t\tif (mTypes.size() < 1)\n\t\t\treturn 1;\n\t\treturn mTypes.size();\n\t}\n\n\t@Override\n\tpublic int getItemViewType(int position) {\n\t\tif (position < mTypes.size())\n\t\t\treturn mTypes.get(position);\n\t\treturn super.getItemViewType(position);\n\t}\n\n\t@Override\n\tpublic View getView(int position, View convertView, ViewGroup parent) {\n\t\tint itemType = getItemViewType(position);\n\t\tif (convertView == null\n\t\t\t\t|| !convertView.getTag().equals(\n\t\t\t\t\t\tR.drawable.ic_launcher + itemType)) {\n\t\t\tfinal WeatherBaseView weakFragment = mWeatherBaseViews\n\t\t\t\t\t.get(itemType);\n\t\t\tif (weakFragment != null ) {\n\t\t\t\tLog.i(\"liweiping\", \"getView...\" + \"weakFragment = \" + weakFragment);\n\t\t\t\tconvertView = weakFragment;\n\t\t\t} else {\n\t\t\t\tswitch (itemType) {\n\t\t\t\tcase FORECAST_TYPE:\n\t\t\t\t\tconvertView = (WeatherBaseView) mLayoutInflater.inflate(\n\t\t\t\t\t\t\tR.layout.weather_forecast, parent, false);\n\t\t\t\t\tbreak;\n\t\t\t\tcase WEATHER_DETAILS_TYPE:\n\t\t\t\t\tconvertView = (WeatherBaseView) mLayoutInflater.inflate(\n\t\t\t\t\t\t\tR.layout.weather_details, parent, false);\n\t\t\t\t\tbreak;\n\t\t\t\tcase AQI_TYPE:\n\t\t\t\t\tconvertView = (WeatherBaseView) mLayoutInflater.inflate(\n\t\t\t\t\t\t\tR.layout.weather_aqi, parent, false);\n\t\t\t\t\tbreak;\n\t\t\t\tcase INDEX_TYPE:\n\t\t\t\t\tconvertView = (WeatherBaseView) mLayoutInflater.inflate(\n\t\t\t\t\t\t\tR.layout.weather_index, parent, false);\n\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tmWeatherBaseViews.put(itemType,\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t(WeatherBaseView) convertView);\n\t\t\t}\n\t\t\tconvertView.setTag(R.drawable.ic_launcher + itemType);\n\t\t}\n\t\tif (convertView instanceof WeatherBaseView\n\t\t\t\t&& !WeatherSpider.isEmpty(mWeatherInfo)) {\n\t\t\tWeatherBaseView baseView = (WeatherBaseView) convertView;\n\t\t\tbaseView.setWeatherInfo(mWeatherInfo);\n\t\t\treturn baseView;\n\t\t}\n\t\treturn convertView;\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/adapter/WeatherPagerAdapter.java",
    "content": "package com.way.adapter;\n\nimport java.lang.ref.WeakReference;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport android.app.Activity;\nimport android.app.Fragment;\nimport android.util.Log;\nimport android.util.SparseArray;\nimport android.view.ViewGroup;\n\nimport com.way.beans.City;\nimport com.way.fragment.WeatherFragment;\n\npublic class WeatherPagerAdapter extends FragmentPagerAdapter {\n\tprivate static final String TAG = \"WeatherPagerAdapter\";\n\t// private final HashMap<String, WeakReference<WeatherFragment>> mFragments\n\t// = new HashMap<String, WeakReference<WeatherFragment>>();\n\tprivate final SparseArray<WeakReference<Fragment>> mFragmentArray = new SparseArray<WeakReference<Fragment>>();\n\tprivate final ArrayList<City> mCitys = new ArrayList<City>();\n\tprivate int mCurrentPage;\n\tprivate Activity mActivity;\n\n\tpublic WeatherPagerAdapter(Activity activity) {\n\t\tsuper(activity.getFragmentManager());\n\t\tmActivity = activity;\n\t}\n\n\tpublic void setData(List<City> mainItems) {\n\t\tsynchronized (mCitys) {\n\t\t\tmCitys.clear();\n\t\t\tmCitys.addAll(mainItems);\n\t\t\tnotifyDataSetChanged();\n\t\t}\n\t}\n\n\tpublic Fragment getFragment(final int position) {\n\t\tLog.i(TAG, \"getFragment...\");\n\t\tfinal WeakReference<Fragment> weakFragment = mFragmentArray\n\t\t\t\t.get(position);\n\t\tif (weakFragment != null && weakFragment.get() != null) {\n\t\t\treturn weakFragment.get();\n\t\t}\n\t\treturn getItem(position);\n\t}\n\n\t@Override\n\tpublic Object instantiateItem(final ViewGroup container, final int position) {\n\t\tLog.i(TAG, \"instantiateItem...\");\n\t\tFragment fragment = (Fragment) super.instantiateItem(container,\n\t\t\t\tposition);\n\t\tfinal WeakReference<Fragment> weakFragment = mFragmentArray\n\t\t\t\t.get(position);\n\t\tif (weakFragment != null) {\n\t\t\tweakFragment.clear();\n\t\t}\n\t\tmFragmentArray.put(position, new WeakReference<Fragment>(fragment));\n\n\t\treturn fragment;\n\t}\n\n\t@Override\n\tpublic void destroyItem(final ViewGroup container, final int position,\n\t\t\tfinal Object object) {\n\t\tLog.i(TAG, \"destroyItem...\");\n\t\tsuper.destroyItem(container, position, object);\n\t\tfinal WeakReference<Fragment> weakFragment = mFragmentArray\n\t\t\t\t.get(position);\n\t\tif (weakFragment != null) {\n\t\t\tweakFragment.clear();\n\t\t}\n\t}\n\n\t@Override\n\tpublic Fragment getItem(int position) {\n\t\tLog.i(TAG, \"getItem...\");\n\t\tfinal City info = mCitys.get(position);\n\t\tfinal Fragment mFragment = WeatherFragment.newInstance(info);\n\t\treturn mFragment;\n\t}\n\n\t@Override\n\tpublic int getCount() {\n\t\treturn mCitys.size();\n\t}\n\n\t@Override\n\tpublic CharSequence getPageTitle(int position) {\n\t\treturn mCitys.get(position).getName();\n\t}\n\n\t/**\n\t * Method that returns the current page position.\n\t * \n\t * @return int The current page.\n\t */\n\tpublic int getCurrentPage() {\n\t\treturn mCurrentPage;\n\t}\n\n\t/**\n\t * Method that sets the current page position.\n\t * \n\t * @param currentPage\n\t *            The current page.\n\t */\n\tpublic void setCurrentPage(final int currentPage) {\n\t\tmCurrentPage = currentPage;\n\t\tLog.i(\"refreshUI\", \"setCurrentPage mCurrentPage = \" + currentPage);\n\t\tif (mCurrentPage < 0 || mCurrentPage >= getCount())\n\t\t\treturn;\n\t\tfinal WeakReference<Fragment> weakFragment = mFragmentArray\n\t\t\t\t.get(currentPage);\n\t\tLog.i(\"refreshUI\", \"weakFragment = \" + weakFragment);\n\t\tif (weakFragment != null && weakFragment.get() != null) {\n\t\t\tWeatherFragment fragment = (WeatherFragment) weakFragment.get();\n\t\t\tLog.i(\"refreshUI\", \"WeatherFragment = \" + fragment);\n\t\t\tfragment.refreshUI();\n\t\t}\n\t}\n\n\t@Override\n\tpublic int getItemPosition(Object object) {\n\t\tLog.i(TAG, \"getItemPosition...\");\n\t\t// if (!TextUtils.equals(((Fragment) object).getTag(),\n\t\t// makeFragmentName(mCurrentPage)))\n\t\treturn POSITION_NONE;\n\t\t// return super.getItemPosition(object);\n\t}\n\n\t@Override\n\tprotected String makeFragmentName(int position) {\n\t\treturn mCitys.get(position).getPostID();\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/beans/Category.java",
    "content": "package com.way.beans;\n\npublic class Category {\n\n\tpublic String mTitleStr;\n\tpublic int mTitleRes;\n\n\tpublic Category(String title) {\n\t\tmTitleStr = title;\n\t}\n\n\tpublic Category(int title) {\n\t\tmTitleRes = title;\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/beans/City.java",
    "content": "package com.way.beans;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\npublic class City implements Parcelable {\n\tprivate String province;\n\tprivate String city;\n\tprivate String name;\n\tprivate String pinyin;\n\tprivate String py;\n\tprivate String phoneCode;\n\tprivate String areaCode;\n\tprivate String postID;\n\tprivate long refreshTime;\n\tprivate int isLocation;\n\tprivate long pubTime;\n\tprivate String weatherInfoStr;\n\n\tpublic City() {\n\t}\n\n\tpublic City(String name, String postID, long refreshTime, int isLocation, long pubTime, String weatherInfoStr) {\n\t\tsuper();\n\t\tthis.name = name;\n\t\tthis.postID = postID;\n\t\tthis.refreshTime = refreshTime;\n\t\tthis.isLocation = isLocation;\n\t\tthis.pubTime = pubTime;\n\t\tthis.weatherInfoStr = weatherInfoStr;\n\t}\n\n\tpublic City(String name, String postID) {\n\t\tsuper();\n\t\tthis.name = name;\n\t\tthis.postID = postID;\n\t}\n\n\tpublic City(String province, String city, String name, String pinyin,\n\t\t\tString py, String phoneCode, String areaCode, String postID) {\n\t\tsuper();\n\t\tthis.province = province;\n\t\tthis.city = city;\n\t\tthis.name = name;\n\t\tthis.pinyin = pinyin;\n\t\tthis.py = py;\n\t\tthis.phoneCode = phoneCode;\n\t\tthis.areaCode = areaCode;\n\t\tthis.postID = postID;\n\t}\n\n\tpublic String getProvince() {\n\t\treturn province;\n\t}\n\n\tpublic void setProvince(String province) {\n\t\tthis.province = province;\n\t}\n\n\tpublic String getCity() {\n\t\treturn city;\n\t}\n\n\tpublic void setCity(String city) {\n\t\tthis.city = city;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic String getPinyin() {\n\t\treturn pinyin;\n\t}\n\n\tpublic void setPinyin(String pinyin) {\n\t\tthis.pinyin = pinyin;\n\t}\n\n\tpublic String getPy() {\n\t\treturn py;\n\t}\n\n\tpublic void setPy(String py) {\n\t\tthis.py = py;\n\t}\n\n\tpublic String getPhoneCode() {\n\t\treturn phoneCode;\n\t}\n\n\tpublic void setPhoneCode(String phoneCode) {\n\t\tthis.phoneCode = phoneCode;\n\t}\n\n\tpublic String getAreaCode() {\n\t\treturn areaCode;\n\t}\n\n\tpublic void setAreaCode(String areaCode) {\n\t\tthis.areaCode = areaCode;\n\t}\n\n\tpublic String getPostID() {\n\t\treturn postID;\n\t}\n\n\tpublic void setPostID(String postID) {\n\t\tthis.postID = postID;\n\t}\n\n\tpublic boolean getIsLocation() {\n\t\treturn isLocation == 0 ? false : true;\n\t}\n\n\tpublic void setIsLocation(int isLocation) {\n\t\tthis.isLocation = isLocation;\n\t}\n\n\tpublic long getRefreshTime() {\n\t\treturn refreshTime;\n\t}\n\n\tpublic void setRefreshTime(long refreshTime) {\n\t\tthis.refreshTime = refreshTime;\n\t}\n\n\tpublic long getPubTime() {\n\t\treturn pubTime;\n\t}\n\n\tpublic void setPubTime(long pubTime) {\n\t\tthis.pubTime = pubTime;\n\t}\n\n\tpublic String getWeatherInfoStr() {\n\t\treturn weatherInfoStr;\n\t}\n\n\tpublic void setWeatherInfoStr(String weatherInfoStr) {\n\t\tthis.weatherInfoStr = weatherInfoStr;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint result = 17;\n\t\tresult = 31 * result + postID != null ? postID.hashCode() : 0;\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (o == null)\n\t\t\treturn false;\n\t\tif (o == this)\n\t\t\treturn true;\n\t\tif (o instanceof City) {\n\t\t\tCity item = (City) o;\n\t\t\tif (item.getPostID().equals(this.postID))\n\t\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"City [province=\" + province + \", city=\" + city + \", name=\"\n\t\t\t\t+ name + \", pinyin=\" + pinyin + \", py=\" + py + \", phoneCode=\"\n\t\t\t\t+ phoneCode + \", areaCode=\" + areaCode + \", postID=\" + postID\n\t\t\t\t+ \", refreshTime=\" + refreshTime + \", isLocation=\" + isLocation\n\t\t\t\t+ \"]\";\n\t}\n\n\t@Override\n\tpublic int describeContents() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void writeToParcel(Parcel dest, int flags) {\n\t\tdest.writeString(province);\n\t\tdest.writeString(city);\n\t\tdest.writeString(name);\n\t\tdest.writeString(pinyin);\n\t\tdest.writeString(py);\n\t\tdest.writeString(phoneCode);\n\t\tdest.writeString(areaCode);\n\t\tdest.writeString(postID);\n\t\tdest.writeLong(refreshTime);\n\t\tdest.writeInt(isLocation);\n\t}\n\n\tpublic static final Parcelable.Creator<City> CREATOR = new Creator<City>() {\n\n\t\t@Override\n\t\tpublic City createFromParcel(Parcel source) {\n\t\t\tCity city = new City();\n\t\t\tcity.province = source.readString();\n\t\t\tcity.city = source.readString();\n\t\t\tcity.name = source.readString();\n\t\t\tcity.pinyin = source.readString();\n\t\t\tcity.py = source.readString();\n\t\t\tcity.phoneCode = source.readString();\n\t\t\tcity.areaCode = source.readString();\n\t\t\tcity.postID = source.readString();\n\t\t\tcity.refreshTime = source.readLong();\n\t\t\tcity.isLocation = source.readInt();\n\t\t\treturn city;\n\t\t}\n\n\t\t@Override\n\t\tpublic City[] newArray(int size) {\n\t\t\treturn new City[size];\n\t\t}\n\n\t};\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/beans/Item.java",
    "content": "package com.way.beans;\n\npublic class Item {\n\tpublic static final int INFINITE_ID = -1;\n\tpublic static final int SETTING_ID = 999;\n\tpublic static final int FEEDBACK_ID = 998;\n\tpublic static final int SHARE_ID = 995;\n\tpublic static final int ABOUT_ID = 996;\n\n\tpublic int mId;\n\tpublic String mTitleStr;\n\tpublic int mTitleRes;\n\tpublic int mIconRes;\n\n\tpublic Item(int id, String title, int iconRes) {\n\t\tmId = id;\n\t\tmTitleStr = title;\n\t\tmIconRes = iconRes;\n\t}\n\n\tpublic Item(int id, int title, int iconRes) {\n\t\tmId = id;\n\t\tmTitleRes = title;\n\t\tmIconRes = iconRes;\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/beans/MainItem.java",
    "content": "package com.way.beans;\n\nimport com.way.weather.plugin.bean.WeatherInfo;\n\npublic class MainItem {\n\tprivate City city;\n\tprivate WeatherInfo weatherInfo;\n\n\tpublic MainItem() {\n\t\tsuper();\n\t}\n\n\tpublic MainItem(City city, WeatherInfo weatherInfo) {\n\t\tsuper();\n\t\tthis.city = city;\n\t\tthis.weatherInfo = weatherInfo;\n\t}\n\n\tpublic City getCity() {\n\t\treturn city;\n\t}\n\n\tpublic void setCity(City city) {\n\t\tthis.city = city;\n\t}\n\n\tpublic WeatherInfo getWeatherInfo() {\n\t\treturn weatherInfo;\n\t}\n\n\tpublic void setWeatherInfo(WeatherInfo weatherInfo) {\n\t\tthis.weatherInfo = weatherInfo;\n\t}\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/common/util/L.java",
    "content": "package com.way.common.util;\n\nimport com.way.yahoo.BuildConfig;\n\nimport android.util.Log;\n\n/**\n * Log统一管理类\n * \n * @author way\n * \n */\npublic class L {\n\tpublic static boolean isDebug = BuildConfig.DEBUG;// 是否需要打印bug，可以在application的onCreate函数里面初始化\n\tprivate static final String TAG = \"way\";\n\n\t// 下面四个是默认tag的函数\n\tpublic static void i(String msg) {\n\t\tif (isDebug)\n\t\t\tLog.i(TAG, msg);\n\t}\n\n\tpublic static void d(String msg) {\n\t\tif (isDebug)\n\t\t\tLog.d(TAG, msg);\n\t}\n\n\tpublic static void e(String msg) {\n\t\tif (isDebug)\n\t\t\tLog.e(TAG, msg);\n\t}\n\n\tpublic static void v(String msg) {\n\t\tif (isDebug)\n\t\t\tLog.v(TAG, msg);\n\t}\n\t//下面是传入类名打印log\n\tpublic static void i(Class<?> _class,String msg){\n\t\tif (isDebug)\n\t\t\tLog.i(_class.getName(), msg);\n\t}\n\tpublic static void d(Class<?> _class,String msg){\n\t\tif (isDebug)\n\t\t\tLog.i(_class.getName(), msg);\n\t}\n\tpublic static void e(Class<?> _class,String msg){\n\t\tif (isDebug)\n\t\t\tLog.i(_class.getName(), msg);\n\t}\n\tpublic static void v(Class<?> _class,String msg){\n\t\tif (isDebug)\n\t\t\tLog.i(_class.getName(), msg);\n\t}\n\t// 下面是传入自定义tag的函数\n\tpublic static void i(String tag, String msg) {\n\t\tif (isDebug)\n\t\t\tLog.i(tag, msg);\n\t}\n\n\tpublic static void d(String tag, String msg) {\n\t\tif (isDebug)\n\t\t\tLog.i(tag, msg);\n\t}\n\n\tpublic static void e(String tag, String msg) {\n\t\tif (isDebug)\n\t\t\tLog.i(tag, msg);\n\t}\n\n\tpublic static void v(String tag, String msg) {\n\t\tif (isDebug)\n\t\t\tLog.i(tag, msg);\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/common/util/LocationUtils.java",
    "content": "package com.way.common.util;\n\nimport android.content.Context;\nimport android.text.TextUtils;\n\nimport com.baidu.location.BDLocation;\nimport com.baidu.location.BDLocationListener;\nimport com.baidu.location.LocationClient;\nimport com.baidu.location.LocationClientOption;\nimport com.baidu.location.LocationClientOption.LocationMode;\n\npublic class LocationUtils {\n\tprivate static final int MAX_TRY_COUNT = 5;\n\tprivate LocationClient mLocationClient = null;\n\tprivate LocationListener mListener;\n\tprivate int mTryCount;\n\n\tpublic static abstract interface LocationListener {\n\t\tpublic abstract void detecting();\n\n\t\tpublic abstract void succeed(String city);\n\n\t\tpublic abstract void failed();\n\t}\n\n\tpublic LocationUtils(Context context, LocationListener listener) {\n\t\tif (listener == null)\n\t\t\tnew NullPointerException(\"LocationListener can't be null\");\n\t\tmListener = listener;\n\t\tmLocationClient = new LocationClient(context,\n\t\t\t\tgetLocationClientOption(context));\n\t\tmLocationClient.registerLocationListener(mLocationListener);\n\t}\n\n\t// 开始定位\n\tpublic void startLocation() {\n\t\tmLocationClient.start();\n\t\tmListener.detecting();\n\t\tmTryCount = 0;\n\t}\n\n\t// 结束定位\n\tpublic void stopLocation() {\n\t\tmLocationClient.stop();\n\t\tmTryCount = 0;\n\t}\n\n\tpublic boolean isStarted() {\n\t\treturn mLocationClient.isStarted();\n\t}\n\n\tprivate LocationClientOption getLocationClientOption(Context context) {\n\t\tLocationClientOption option = new LocationClientOption();\n\t\toption.setOpenGps(true);\n\t\toption.setLocationMode(LocationMode.Hight_Accuracy);// 设置定位模式\n\t\toption.setCoorType(\"gcj02\");// 返回的定位结果是百度经纬度，默认值gcj02\n\t\toption.setScanSpan(1000);// 设置发起定位请求的间隔时间为1000ms\n\t\toption.setProdName(context.getPackageName());\n\t\toption.setIsNeedAddress(true);\n\t\treturn option;\n\t}\n\n\t/**\n\t * 实现定位回调监听\n\t */\n\tBDLocationListener mLocationListener = new BDLocationListener() {\n\n\t\t@Override\n\t\tpublic void onReceiveLocation(BDLocation location) {\n\t\t\tif (location == null || location.getLocType() != 161\n\t\t\t\t\t|| TextUtils.isEmpty(location.getCity())) {\n\t\t\t\tmTryCount++;\n\t\t\t\tif (mTryCount >= MAX_TRY_COUNT) {\n\t\t\t\t\tmListener.failed();\n\t\t\t\t\tstopLocation();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tString city = location.getCity().replace(\"市\", \"\");\n\t\t\tmListener.succeed(city);\n\t\t\tstopLocation();// 停止定位\n\t\t}\n\n\t};\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/common/util/LunarCalendar.java",
    "content": "package com.way.common.util;\n\nimport android.content.Context;\n\nimport com.way.yahoo.R;\n\n\npublic class LunarCalendar {\n    private static String[] lunarCalendarNumber = null;\n\n    private static String[] lunarCalendarTen = null;\n\n    private static String[] year_of_birth = null;\n\n    private static String[] lunarTerm = null;\n\n    private static String lunarLeapTag = null, lunarMonthTag = null,\n            zhengyueTag = null;\n\n    Context mContext;\n\n    public int lunarYear = 0;\n\n    public int lunarMonth = 0;\n\n    public int lunarDay = 0;\n\n    public int solarYear = 0;\n\n    public int solarMonth = 0;\n\n    public int solarDay = 0;\n\n    public boolean isLeapMonth = false;\n\n    public boolean isFastival = false;\n\n    /* SPRD: bug257032 2013-12-26 special solar term dates @{ */\n    private static String[] mSpecialSolarTermDates;\n    /* SPRD: bug257032 2013-12-26 @} */\n\n    /* SPRD: bug258885 2013-12-25 update lunar language resources info @{ */\n    private static boolean mHasInitialedRes;\n    // such as Mid-Autumn Day\n    private static String[] mTraditionalFestivalStr;\n    // such as Valentine's Day\n    private static String[] mFestivalStr;\n    // such as Jia, Yi, Bing, Ding\n    private static String[] mYearStemStr;\n    // such as Zi, Chou, Yin, Mao\n    private static String[] mYearBranchStr;\n\n    static {\n        mHasInitialedRes = false;\n    }\n\n    public LunarCalendar(Context context) {\n        mContext = context;\n        if (!mHasInitialedRes) {\n            reloadLanguageResources(context);\n            mHasInitialedRes = true;\n        }\n    }\n\n    public static void reloadLanguageResources(Context ctx) {\n        if (lunarCalendarNumber == null) {\n            lunarCalendarNumber = new String[12];\n        }\n        lunarCalendarNumber[0] = getString(ctx, R.string.chineseNumber1);\n        lunarCalendarNumber[1] = getString(ctx, R.string.chineseNumber2);\n        lunarCalendarNumber[2] = getString(ctx, R.string.chineseNumber3);\n        lunarCalendarNumber[3] = getString(ctx, R.string.chineseNumber4);\n        lunarCalendarNumber[4] = getString(ctx, R.string.chineseNumber5);\n        lunarCalendarNumber[5] = getString(ctx, R.string.chineseNumber6);\n        lunarCalendarNumber[6] = getString(ctx, R.string.chineseNumber7);\n        lunarCalendarNumber[7] = getString(ctx, R.string.chineseNumber8);\n        lunarCalendarNumber[8] = getString(ctx, R.string.chineseNumber9);\n        lunarCalendarNumber[9] = getString(ctx, R.string.chineseNumber10);\n        lunarCalendarNumber[10] = getString(ctx, R.string.chineseNumber11);\n        lunarCalendarNumber[11] = getString(ctx, R.string.chineseNumber12);\n\n        if (lunarCalendarTen == null) {\n            lunarCalendarTen = new String[5];\n        }\n        lunarCalendarTen[0] = getString(ctx, R.string.chineseTen0);\n        lunarCalendarTen[1] = getString(ctx, R.string.chineseTen1);\n        lunarCalendarTen[2] = getString(ctx, R.string.chineseTen2);\n        lunarCalendarTen[3] = getString(ctx, R.string.chineseTen3);\n        lunarCalendarTen[4] = getString(ctx, R.string.chineseTen4);\n\n        if (year_of_birth == null) {\n            year_of_birth = new String[12];\n        }\n        year_of_birth[0] = getString(ctx, R.string.animals0);\n        year_of_birth[1] = getString(ctx, R.string.animals1);\n        year_of_birth[2] = getString(ctx, R.string.animals2);\n        year_of_birth[3] = getString(ctx, R.string.animals3);\n        year_of_birth[4] = getString(ctx, R.string.animals4);\n        year_of_birth[5] = getString(ctx, R.string.animals5);\n        year_of_birth[6] = getString(ctx, R.string.animals6);\n        year_of_birth[7] = getString(ctx, R.string.animals7);\n        year_of_birth[8] = getString(ctx, R.string.animals8);\n        year_of_birth[9] = getString(ctx, R.string.animals9);\n        year_of_birth[10] = getString(ctx, R.string.animals10);\n        year_of_birth[11] = getString(ctx, R.string.animals11);\n\n        lunarLeapTag = getString(ctx, R.string.leap_month);\n        lunarMonthTag = getString(ctx, R.string.month);\n        zhengyueTag = getString(ctx, R.string.zheng);\n\n        if (lunarTerm == null) {\n            lunarTerm = new String[24];\n        }\n        lunarTerm[0] = getString(ctx, R.string.terms0);\n        lunarTerm[1] = getString(ctx, R.string.terms1);\n        lunarTerm[2] = getString(ctx, R.string.terms2);\n        lunarTerm[3] = getString(ctx, R.string.terms3);\n        lunarTerm[4] = getString(ctx, R.string.terms4);\n        lunarTerm[5] = getString(ctx, R.string.terms5);\n        lunarTerm[6] = getString(ctx, R.string.terms6);\n        lunarTerm[7] = getString(ctx, R.string.terms7);\n        lunarTerm[8] = getString(ctx, R.string.terms8);\n        lunarTerm[9] = getString(ctx, R.string.terms9);\n        lunarTerm[10] = getString(ctx, R.string.terms10);\n        lunarTerm[11] = getString(ctx, R.string.terms11);\n        lunarTerm[12] = getString(ctx, R.string.terms12);\n        lunarTerm[13] = getString(ctx, R.string.terms13);\n        lunarTerm[14] = getString(ctx, R.string.terms14);\n        lunarTerm[15] = getString(ctx, R.string.terms15);\n        lunarTerm[16] = getString(ctx, R.string.terms16);\n        lunarTerm[17] = getString(ctx, R.string.terms17);\n        lunarTerm[18] = getString(ctx, R.string.terms18);\n        lunarTerm[19] = getString(ctx, R.string.terms19);\n        lunarTerm[20] = getString(ctx, R.string.terms20);\n        lunarTerm[21] = getString(ctx, R.string.terms21);\n        lunarTerm[22] = getString(ctx, R.string.terms22);\n        lunarTerm[23] = getString(ctx, R.string.terms23);\n\n        if (mTraditionalFestivalStr == null) {\n            mTraditionalFestivalStr = new String[9];\n        }\n        mTraditionalFestivalStr[0] = getString(ctx, R.string.chunjie);\n        mTraditionalFestivalStr[1] = getString(ctx, R.string.yuanxiao);\n        mTraditionalFestivalStr[2] = getString(ctx, R.string.duanwu);\n        mTraditionalFestivalStr[3] = getString(ctx, R.string.qixi);\n        mTraditionalFestivalStr[4] = getString(ctx, R.string.zhongqiu);\n        mTraditionalFestivalStr[5] = getString(ctx, R.string.chongyang);\n        mTraditionalFestivalStr[6] = getString(ctx, R.string.laba);\n        mTraditionalFestivalStr[7] = getString(ctx, R.string.xiaonian);\n        mTraditionalFestivalStr[8] = getString(ctx, R.string.chuxi);\n\n        if (mFestivalStr == null) {\n            mFestivalStr = new String[12];\n        }\n        mFestivalStr[0] = getString(ctx, R.string.new_Year_day);\n        mFestivalStr[1] = getString(ctx, R.string.valentin_day);\n        mFestivalStr[2] = getString(ctx, R.string.women_day);\n        mFestivalStr[3] = getString(ctx, R.string.arbor_day);\n        mFestivalStr[4] = getString(ctx, R.string.labol_day);\n        mFestivalStr[5] = getString(ctx, R.string.youth_day);\n        mFestivalStr[6] = getString(ctx, R.string.children_day);\n        mFestivalStr[7] = getString(ctx, R.string.Communist_day);\n        mFestivalStr[8] = getString(ctx, R.string.army_day);\n        mFestivalStr[9] = getString(ctx, R.string.teacher_day);\n        mFestivalStr[10] = getString(ctx, R.string.national_day);\n        mFestivalStr[11] = getString(ctx, R.string.christmas_day);\n\n        if (mYearStemStr == null) {\n            mYearStemStr = new String[10];\n        }\n        mYearStemStr[0] = getString(ctx, R.string.jia);\n        mYearStemStr[1] = getString(ctx, R.string.yi);\n        mYearStemStr[2] = getString(ctx, R.string.bing);\n        mYearStemStr[3] = getString(ctx, R.string.ding);\n        mYearStemStr[4] = getString(ctx, R.string.wutian);\n        mYearStemStr[5] = getString(ctx, R.string.ji);\n        mYearStemStr[6] = getString(ctx, R.string.geng);\n        mYearStemStr[7] = getString(ctx, R.string.xin);\n        mYearStemStr[8] = getString(ctx, R.string.ren);\n        mYearStemStr[9] = getString(ctx, R.string.gui);\n\n        if (mYearBranchStr == null) {\n            mYearBranchStr = new String[12];\n        }\n        mYearBranchStr[0] = getString(ctx, R.string.zi);\n        mYearBranchStr[1] = getString(ctx, R.string.chou);\n        mYearBranchStr[2] = getString(ctx, R.string.yin);\n        mYearBranchStr[3] = getString(ctx, R.string.mao);\n        mYearBranchStr[4] = getString(ctx, R.string.chen);\n        mYearBranchStr[5] = getString(ctx, R.string.si);\n        mYearBranchStr[6] = getString(ctx, R.string.wudi);\n        mYearBranchStr[7] = getString(ctx, R.string.wei);\n        mYearBranchStr[8] = getString(ctx, R.string.shen);\n        mYearBranchStr[9] = getString(ctx, R.string.you);\n        mYearBranchStr[10] = getString(ctx, R.string.xu);\n        mYearBranchStr[11] = getString(ctx, R.string.hai);\n\n        /* SPRD: bug257032 2013-12-26 special solar term dates @{ */\n        if (mSpecialSolarTermDates == null) {\n            mSpecialSolarTermDates = ctx.getResources()\n                .getStringArray(R.array.special_solar_term_dates);\n        }\n        /* SPRD: bug257032 2013-12-26 */\n    }\n\n    public static void clearLanguageResourcesRefs() {\n        lunarCalendarNumber = null;\n        lunarCalendarTen = null;\n        year_of_birth = null;\n        lunarTerm = null;\n        mTraditionalFestivalStr = null;\n        mFestivalStr = null;\n        mYearStemStr = null;\n        mYearBranchStr = null;\n\n        mHasInitialedRes = false;\n\n        // SPRD: bug257032 2013-12-26 special solar term dates\n        mSpecialSolarTermDates = null;\n    }\n\n    private static String getString(Context ctx, int resId) {\n        return ctx.getString(resId);\n    }\n\n    public String getTraditionalFestival() {\n        return getTraditionalFestival(lunarYear, lunarMonth, lunarDay);\n    }\n\n    public String getTraditionalFestival(int lunarYear, int lunarMonth,\n            int lunarDay) {\n        /* SPRD: bug254439 delete duplicatin traditional festival string @{ */\n        // if is leap month, return empty string\n        if (isLeapMonth) {\n            return \"\";\n        }\n        /* @} */\n        String festivalStr = \"\";\n        if (lunarMonth == 1 && lunarDay == 1)\n            festivalStr = mTraditionalFestivalStr[0];\n        if (lunarMonth == 1 && lunarDay == 15)\n            festivalStr = mTraditionalFestivalStr[1];\n        if (lunarMonth == 5 && lunarDay == 5)\n            festivalStr = mTraditionalFestivalStr[2];\n        if (lunarMonth == 7 && lunarDay == 7)\n            festivalStr = mTraditionalFestivalStr[3];\n        if (lunarMonth == 8 && lunarDay == 15)\n            festivalStr = mTraditionalFestivalStr[4];\n        if (lunarMonth == 9 && lunarDay == 9)\n            festivalStr = mTraditionalFestivalStr[5];\n        if (lunarMonth == 12 && lunarDay == 8)\n            festivalStr = mTraditionalFestivalStr[6];\n        if (lunarMonth == 12 && lunarDay == 23)\n            festivalStr = mTraditionalFestivalStr[7];\n\n        if (lunarMonth == 12) {\n            if (lunarDay == LunarCalendarConvertUtil.getLunarMonthDays(\n                    lunarYear, lunarMonth))\n                festivalStr = mTraditionalFestivalStr[8];\n        }\n        return festivalStr;\n    }\n\n    public String getFestival() {\n        return getFestival(solarMonth, solarDay);\n    }\n\n    private String getFestival(int lunarMonth, int lunarDay) {\n        String festivalStr = \"\";\n        if (lunarMonth == 0 && lunarDay == 1)\n            festivalStr = mFestivalStr[0];\n        if (lunarMonth == 1 && lunarDay == 14)\n            festivalStr = mFestivalStr[1];\n        if (lunarMonth == 2 && lunarDay == 8)\n            festivalStr = mFestivalStr[2];\n        if (lunarMonth == 2 && lunarDay == 12)\n            festivalStr = mFestivalStr[3];\n        if (lunarMonth == 4 && lunarDay == 1)\n            festivalStr = mFestivalStr[4];\n        if (lunarMonth == 4 && lunarDay == 4)\n            festivalStr = mFestivalStr[5];\n        if (lunarMonth == 5 && lunarDay == 1)\n            festivalStr = mFestivalStr[6];\n        /** add 20130702 spreadst of 181042 no communist day start */\n        if (lunarMonth == 6 && lunarDay == 1)\n            festivalStr = mFestivalStr[7];\n        /** add 20130702 spreadst of 181042 no communist day end */\n        if (lunarMonth == 7 && lunarDay == 1)\n            festivalStr = mFestivalStr[8];\n        if (lunarMonth == 8 && lunarDay == 10)\n            festivalStr = mFestivalStr[9];\n        if (lunarMonth == 9 && lunarDay == 1)\n            festivalStr = mFestivalStr[10];\n        if (lunarMonth == 11 && lunarDay == 25)\n            festivalStr = mFestivalStr[11];\n        return festivalStr;\n    }\n\n    /* SPRD: bug254474 correct the algorithm of getting solar terms @{ */\n    /* SPRD: bug257032 2013-12-26 special solar term dates @{ */\n    private String getSolarTerm(int year, int month, int date) {\n        String termStr = \"\";\n        SpecialSolarTermInfo info = getSpecialSolarTermInfo(year, month, date);\n        if (info != null && info.mIndex != -1) {\n            if (info.mIndex != 0) {\n                termStr = info.mTermStr;\n            } // else info.mIndex == 0, then this should return empty string\n        } else {\n            if (date == LunarCalendarConvertUtil.getSolarTermDayOfMonth(year,\n                    month * 2)) {\n                termStr = lunarTerm[month * 2];\n            } else if (date == LunarCalendarConvertUtil.getSolarTermDayOfMonth(\n                    year, month * 2 + 1)) {\n                termStr = lunarTerm[month * 2 + 1];\n            }\n        }\n        return termStr;\n    }\n    /* SPRD: bug257032 2013-12-26 @} */\n    /* @} */\n\n    private String getChinaMonthString() {\n        return getChinaMonthString(lunarMonth, isLeapMonth);\n    }\n\n    private String getChinaMonthString(int lunarMonth, boolean isLeapMonth) {\n        String chinaMonth = (isLeapMonth ? lunarLeapTag : \"\")\n                + ((lunarMonth == 1) ? zhengyueTag\n                        : lunarCalendarNumber[lunarMonth - 1]) + lunarMonthTag;\n        return chinaMonth;\n    }\n\n    private String getChinaDayString(boolean notDisplayLunarMonthForFirstDay) {\n        return getChinaDayString(lunarMonth, lunarDay, isLeapMonth,\n                notDisplayLunarMonthForFirstDay);\n    }\n\n    public String getChinaDayString(int lunarMonth, int lunarDay,\n            boolean isLeapMonth, boolean notDisplayLunarMonthForFirstDay) {\n        if (lunarDay > 30)\n            return \"\";\n        if (lunarDay == 1 && notDisplayLunarMonthForFirstDay)\n            return getChinaMonthString(lunarMonth, isLeapMonth);\n        if (lunarDay == 10)\n            return lunarCalendarTen[0] + lunarCalendarTen[1];\n        if (lunarDay == 20)\n            return lunarCalendarTen[4] + lunarCalendarTen[1];\n\n        return lunarCalendarTen[lunarDay / 10]\n                + lunarCalendarNumber[(lunarDay + 9) % 10];\n    }\n\n    private String getChinaYearString() {\n        return getChinaYearString(lunarYear);\n    }\n\n    private String getChinaYearString(int lunarYear) {\n        return String.valueOf(lunarYear);\n    }\n\n    private String getLunarYearString(int num) {\n        return (mYearStemStr[num % 10] + mYearBranchStr[num % 12]);\n    }\n    /* SPRD: bug258885 2013-12-25 @} */\n\n    public String getLunarYear(int year) {\n        int num = year - 1900 + 36;\n        return getLunarYearString(num);\n    }\n\n    public String animalsYear(int year) {\n        return year_of_birth[(year - 4) % 12];\n    }\n\n    public String[] getLunarCalendarInfo(boolean notDisplayLunarMonthForFirstDay) {\n        if (lunarYear == 0 || lunarMonth == 0 || lunarDay == 0)\n            return null;// new String[]{null,null,null,null,null};\n        String lunarYearStr = getChinaYearString();\n        String lunarMonthStr = getChinaMonthString();\n        String lunarDayStr = getChinaDayString(notDisplayLunarMonthForFirstDay);\n\n        String traditionFestivalStr = getTraditionalFestival();\n        String festivalStr = getFestival();\n        // SPRD: bug254474 correct the algorithm of getting solar terms\n        String solarTermStr = getSolarTerm(solarYear, solarMonth, solarDay);\n\n        return new String[] { lunarYearStr, lunarMonthStr, lunarDayStr,\n                traditionFestivalStr, festivalStr, solarTermStr };\n    }\n\n    public String getLunarDayInfo() {\n        if (lunarYear == 0 || lunarMonth == 0 || lunarDay == 0) {\n            return \"\";\n        }\n        // if this day is traditional festival, show as it\n        String traditionFestivalStr = getTraditionalFestival();\n        String festivalStr = getFestival();\n        // SPRD: bug254474 correct the algorithm of getting solar terms\n        String solarTermStr = getSolarTerm(solarYear, solarMonth, solarDay);\n        /*add 20130703 Spreadst of 176738 the color error start*/\n        if (!traditionFestivalStr.trim().equals(\"\")\n                || !festivalStr.trim().equals(\"\")\n                || !solarTermStr.trim().equals(\"\")) {\n            isFastival = true;\n        } else {\n            isFastival = false;\n        }\n        /* add 20130703 Spreadst of 176738 the color error end */\n        if (traditionFestivalStr != null && festivalStr != null\n                && !traditionFestivalStr.trim().equals(\"\")\n                && !festivalStr.trim().equals(\"\")) {\n            return traditionFestivalStr + \"/\" + festivalStr;\n        }\n\n        if (traditionFestivalStr != null && solarTermStr != null\n                && !traditionFestivalStr.trim().equals(\"\")\n                && !solarTermStr.trim().equals(\"\")) {\n            return traditionFestivalStr + \"/\" + solarTermStr;\n        }\n\n        if (festivalStr != null && solarTermStr != null\n                && !festivalStr.trim().equals(\"\")\n                && !solarTermStr.trim().equals(\"\")) {\n            return festivalStr + \"/\" + solarTermStr;\n        }\n\n        if (traditionFestivalStr != null\n                && !traditionFestivalStr.trim().equals(\"\")) {\n            return traditionFestivalStr;\n        }\n\n        // if this day is festival, show as it\n        if (festivalStr != null && !festivalStr.trim().equals(\"\")) {\n            return festivalStr;\n        }\n\n        // if this day is solar term, show as it\n        if (solarTermStr != null && !solarTermStr.trim().equals(\"\")) {\n            return solarTermStr;\n        }\n\n        // if this day is first day of lunar month, show lunar month number\n        String lunarMonthStr = getChinaMonthString();\n        if (lunarDay == 1) {\n            return lunarMonthStr;\n        }\n\n        // otherwise, show lunar day number\n        String lunarDayStr = getChinaDayString(false);\n        return lunarDayStr;\n\n    }\n\n    /* SPRD: bug257032 2013-12-26 special solar term dates @{ */\n    final static class SpecialSolarTermInfo {\n        String mSpecialStr;\n        String mTermStr;\n        int mIndex;\n\n        SpecialSolarTermInfo(String specialStr, String termStr,\n                int index) {\n            mSpecialStr = specialStr;\n            mTermStr = termStr;\n            mIndex = index;\n        }\n    }\n\n    private static SpecialSolarTermInfo getSpecialSolarTermInfo(int year,\n            int month, int day) {\n        SpecialSolarTermInfo info = null;\n        if (mSpecialSolarTermDates != null) {\n            // out date format will be xxxxxxxx, eg. 20131221, length equals 8\n            StringBuilder dateStrBuilder = new StringBuilder(8);\n            dateStrBuilder.setLength(0);\n            dateStrBuilder.append(year);\n            // month is from 0-11\n            if (month < 9) {\n                dateStrBuilder.append(0);\n            }\n            dateStrBuilder.append(month + 1);\n            // day is from 1-31\n            if (day < 10) {\n                dateStrBuilder.append(0);\n            }\n            dateStrBuilder.append(day);\n            //Log.d(\"chen\", \"current date str: \" + dateStrBuilder.toString());\n            int index;\n            String term = \"\";\n            for (String dateStr : mSpecialSolarTermDates) {\n                index = dateStr.indexOf(dateStrBuilder.toString());\n                if (index != -1) {\n                    term = lunarTerm[Integer.valueOf(dateStr\n                        .substring(dateStr.lastIndexOf('|') + 1))];\n                    info = new SpecialSolarTermInfo(dateStr, term,\n                            index);\n                    break;\n                }\n            }\n        }\n        return info;\n    }\n    /* SPRD: bug257032 2013-12-26 @} */\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/common/util/LunarCalendarConvertUtil.java",
    "content": "package com.way.common.util;\n\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.Locale;\n\n\npublic class LunarCalendarConvertUtil {\n\tprivate final static short[] lunarCalendarBaseInfo = new short[] { 0x4bd,\n\t\t\t0x4ae, 0xa57, 0x54d, 0xd26, 0xd95, 0x655, 0x56a, 0x9ad, 0x55d,\n\t\t\t0x4ae, 0xa5b, 0xa4d, 0xd25, 0xd25, 0xb54, 0xd6a, 0xada, 0x95b,\n\t\t\t0x497, 0x497, 0xa4b, 0xb4b, 0x6a5, 0x6d4, 0xab5, 0x2b6, 0x957,\n\t\t\t0x52f, 0x497, 0x656, 0xd4a, 0xea5, 0x6e9, 0x5ad, 0x2b6, 0x86e,\n\t\t\t0x92e, 0xc8d, 0xc95, 0xd4a, 0xd8a, 0xb55, 0x56a, 0xa5b, 0x25d,\n\t\t\t0x92d, 0xd2b, 0xa95, 0xb55, 0x6ca, 0xb55, 0x535, 0x4da, 0xa5d,\n\t\t\t0x457, 0x52d, 0xa9a, 0xe95, 0x6aa, 0xaea, 0xab5, 0x4b6, 0xaae,\n\t\t\t0xa57, 0x526, 0xf26, 0xd95, 0x5b5, 0x56a, 0x96d, 0x4dd, 0x4ad,\n\t\t\t0xa4d, 0xd4d, 0xd25, 0xd55, 0xb54, 0xb5a, 0x95a, 0x95b, 0x49b,\n\t\t\t0xa97, 0xa4b, 0xb27, 0x6a5, 0x6d4, 0xaf4, 0xab6, 0x957, 0x4af,\n\t\t\t0x497, 0x64b, 0x74a, 0xea5, 0x6b5, 0x55c, 0xab6, 0x96d, 0x92e,\n\t\t\t0xc96, 0xd95, 0xd4a, 0xda5, 0x755, 0x56a, 0xabb, 0x25d, 0x92d,\n\t\t\t0xcab, 0xa95, 0xb4a, 0xbaa, 0xad5, 0x55d, 0x4ba, 0xa5b, 0x517,\n\t\t\t0x52b, 0xa93, 0x795, 0x6aa, 0xad5, 0x5b5, 0x4b6, 0xa6e, 0xa4e,\n\t\t\t0xd26, 0xea6, 0xd53, 0x5aa, 0x76a, 0x96d, 0x4bd, 0x4ad, 0xa4d,\n\t\t\t0xd0b, 0xd25, 0xd52, 0xdd4, 0xb5a, 0x56d, 0x55b, 0x49b, 0xa57,\n\t\t\t0xa4b, 0xaa5, 0xb25, 0x6d2, 0xada };\n\tprivate final static byte[] lunarCalendarSpecialInfo = new byte[] { 0x08,\n\t\t\t0x00, 0x00, 0x05, 0x00, 0x00, 0x14, 0x00, 0x00, 0x02, 0x00, 0x06,\n\t\t\t0x00, 0x00, 0x15, 0x00, 0x00, 0x02, 0x00, 0x17, 0x00, 0x00, 0x05,\n\t\t\t0x00, 0x00, 0x14, 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, 0x00, 0x05,\n\t\t\t0x00, 0x00, 0x13, 0x00, 0x17, 0x00, 0x00, 0x16, 0x00, 0x00, 0x14,\n\t\t\t0x00, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, 0x15, 0x00, 0x00, 0x13,\n\t\t\t0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03,\n\t\t\t0x00, 0x07, 0x00, 0x00, 0x05, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00,\n\t\t\t0x00, 0x16, 0x00, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x06, 0x00,\n\t\t\t0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x05, 0x00,\n\t\t\t0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, 0x05, 0x00,\n\t\t\t0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x16, 0x00, 0x00, 0x04, 0x00,\n\t\t\t0x00, 0x02, 0x00, 0x06, 0x00, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00,\n\t\t\t0x07, 0x00, 0x00, 0x16, 0x00, 0x00, 0x05, 0x00, 0x00, 0x02, 0x00,\n\t\t\t0x07, 0x00, 0x00, 0x15, 0x00, 0x00 };\n\n\t/* SPRD: bug254474 correct the algorithm of getting solar terms @{ */\n\tprivate final static long[] mSolarTermInfo = new long[] { 0, 21208, 42467,\n\t\t\t63836, 85337, 107014, 128867, 150921, 173149, 195551, 218072,\n\t\t\t240693, 263343, 285989, 308563, 331033, 353350, 375494, 397447,\n\t\t\t419210, 440795, 462224, 483532, 504758 };\n\n\tprivate final static Calendar mOffDateCalendar;\n\tprivate final static long mMilliSecondsForSolarTerm;\n\t/* @} */\n\n\tprivate final static int baseYear = 1900;\n\n\tprivate final static int outBoundYear = 2050;\n\n\tprivate static long baseDayTime = 0;\n\n\tprivate final static int bigMonthDays = 30;\n\n\tprivate final static int smallMonthDays = 29;\n\n\tstatic {\n\t\t/* SPRD: bug251911 lunar algorithm error @{ */\n\t\t// use Date.getTime to return milliseconds for we don't need timezone\n\t\t// info\n\t\t// Date(0, 0, 31) represent 1900-1-31, it is the first day of Gengzi\n\t\t// year\n\t\t// in lunar\n\t\tbaseDayTime = new Date(0, 0, 31).getTime();\n\t\t/* SPRD: bug254474 correct the algorithm of getting solar terms @{ */\n\t\tmOffDateCalendar = Calendar.getInstance();\n\t\tmOffDateCalendar.set(1900, 0, 6, 2, 5, 0);\n\t\tmMilliSecondsForSolarTerm = mOffDateCalendar.getTime().getTime();\n\t\t/* @} */\n\t}\n\n\t/* SPRD: bug254474 correct the algorithm of getting solar terms @{ */\n\tpublic static int getSolarTermDayOfMonth(int year, int n) {\n\t\tmOffDateCalendar\n\t\t\t\t.setTime(new Date(\n\t\t\t\t\t\t(long) ((31556925974.7 * (year - 1900) + mSolarTermInfo[n] * 60000L) + mMilliSecondsForSolarTerm)));\n\t\treturn mOffDateCalendar.get(Calendar.DAY_OF_MONTH);\n\t}\n\n\t/* @} */\n\n\tpublic static int getLunarMonthDays(int lunarYear, int lunarMonth) {\n\t\tif (isLunarBigMonth(lunarYear, lunarMonth))\n\t\t\treturn bigMonthDays;\n\t\telse\n\t\t\treturn smallMonthDays;\n\t}\n\n\tpublic static boolean isLunarBigMonth(int lunarYear, int lunarMonth) {\n\t\tshort lunarYearBaseInfo = lunarCalendarBaseInfo[lunarYear - baseYear];\n\t\tif ((lunarYearBaseInfo & (0x01000 >>> lunarMonth)) != 0)\n\t\t\treturn true;\n\t\telse\n\t\t\treturn false;\n\t}\n\n\tfinal public static int getYearDays(int lunarYear) {\n\t\tint retSum = 0;\n\t\tfor (int iLunarMonth = 1; iLunarMonth <= 12; iLunarMonth++) {\n\t\t\tretSum += getLunarMonthDays(lunarYear, iLunarMonth);\n\t\t}\n\t\treturn (retSum + getLeapMonthDays(lunarYear));\n\t}\n\n\tfinal public static int getLeapMonth(int lunarYear) {\n\t\treturn lunarCalendarSpecialInfo[lunarYear - baseYear] & 0xf;\n\t}\n\n\tfinal public static int getLeapMonthDays(int lunarYear) {\n\t\tif (getLeapMonth(lunarYear) == 0)\n\t\t\treturn 0;\n\t\telse if ((lunarCalendarSpecialInfo[lunarYear - baseYear] & 0x10) != 0)\n\t\t\treturn bigMonthDays;\n\t\telse\n\t\t\treturn smallMonthDays;\n\t}\n\n\tpublic static void parseLunarCalendar(int year, int month, int day,\n\t\t\tLunarCalendar lunarCalendar) {\n\t\tif (lunarCalendar == null)\n\t\t\treturn;\n\n\t\tint leapLunarMonth = 0;\n\n\t\tDate presentDate = null;\n\n\t\tboolean isLeapMonth = false;\n\n\t\t/* SPRD: bug251911 lunar algorithm error @{ */\n\t\tpresentDate = new Date(year - 1900, month, day);\n\n\t\t// we use Math.ceil() here because offsetDayNum some time be truncate\n\t\t// this will cause we lost one day\n\t\tint offsetDayNum = (int) Math\n\t\t\t\t.ceil((presentDate.getTime() - baseDayTime) * 1.0 / 86400000L);\n\t\t/* @} */\n\n\t\tint lunarYear = 0;\n\t\tint lunarMonth = 0;\n\t\tint lunarDay = 0;\n\n\t\tfor (lunarYear = baseYear; lunarYear < outBoundYear; lunarYear++) {\n\t\t\tint daysOfLunarYear = getYearDays(lunarYear);\n\t\t\tif (offsetDayNum < daysOfLunarYear)\n\t\t\t\tbreak;\n\t\t\toffsetDayNum -= daysOfLunarYear;\n\t\t}\n\t\tif (offsetDayNum < 0 || lunarYear == outBoundYear)\n\t\t\treturn;\n\n\t\tleapLunarMonth = getLeapMonth(lunarYear);\n\n\t\tfor (lunarMonth = 1; lunarMonth <= 12; lunarMonth++) {\n\t\t\tint daysOfLunarMonth = 0;\n\t\t\tif (isLeapMonth)\n\t\t\t\tdaysOfLunarMonth = getLeapMonthDays(lunarYear);\n\t\t\telse\n\t\t\t\tdaysOfLunarMonth = getLunarMonthDays(lunarYear, lunarMonth);\n\n\t\t\tif (offsetDayNum < daysOfLunarMonth)\n\t\t\t\tbreak;\n\t\t\telse {\n\t\t\t\toffsetDayNum -= daysOfLunarMonth;\n\t\t\t\tif (lunarMonth == leapLunarMonth) {\n\t\t\t\t\tif (!isLeapMonth) {\n\t\t\t\t\t\tlunarMonth--;\n\t\t\t\t\t\tisLeapMonth = true;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tisLeapMonth = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlunarDay = offsetDayNum + 1;\n\n\t\tlunarCalendar.lunarYear = lunarYear;\n\t\tlunarCalendar.lunarMonth = lunarMonth;\n\t\tlunarCalendar.lunarDay = lunarDay;\n\t\tlunarCalendar.isLeapMonth = isLeapMonth;\n\n\t\tlunarCalendar.solarYear = year;\n\t\tlunarCalendar.solarMonth = month;\n\t\tlunarCalendar.solarDay = day;\n\t}\n\n\tpublic static boolean isLunarSetting() {\n\t\tString language = getLanguageEnv();\n\n\t\tif (language != null\n\t\t\t\t&& (language.trim().equals(\"zh-CN\") || language.trim().equals(\n\t\t\t\t\t\t\"zh-TW\")))\n\t\t\treturn true;\n\t\telse\n\t\t\treturn false;\n\t}\n\n\tprivate static String getLanguageEnv() {\n\t\tLocale l = Locale.getDefault();\n\t\tString language = l.getLanguage();\n\t\tString country = l.getCountry().toLowerCase();\n\t\tif (\"zh\".equals(language)) {\n\t\t\tif (\"cn\".equals(country)) {\n\t\t\t\tlanguage = \"zh-CN\";\n\t\t\t} else if (\"tw\".equals(country)) {\n\t\t\t\tlanguage = \"zh-TW\";\n\t\t\t}\n\t\t} else if (\"pt\".equals(language)) {\n\t\t\tif (\"br\".equals(country)) {\n\t\t\t\tlanguage = \"pt-BR\";\n\t\t\t} else if (\"pt\".equals(country)) {\n\t\t\t\tlanguage = \"pt-PT\";\n\t\t\t}\n\t\t}\n\t\treturn language;\n\t}\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/common/util/NetUtil.java",
    "content": "package com.way.common.util;\r\n\r\nimport android.content.Context;\r\nimport android.net.ConnectivityManager;\r\nimport android.net.NetworkInfo.State;\r\n\r\npublic class NetUtil {\r\n\tpublic static final int NETWORN_NONE = 0;\r\n\tpublic static final int NETWORN_WIFI = 1;\r\n\tpublic static final int NETWORN_MOBILE = 2;\r\n\r\n\tpublic static int getNetworkState(Context context) {\r\n\t\tConnectivityManager connManager = (ConnectivityManager) context\r\n\t\t\t\t.getSystemService(Context.CONNECTIVITY_SERVICE);\r\n\r\n\t\t// Wifi\r\n\t\tState state = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI)\r\n\t\t\t\t.getState();\r\n\t\tif (state == State.CONNECTED || state == State.CONNECTING) {\r\n\t\t\treturn NETWORN_WIFI;\r\n\t\t}\r\n\r\n\t\t// 3G\r\n\t\tstate = connManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE)\r\n\t\t\t\t.getState();\r\n\t\tif (state == State.CONNECTED || state == State.CONNECTING) {\r\n\t\t\treturn NETWORN_MOBILE;\r\n\t\t}\r\n\t\treturn NETWORN_NONE;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "WayHoo/src/com/way/common/util/PreferenceUtils.java",
    "content": "package com.way.common.util;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.content.SharedPreferences.Editor;\nimport android.preference.PreferenceManager;\n\npublic class PreferenceUtils {\n\tpublic static String getPrefString(Context context, String key,\n\t\t\tfinal String defaultValue) {\n\t\tfinal SharedPreferences settings = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(context);\n\t\treturn settings.getString(key, defaultValue);\n\t}\n\n\tpublic static void setPrefString(Context context, final String key,\n\t\t\tfinal String value) {\n\t\tfinal SharedPreferences settings = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(context);\n\t\tsettings.edit().putString(key, value).commit();\n\t}\n\n\tpublic static boolean getPrefBoolean(Context context, final String key,\n\t\t\tfinal boolean defaultValue) {\n\t\tfinal SharedPreferences settings = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(context);\n\t\treturn settings.getBoolean(key, defaultValue);\n\t}\n\n\tpublic static boolean hasKey(Context context, final String key) {\n\t\treturn PreferenceManager.getDefaultSharedPreferences(context).contains(\n\t\t\t\tkey);\n\t}\n\n\tpublic static void setPrefBoolean(Context context, final String key,\n\t\t\tfinal boolean value) {\n\t\tfinal SharedPreferences settings = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(context);\n\t\tsettings.edit().putBoolean(key, value).commit();\n\t}\n\n\tpublic static void setPrefInt(Context context, final String key,\n\t\t\tfinal int value) {\n\t\tfinal SharedPreferences settings = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(context);\n\t\tsettings.edit().putInt(key, value).commit();\n\t}\n\n\tpublic static int getPrefInt(Context context, final String key,\n\t\t\tfinal int defaultValue) {\n\t\tfinal SharedPreferences settings = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(context);\n\t\treturn settings.getInt(key, defaultValue);\n\t}\n\n\tpublic static void setPrefFloat(Context context, final String key,\n\t\t\tfinal float value) {\n\t\tfinal SharedPreferences settings = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(context);\n\t\tsettings.edit().putFloat(key, value).commit();\n\t}\n\n\tpublic static float getPrefFloat(Context context, final String key,\n\t\t\tfinal float defaultValue) {\n\t\tfinal SharedPreferences settings = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(context);\n\t\treturn settings.getFloat(key, defaultValue);\n\t}\n\n\tpublic static void setSettingLong(Context context, final String key,\n\t\t\tfinal long value) {\n\t\tfinal SharedPreferences settings = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(context);\n\t\tsettings.edit().putLong(key, value).commit();\n\t}\n\n\tpublic static long getPrefLong(Context context, final String key,\n\t\t\tfinal long defaultValue) {\n\t\tfinal SharedPreferences settings = PreferenceManager\n\t\t\t\t.getDefaultSharedPreferences(context);\n\t\treturn settings.getLong(key, defaultValue);\n\t}\n\n\tpublic static void clearPreference(Context context,\n\t\t\tfinal SharedPreferences p) {\n\t\tfinal Editor editor = p.edit();\n\t\teditor.clear();\n\t\teditor.commit();\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/common/util/SystemUtils.java",
    "content": "package com.way.common.util;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.InputStream;\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport android.app.Activity;\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.os.Environment;\nimport android.util.DisplayMetrics;\nimport android.view.View;\nimport android.view.Window;\nimport android.view.WindowManager;\nimport android.view.WindowManager.LayoutParams;\n\nimport com.way.beans.City;\nimport com.way.db.CityProvider;\nimport com.way.db.CityProvider.CityConstants;\n\npublic class SystemUtils {\n\n\t/**\n\t * 获取临时城市数组\n\t * \n\t * @param c\n\t * @return\n\t */\n\tpublic static List<City> getTmpCities(Cursor c) {\n\t\tList<City> list = new ArrayList<City>();\n\t\tif (c == null || c.getCount() == 0)\n\t\t\treturn list;\n\t\twhile (c.moveToNext()) {\n\t\t\tString name = c.getString(c.getColumnIndex(CityConstants.NAME));\n\t\t\tString postID = c\n\t\t\t\t\t.getString(c.getColumnIndex(CityConstants.POST_ID));\n\t\t\tlong refreshTime = c.getLong(c\n\t\t\t\t\t.getColumnIndex(CityConstants.REFRESH_TIME));\n\t\t\tint isLocation = c.getInt(c\n\t\t\t\t\t.getColumnIndex(CityConstants.ISLOCATION));\n\t\t\tlong pubTime = c.getLong(c.getColumnIndex(CityConstants.PUB_TIME));\n\t\t\tString weatherInfoStr = c.getString(c.getColumnIndex(CityConstants.WEATHER_INFO));\n\t\t\tCity item = new City(name, postID, refreshTime, isLocation, pubTime, weatherInfoStr);\n\t\t\t// L.i(\"liweiping\", \"TmpCity  \" + item.toString());\n\t\t\tif (!list.contains(item))// 如果不存在再添加\n\t\t\t\tlist.add(item);\n\t\t}\n\t\tc.close();\n\t\treturn list;\n\t}\n\n\t/**\n\t * 获取热门城市数组\n\t * \n\t * @param c\n\t * @return\n\t */\n\tpublic static List<City> getHotCities(Cursor c) {\n\t\tList<City> list = new ArrayList<City>();\n\t\tif (c == null || c.getCount() == 0)\n\t\t\treturn list;\n\t\twhile (c.moveToNext()) {\n\t\t\tString name = c.getString(c.getColumnIndex(CityConstants.NAME));\n\t\t\tString postID = c\n\t\t\t\t\t.getString(c.getColumnIndex(CityConstants.POST_ID));\n\t\t\tCity item = new City(name, postID);\n\t\t\tlist.add(item);\n\t\t}\n\t\tc.close();\n\t\treturn list;\n\t}\n\n\t/**\n\t * 获取所有城市数组\n\t * \n\t * @param c\n\t * @return\n\t */\n\tpublic static List<City> getAllCities(Cursor c) {\n\t\tList<City> list = new ArrayList<City>();\n\t\tif (c == null || c.getCount() == 0)\n\t\t\treturn list;\n\t\twhile (c.moveToNext()) {\n\t\t\tString province = c.getString(c\n\t\t\t\t\t.getColumnIndex(CityConstants.PROVINCE));\n\t\t\tString city = c.getString(c.getColumnIndex(CityConstants.CITY));\n\t\t\tString name = c.getString(c.getColumnIndex(CityConstants.NAME));\n\t\t\tString pinyin = c.getString(c.getColumnIndex(CityConstants.PINYIN));\n\t\t\tString py = c.getString(c.getColumnIndex(CityConstants.PY));\n\t\t\tString phoneCode = c.getString(c\n\t\t\t\t\t.getColumnIndex(CityConstants.PHONE_CODE));\n\t\t\tString areaCode = c.getString(c\n\t\t\t\t\t.getColumnIndex(CityConstants.AREA_CODE));\n\t\t\tString postID = c\n\t\t\t\t\t.getString(c.getColumnIndex(CityConstants.POST_ID));\n\t\t\tCity item = new City(province, city, name, pinyin, py, phoneCode,\n\t\t\t\t\tareaCode, postID);\n\t\t\tlist.add(item);\n\t\t}\n\t\tc.close();\n\t\treturn list;\n\t}\n\n\tpublic static String getDBFilePath(Context context) {\n\t\treturn \"/data\" + Environment.getDataDirectory().getAbsolutePath()\n\t\t\t\t+ File.separator + context.getPackageName() + File.separator\n\t\t\t\t+ \"databases\" + File.separator + CityProvider.CITY_DB_NAME;\n\t}\n\n\tpublic static String getDBDirPath(Context context) {\n\t\treturn \"/data\" + Environment.getDataDirectory().getAbsolutePath()\n\t\t\t\t+ File.separator + context.getPackageName() + File.separator\n\t\t\t\t+ \"databases\";\n\t}\n\n\tpublic static void copyDB(Context context) {\n\t\tL.i(\"liweiping\", \"copyDB begin....\");\n\t\t// 如果不是第一次运行程序，直接返回\n\t\tif (!PreferenceUtils.getPrefBoolean(context, \"isFirstRun\", true))\n\t\t\treturn;\n\t\tFile dbDir = new File(getDBDirPath(context));\n\t\tif (!dbDir.exists())\n\t\t\tdbDir.mkdir();\n\t\ttry {\n\t\t\tFile dbFile = new File(dbDir, CityProvider.CITY_DB_NAME);\n\t\t\tInputStream is = context.getAssets()\n\t\t\t\t\t.open(CityProvider.CITY_DB_NAME);\n\t\t\tFileOutputStream fos = new FileOutputStream(dbFile);\n\t\t\tbyte[] buffer = new byte[is.available()];// 本地文件读写可用此方法\n\t\t\tis.read(buffer);\n\t\t\tfos.write(buffer);\n\t\t\t// int len = -1;\n\t\t\t// byte[] buffer = new byte[1024 * 8];\n\t\t\t// while ((len = is.read(buffer)) != -1) {\n\t\t\t// fos.write(buffer, 0, len);\n\t\t\tfos.close();\n\t\t\tis.close();\n\t\t\tL.i(\"liweiping\", \"copyDB finish....\");\n\t\t\tCityProvider.createTmpCityTable(context);\n\t\t\tPreferenceUtils.setPrefBoolean(context, \"isFirstRun\", false);\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\t/**\n\t * 获取一个自定义风格的Dialog\n\t * \n\t * @param activity\n\t *            上下文对象\n\t * @param style\n\t *            风格\n\t * @param customView\n\t *            自定义view\n\t * @return dialog\n\t */\n\tpublic static Dialog getCustomeDialog(Activity activity, int style,\n\t\t\tView customView) {\n\t\tDialog dialog = new Dialog(activity, style);\n\t\tdialog.setCancelable(false);\n\t\tdialog.setCanceledOnTouchOutside(false);\n\t\tdialog.setContentView(customView);\n\t\tWindow window = dialog.getWindow();\n\t\tWindowManager.LayoutParams lp = window.getAttributes();\n\t\tlp.width = LayoutParams.MATCH_PARENT;\n\t\tlp.height = LayoutParams.MATCH_PARENT;\n\t\tlp.x = 0;\n\t\tlp.y = 0;\n\t\twindow.setAttributes(lp);\n\t\treturn dialog;\n\t}\n\n\tpublic static Dialog getCustomeDialog(Activity activity, int style,\n\t\t\tint customView) {\n\t\tDialog dialog = new Dialog(activity, style);\n\t\tdialog.setCancelable(false);\n\t\tdialog.setCanceledOnTouchOutside(false);\n\t\tdialog.setContentView(customView);\n\t\tWindow window = dialog.getWindow();\n\t\tWindowManager.LayoutParams lp = window.getAttributes();\n\t\tlp.width = LayoutParams.MATCH_PARENT;\n\t\tlp.height = LayoutParams.MATCH_PARENT;\n\t\tlp.x = 0;\n\t\tlp.y = 0;\n\t\twindow.setAttributes(lp);\n\t\treturn dialog;\n\t}\n\n\t/**\n\t * 获取手机屏幕高度\n\t * \n\t * @param context\n\t * @return\n\t */\n\tpublic static int getDisplayHeight(Context context) {\n\t\tWindowManager wm = (WindowManager) context\n\t\t\t\t.getSystemService(Context.WINDOW_SERVICE);\n\t\tDisplayMetrics dm = new DisplayMetrics();\n\t\t// 获取屏幕信息\n\t\twm.getDefaultDisplay().getMetrics(dm);\n\t\treturn dm.heightPixels;\n\t}\n\n\t/**\n\t * 获取手机屏幕宽度\n\t * \n\t * @param context\n\t * @return\n\t */\n\tpublic static int getDisplayWidth(Context context) {\n\t\tWindowManager wm = (WindowManager) context\n\t\t\t\t.getSystemService(Context.WINDOW_SERVICE);\n\t\tDisplayMetrics dm = new DisplayMetrics();\n\t\t// 获取屏幕信息\n\t\twm.getDefaultDisplay().getMetrics(dm);\n\t\treturn dm.widthPixels;\n\t}\n\n\t/**\n\t * 反射方法获取状态栏高度\n\t * \n\t * @return\n\t */\n\tpublic static int getStatusBarHeight(Context context) {\n\t\tint statusBarHeight = 20;\n\t\ttry {\n\t\t\tClass<?> _class = Class.forName(\"com.android.internal.R$dimen\");\n\t\t\tObject object = _class.newInstance();\n\t\t\tField field = _class.getField(\"status_bar_height\");\n\t\t\tint restult = Integer.parseInt(field.get(object).toString());\n\t\t\tstatusBarHeight = context.getResources().getDimensionPixelSize(\n\t\t\t\t\trestult);\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\t// Toast.makeText(getActivity(), \"StatusBarHeight = \" + statusBarHeight,\n\t\t// Toast.LENGTH_SHORT).show();\n\t\treturn statusBarHeight;\n\t}\n\n\t/**\n\t * 以最省内存的方式读取本地资源的图片\n\t * \n\t * @param context\n\t * @param resId\n\t * @return\n\t */\n\tpublic static Bitmap readBitMap(Context context, int resId) {\n\t\tBitmapFactory.Options opt = new BitmapFactory.Options();\n//\t\topt.inPreferredConfig = Bitmap.Config.RGB_565;\n//\t\topt.inPurgeable = true;\n//\t\topt.inInputShareable = true;\n\t\topt.inSampleSize = 2;\n\t\t// 获取资源图片\n\t\tInputStream is = context.getResources().openRawResource(resId);\n\t\treturn BitmapFactory.decodeStream(is, null, opt);\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/common/util/T.java",
    "content": "package com.way.common.util;\n\nimport android.content.Context;\nimport android.widget.Toast;\n\n/**\n * Toast统一管理类\n * \n * @author way\n * \n */\npublic class T {\n\t// Toast\n\tprivate static Toast toast;\n\n\t/**\n\t * 短时间显示Toast\n\t * \n\t * @param context\n\t * @param message\n\t */\n\tpublic static void showShort(Context context, CharSequence message) {\n\t\tif (null == toast) {\n\t\t\ttoast = Toast.makeText(context, message, Toast.LENGTH_SHORT);\n\t\t\t// toast.setGravity(Gravity.CENTER, 0, 0);\n\t\t} else {\n\t\t\ttoast.setText(message);\n\t\t}\n\t\ttoast.show();\n\t}\n\n\t/**\n\t * 短时间显示Toast\n\t * \n\t * @param context\n\t * @param message\n\t */\n\tpublic static void showShort(Context context, int message) {\n\t\tif (null == toast) {\n\t\t\ttoast = Toast.makeText(context, message, Toast.LENGTH_SHORT);\n\t\t\t// toast.setGravity(Gravity.CENTER, 0, 0);\n\t\t} else {\n\t\t\ttoast.setText(message);\n\t\t}\n\t\ttoast.show();\n\t}\n\n\t/**\n\t * 长时间显示Toast\n\t * \n\t * @param context\n\t * @param message\n\t */\n\tpublic static void showLong(Context context, CharSequence message) {\n\t\tif (null == toast) {\n\t\t\ttoast = Toast.makeText(context, message, Toast.LENGTH_LONG);\n\t\t\t// toast.setGravity(Gravity.CENTER, 0, 0);\n\t\t} else {\n\t\t\ttoast.setText(message);\n\t\t}\n\t\ttoast.show();\n\t}\n\n\t/**\n\t * 长时间显示Toast\n\t * \n\t * @param context\n\t * @param message\n\t */\n\tpublic static void showLong(Context context, int message) {\n\t\tif (null == toast) {\n\t\t\ttoast = Toast.makeText(context, message, Toast.LENGTH_LONG);\n\t\t\t// toast.setGravity(Gravity.CENTER, 0, 0);\n\t\t} else {\n\t\t\ttoast.setText(message);\n\t\t}\n\t\ttoast.show();\n\t}\n\n\t/**\n\t * 自定义显示Toast时间\n\t * \n\t * @param context\n\t * @param message\n\t * @param duration\n\t */\n\tpublic static void show(Context context, CharSequence message, int duration) {\n\t\tif (null == toast) {\n\t\t\ttoast = Toast.makeText(context, message, duration);\n\t\t\t// toast.setGravity(Gravity.CENTER, 0, 0);\n\t\t} else {\n\t\t\ttoast.setText(message);\n\t\t}\n\t\ttoast.show();\n\t}\n\n\t/**\n\t * 自定义显示Toast时间\n\t * \n\t * @param context\n\t * @param message\n\t * @param duration\n\t */\n\tpublic static void show(Context context, int message, int duration) {\n\t\tif (null == toast) {\n\t\t\ttoast = Toast.makeText(context, message, duration);\n\t\t\t// toast.setGravity(Gravity.CENTER, 0, 0);\n\t\t} else {\n\t\t\ttoast.setText(message);\n\t\t}\n\t\ttoast.show();\n\t}\n\n\t/** Hide the toast, if any. */\n\tpublic static void hideToast() {\n\t\tif (null != toast) {\n\t\t\ttoast.cancel();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/common/util/TimeUtils.java",
    "content": "package com.way.common.util;\r\n\r\nimport java.text.SimpleDateFormat;\r\nimport java.util.Calendar;\r\nimport java.util.Date;\r\nimport java.util.TimeZone;\r\n\r\nimport android.content.res.Resources;\r\n\r\nimport com.way.yahoo.R;\r\n\r\n/**\r\n * User: qii Date: 12-8-28\r\n */\r\npublic class TimeUtils {\r\n\r\n\tprivate static int MILL_MIN = 1000 * 60;\r\n\tprivate static int MILL_HOUR = MILL_MIN * 60;\r\n\tprivate static int MILL_DAY = MILL_HOUR * 24;\r\n\r\n\tprivate static Calendar msgCalendar = null;\r\n\tprivate static java.text.SimpleDateFormat dayFormat = null;\r\n\tprivate static java.text.SimpleDateFormat dateFormat = null;\r\n\tprivate static java.text.SimpleDateFormat yearFormat = null;\r\n\r\n\tprivate static final String[] WEEK = { \"天\", \"一\", \"二\", \"三\", \"四\", \"五\", \"六\" };\r\n\tpublic static final String XING_QI = \"星期\";\r\n\tpublic static final String ZHOU = \"周\";\r\n\r\n\tprivate TimeUtils() {\r\n\r\n\t}\r\n\r\n\tpublic static String getWeek(int num, String format) {\r\n\t\tfinal Calendar c = Calendar.getInstance();\r\n\t\tc.setTimeZone(TimeZone.getTimeZone(\"GMT+8:00\"));\r\n\t\tint weekNum = c.get(Calendar.DAY_OF_WEEK) + num;\r\n\t\tif (weekNum > 7)\r\n\t\t\tweekNum = weekNum - 7;\r\n\t\treturn format + WEEK[weekNum - 1];\r\n\t}\r\n\r\n\tpublic static String getZhouWeek() {\r\n\t\tSimpleDateFormat format = new SimpleDateFormat(\"MM/dd\");\r\n\t\treturn format.format(new Date(System.currentTimeMillis())) + \" \"\r\n\t\t\t\t+ getWeek(0, ZHOU);\r\n\t}\r\n\r\n\tpublic static String getDay(long timesamp) {\r\n\t\tif (timesamp == 0L)\r\n\t\t\treturn \"未\";\r\n\t\tString result = \"未\";\r\n\t\tSimpleDateFormat sdf = new SimpleDateFormat(\"dd\");\r\n\t\tDate today = new Date(System.currentTimeMillis());\r\n\t\tDate otherDay = new Date(timesamp);\r\n\t\tint temp = Integer.parseInt(sdf.format(today))\r\n\t\t\t\t- Integer.parseInt(sdf.format(otherDay));\r\n\r\n\t\tswitch (temp) {\r\n\t\tcase 0:\r\n\t\t\tresult = \"今天\" + getTime(timesamp);\r\n\t\t\tbreak;\r\n\t\tcase 1:\r\n\t\t\tresult = \"昨天\" + getTime(timesamp);\r\n\t\t\tbreak;\r\n\t\tcase 2:\r\n\t\t\tresult = \"前天\" + getTime(timesamp);\r\n\t\t\tbreak;\r\n\r\n\t\tdefault:\r\n\t\t\tresult = temp + \"天前\" + getTime(timesamp);\r\n\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\treturn result;\r\n\t}\r\n\r\n\tpublic static long getLongTime(String time) {\r\n\t\ttry {\r\n\t\t\ttime = time.substring(0, time.indexOf('.'));\r\n\t\t\tDate date = new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss\")\r\n\t\t\t\t\t.parse(time);\r\n\t\t\treturn date.getTime();\r\n\t\t} catch (Exception e) {\r\n\t\t\te.printStackTrace();\r\n\t\t}\r\n\t\treturn 0L;\r\n\t}\r\n\tpublic static String getTime(long time) {\r\n\t\treturn new SimpleDateFormat(\"HH:mm\").format(new Date(time));\r\n\t}\r\n\tpublic static String getDateTime(long time) {\r\n\t\treturn new SimpleDateFormat(\"MM/dd\").format(new Date(time));\r\n\t}\r\n\r\n\tpublic static String getListTime(Resources res, long time) {\r\n\t\tlong now = System.currentTimeMillis();\r\n\t\tlong msg = time;\r\n\r\n\t\tCalendar nowCalendar = Calendar.getInstance();\r\n\r\n\t\tif (msgCalendar == null)\r\n\t\t\tmsgCalendar = Calendar.getInstance();\r\n\r\n\t\tmsgCalendar.setTimeInMillis(time);\r\n\r\n\t\tlong calcMills = now - msg;\r\n\r\n\t\tlong calSeconds = calcMills / 1000;\r\n\t\tif (calSeconds < 60)\r\n\t\t\t// return res.getString(R.string.justnow);\r\n\t\t\treturn new StringBuilder().append(calSeconds)\r\n\t\t\t\t\t.append(res.getString(R.string.sec)).toString();\r\n\r\n\t\tlong calMins = calSeconds / 60;\r\n\t\tif (calMins < 60)\r\n\t\t\treturn new StringBuilder().append(calMins)\r\n\t\t\t\t\t.append(res.getString(R.string.min)).toString();\r\n\r\n\t\tlong calHours = calMins / 60;\r\n\t\tif (calHours < 24 && isSameDay(nowCalendar, msgCalendar)) {\r\n\t\t\tif (dayFormat == null)\r\n\t\t\t\tdayFormat = new java.text.SimpleDateFormat(\"HH:mm\");\r\n\t\t\tString result = dayFormat.format(msgCalendar.getTime());\r\n\t\t\treturn new StringBuilder().append(res.getString(R.string.today))\r\n\t\t\t\t\t.append(\" \").append(result).toString();\r\n\t\t}\r\n\r\n\t\tlong calDay = calHours / 24;\r\n\t\tif (calDay < 31) {\r\n\t\t\tif (isYesterDay(nowCalendar, msgCalendar)) {\r\n\t\t\t\tif (dayFormat == null)\r\n\t\t\t\t\tdayFormat = new java.text.SimpleDateFormat(\"HH:mm\");\r\n\t\t\t\tString result = dayFormat.format(msgCalendar.getTime());\r\n\t\t\t\treturn new StringBuilder(res.getString(R.string.yesterday))\r\n\t\t\t\t\t\t.append(\" \").append(result).toString();\r\n\t\t\t} else if (isTheDayBeforeYesterDay(nowCalendar, msgCalendar)) {\r\n\t\t\t\tif (dayFormat == null)\r\n\t\t\t\t\tdayFormat = new java.text.SimpleDateFormat(\"HH:mm\");\r\n\t\t\t\tString result = dayFormat.format(msgCalendar.getTime());\r\n\t\t\t\treturn new StringBuilder(\r\n\t\t\t\t\t\tres.getString(R.string.the_day_before_yesterday))\r\n\t\t\t\t\t\t.append(\" \").append(result).toString();\r\n\t\t\t} else {\r\n\t\t\t\tif (dateFormat == null)\r\n\t\t\t\t\tdateFormat = new java.text.SimpleDateFormat(\r\n\t\t\t\t\t\t\tres.getString(R.string.date_format));\r\n\t\t\t\tString result = dateFormat.format(msgCalendar.getTime());\r\n\t\t\t\treturn new StringBuilder(result).toString();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tlong calMonth = calDay / 31;\r\n\r\n\t\tif (calMonth < 12) {\r\n\t\t\tif (dateFormat == null)\r\n\t\t\t\tdateFormat = new java.text.SimpleDateFormat(\r\n\t\t\t\t\t\tres.getString(R.string.date_format));\r\n\r\n\t\t\tString result = dateFormat.format(msgCalendar.getTime());\r\n\t\t\treturn new StringBuilder().append(result).toString();\r\n\r\n\t\t}\r\n\t\tif (yearFormat == null)\r\n\t\t\tyearFormat = new java.text.SimpleDateFormat(\r\n\t\t\t\t\tres.getString(R.string.year_format));\r\n\t\tString result = yearFormat.format(msgCalendar.getTime());\r\n\t\treturn new StringBuilder().append(result).toString();\r\n\r\n\t}\r\n\r\n\tprivate static boolean isSameHalfDay(Calendar now, Calendar msg) {\r\n\t\tint nowHour = now.get(Calendar.HOUR_OF_DAY);\r\n\t\tint msgHOur = msg.get(Calendar.HOUR_OF_DAY);\r\n\r\n\t\tif (nowHour <= 12 & msgHOur <= 12) {\r\n\t\t\treturn true;\r\n\t\t} else if (nowHour >= 12 & msgHOur >= 12) {\r\n\t\t\treturn true;\r\n\t\t} else {\r\n\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\r\n\tprivate static boolean isSameDay(Calendar now, Calendar msg) {\r\n\t\tint nowDay = now.get(Calendar.DAY_OF_YEAR);\r\n\t\tint msgDay = msg.get(Calendar.DAY_OF_YEAR);\r\n\r\n\t\treturn nowDay == msgDay;\r\n\t}\r\n\r\n\tprivate static boolean isYesterDay(Calendar now, Calendar msg) {\r\n\t\tint nowDay = now.get(Calendar.DAY_OF_YEAR);\r\n\t\tint msgDay = msg.get(Calendar.DAY_OF_YEAR);\r\n\r\n\t\treturn (nowDay - msgDay) == 1;\r\n\t}\r\n\r\n\tprivate static boolean isTheDayBeforeYesterDay(Calendar now, Calendar msg) {\r\n\t\tint nowDay = now.get(Calendar.DAY_OF_YEAR);\r\n\t\tint msgDay = msg.get(Calendar.DAY_OF_YEAR);\r\n\r\n\t\treturn (nowDay - msgDay) == 2;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "WayHoo/src/com/way/common/util/WeatherIconUtils.java",
    "content": "package com.way.common.util;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n\nimport com.way.weather.plugin.util.Constants;\nimport com.way.yahoo.R;\n\npublic class WeatherIconUtils {\n\n\tprivate WeatherIconUtils() {\n\t}\n\t\n\t/**\n\t * 获取天气图标\n\t * @param type\n\t * @return\n\t */\n\tpublic static int getWeatherIcon(int type) {\n\t\t// 如果是晚上\n\t\tif (isNight(System.currentTimeMillis()))\n\t\t\tswitch (type) {\n\t\t\tcase Constants.SUNNY:\n\t\t\t\treturn R.drawable.ic_nightsunny_big;\n\t\t\tcase Constants.CLOUDY:\n\t\t\t\treturn R.drawable.ic_nightcloudy_big;\n\t\t\tcase Constants.HEAVY_RAIN:\n\t\t\tcase Constants.LIGHT_RAIN:\n\t\t\tcase Constants.MODERATE_RAIN:\n\t\t\tcase Constants.SHOWER:\n\t\t\tcase Constants.STORM:\n\t\t\t\treturn R.drawable.ic_nightrain_big;\n\t\t\tcase Constants.SNOWSTORM:\n\t\t\tcase Constants.LIGHT_SNOW:\n\t\t\tcase Constants.MODERATE_SNOW:\n\t\t\tcase Constants.HEAVY_SNOW:\n\t\t\tcase Constants.SNOW_SHOWER:\n\t\t\t\treturn R.drawable.ic_nightsnow_big;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t// 如果是白天\n\t\tswitch (type) {\n\t\tcase Constants.SUNNY:\n\t\t\treturn R.drawable.ic_sunny_big;\n\t\tcase Constants.CLOUDY:\n\t\t\treturn R.drawable.ic_cloudy_big;\n\t\tcase Constants.OVERCAST:\n\t\t\treturn R.drawable.ic_overcast_big;\n\t\tcase Constants.FOGGY:\n\t\t\treturn R.drawable.tornado_day_night;\n\t\tcase Constants.SEVERE_STORM:\n\t\t\treturn R.drawable.hurricane_day_night;\n\t\tcase Constants.HEAVY_STORM:\n\t\t\treturn R.drawable.ic_heavyrain_big;\n\t\tcase Constants.STORM:\n\t\t\treturn R.drawable.ic_heavyrain_big;\n\t\tcase Constants.THUNDERSHOWER:\n\t\t\treturn R.drawable.ic_thundeshower_big;\n\t\tcase Constants.SHOWER:\n\t\t\treturn R.drawable.ic_shower_big;\n\t\tcase Constants.HEAVY_RAIN:\n\t\t\treturn R.drawable.ic_heavyrain_big;\n\t\tcase Constants.MODERATE_RAIN:\n\t\t\treturn R.drawable.ic_moderraterain_big;\n\t\tcase Constants.LIGHT_RAIN:\n\t\t\treturn R.drawable.ic_lightrain_big;\n\t\tcase Constants.SLEET:\n\t\t\treturn R.drawable.ic_sleet_big;\n\t\tcase Constants.SNOWSTORM:\n\t\t\treturn R.drawable.ic_snow_big;\n\t\tcase Constants.SNOW_SHOWER:\n\t\t\treturn R.drawable.ic_snow_big;\n\t\tcase Constants.HEAVY_SNOW:\n\t\t\treturn R.drawable.ic_heavysnow_big;\n\t\tcase Constants.MODERATE_SNOW:\n\t\t\treturn R.drawable.ic_snow_big;\n\t\tcase Constants.LIGHT_SNOW:\n\t\t\treturn R.drawable.ic_snow_big;\n\t\tcase Constants.STRONGSANDSTORM:\n\t\t\treturn R.drawable.ic_sandstorm_big;\n\t\tcase Constants.SANDSTORM:\n\t\t\treturn R.drawable.ic_sandstorm_big;\n\t\tcase Constants.SAND:\n\t\t\treturn R.drawable.ic_sandstorm_big;\n\t\tcase Constants.BLOWING_SAND:\n\t\t\treturn R.drawable.ic_sandstorm_big;\n\t\tcase Constants.ICE_RAIN:\n\t\t\treturn R.drawable.freezing_rain_day_night;\n\t\tcase Constants.DUST:\n\t\t\treturn R.drawable.ic_dust_big;\n\t\tcase Constants.HAZE:\n\t\t\treturn R.drawable.ic_haze_big;\n\t\tdefault:\n\t\t\treturn R.drawable.ic_default_big;\n\t\t}\n\t}\n\t\n\t/**\n\t * 获取天气清晰背景\n\t * @param type\n\t * @return\n\t */\n\tpublic static int getWeatherNromalBg(int type) {\n\t\tif (isNight(System.currentTimeMillis()))\n\t\t\tswitch (type) {\n\t\t\tcase Constants.SUNNY:\n\t\t\t\treturn R.drawable.bg_fine_night;\n\t\t\tcase Constants.CLOUDY:\n\t\t\t\treturn R.drawable.bg_cloudy_night;\n\t\t\tcase Constants.HEAVY_RAIN:\n\t\t\tcase Constants.LIGHT_RAIN:\n\t\t\tcase Constants.MODERATE_RAIN:\n\t\t\tcase Constants.SHOWER:\n\t\t\tcase Constants.ICE_RAIN:\n\t\t\tcase Constants.STORM:\n\t\t\t\treturn R.drawable.bg_rain;\n\t\t\tcase Constants.SNOWSTORM:\n\t\t\tcase Constants.LIGHT_SNOW:\n\t\t\tcase Constants.MODERATE_SNOW:\n\t\t\tcase Constants.HEAVY_SNOW:\n\t\t\tcase Constants.SNOW_SHOWER:\n\t\t\t\treturn R.drawable.bg_snow_night;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t// 如果是白天\n\t\tswitch (type) {\n\t\tcase Constants.SUNNY:\n\t\t\treturn R.drawable.bg_fine_day;\n\t\tcase Constants.CLOUDY:\n\t\t\treturn R.drawable.bg_cloudy_day;\n\t\tcase Constants.OVERCAST:\n\t\t\treturn R.drawable.bg_overcast;\n\t\tcase Constants.FOGGY:\n\t\t\treturn R.drawable.bg_fog;\n\t\tcase Constants.SEVERE_STORM:\n\t\tcase Constants.HEAVY_STORM:\n\t\tcase Constants.STORM:\n\t\t\treturn R.drawable.bg_rain;\n\t\tcase Constants.THUNDERSHOWER:\n\t\t\treturn R.drawable.bg_thunder_storm;\n\t\tcase Constants.SHOWER:\n\t\tcase Constants.HEAVY_RAIN:\n\t\tcase Constants.MODERATE_RAIN:\n\t\tcase Constants.LIGHT_RAIN:\n\t\tcase Constants.SLEET:\n\t\t\treturn R.drawable.bg_rain;\n\t\tcase Constants.SNOWSTORM:\n\t\tcase Constants.SNOW_SHOWER:\n\t\tcase Constants.HEAVY_SNOW:\n\t\tcase Constants.MODERATE_SNOW:\n\t\tcase Constants.LIGHT_SNOW:\n\t\t\treturn R.drawable.bg_snow;\n\t\tcase Constants.STRONGSANDSTORM:\n\t\tcase Constants.SANDSTORM:\n\t\tcase Constants.SAND:\n\t\tcase Constants.BLOWING_SAND:\n\t\t\treturn R.drawable.bg_sand_storm;\n\t\tcase Constants.ICE_RAIN:\n\t\t\treturn R.drawable.bg_rain;\n\t\tcase Constants.DUST:\n\t\tcase Constants.HAZE:\n\t\t\treturn R.drawable.bg_haze;\n\n\t\tdefault:\n\t\t\treturn R.drawable.bg_na;\n\t\t}\n\t}\n\t/**\n\t * 获取天气模糊背景\n\t * @param type\n\t * @return\n\t */\n\tpublic static int getWeatherBlurBg(int type) {\n\t\tif (isNight(System.currentTimeMillis()))\n\t\t\tswitch (type) {\n\t\t\tcase Constants.SUNNY:\n\t\t\t\treturn R.drawable.bg_fine_night_blur;\n\t\t\tcase Constants.CLOUDY:\n\t\t\t\treturn R.drawable.bg_cloudy_night_blur;\n\t\t\tcase Constants.HEAVY_RAIN:\n\t\t\tcase Constants.LIGHT_RAIN:\n\t\t\tcase Constants.MODERATE_RAIN:\n\t\t\tcase Constants.SHOWER:\n\t\t\tcase Constants.ICE_RAIN:\n\t\t\t\treturn R.drawable.bg_rain_blur;\n\t\t\tcase Constants.STORM:\n\t\t\t\treturn R.drawable.bg_rain_blur;\n\t\t\tcase Constants.SNOWSTORM:\n\t\t\tcase Constants.LIGHT_SNOW:\n\t\t\tcase Constants.MODERATE_SNOW:\n\t\t\tcase Constants.HEAVY_SNOW:\n\t\t\tcase Constants.SNOW_SHOWER:\n\t\t\t\treturn R.drawable.bg_snow_night_blur;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t// 如果是白天\n\t\tswitch (type) {\n\t\tcase Constants.SUNNY:\n\t\t\treturn R.drawable.bg_fine_day_blur;\n\t\tcase Constants.CLOUDY:\n\t\t\treturn R.drawable.bg_cloudy_day_blur;\n\t\tcase Constants.OVERCAST:\n\t\t\treturn R.drawable.bg_overcast_blur;\n\t\tcase Constants.FOGGY:\n\t\t\treturn R.drawable.bg_fog_blur;\n\t\tcase Constants.SEVERE_STORM:\n\t\tcase Constants.HEAVY_STORM:\n\t\tcase Constants.STORM:\n\t\t\treturn R.drawable.bg_rain_blur;\n\t\tcase Constants.THUNDERSHOWER:\n\t\t\treturn R.drawable.bg_thunder_storm_blur;\n\t\tcase Constants.SHOWER:\n\t\tcase Constants.HEAVY_RAIN:\n\t\tcase Constants.MODERATE_RAIN:\n\t\tcase Constants.LIGHT_RAIN:\n\t\tcase Constants.SLEET:\n\t\t\treturn R.drawable.bg_rain_blur;\n\t\tcase Constants.SNOWSTORM:\n\t\tcase Constants.SNOW_SHOWER:\n\t\tcase Constants.HEAVY_SNOW:\n\t\tcase Constants.MODERATE_SNOW:\n\t\tcase Constants.LIGHT_SNOW:\n\t\t\treturn R.drawable.bg_snow_blur;\n\t\tcase Constants.STRONGSANDSTORM:\n\t\tcase Constants.SANDSTORM:\n\t\tcase Constants.SAND:\n\t\tcase Constants.BLOWING_SAND:\n\t\t\treturn R.drawable.bg_sand_storm_blur;\n\t\tcase Constants.ICE_RAIN:\n\t\t\treturn R.drawable.bg_rain_blur;\n\t\tcase Constants.DUST:\n\t\tcase Constants.HAZE:\n\t\t\treturn R.drawable.bg_haze_blur;\n\t\tdefault:\n\t\t\treturn R.drawable.bg_na_blur;\n\t\t}\n\t}\n\n\tpublic static boolean isNight(long time) {\n\t\tSimpleDateFormat df = new SimpleDateFormat(\"HH\");\n\t\tString timeStr = df.format(new Date(System.currentTimeMillis()));\n\t\t// L.i(\"liweiping\", \"timeStr = \" + timeStr);\n\t\ttry {\n\t\t\tint timeHour = Integer.parseInt(timeStr);\n\t\t\treturn (timeHour >= 18 || timeHour <= 6);\n\t\t} catch (NumberFormatException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn false;\n\t}\n\t\n\t\n\t/**\n\t * 获取天气清晰背景\n\t * @param type\n\t * @return\n\t */\n\tpublic static int getRawNromalBg(int type) {\n\t\tif (isNight(System.currentTimeMillis()))\n\t\t\tswitch (type) {\n\t\t\tcase Constants.SUNNY:\n\t\t\t\treturn R.drawable.bg_fine_night;\n\t\t\tcase Constants.CLOUDY:\n\t\t\t\treturn R.drawable.bg_cloudy_night;\n//\t\t\tcase Constants.FOGGY:\n//\t\t\t\treturn R.raw.foggy_n;\n//\t\t\tcase Constants.HEAVY_RAIN:\n//\t\t\tcase Constants.LIGHT_RAIN:\n//\t\t\tcase Constants.MODERATE_RAIN:\n//\t\t\tcase Constants.SHOWER:\n//\t\t\tcase Constants.ICE_RAIN:\n//\t\t\t\treturn R.raw.rain_n;\n//\t\t\tcase Constants.STORM:\n//\t\t\t\treturn R.raw.storm_n;\n\t\t\tcase Constants.SNOWSTORM:\n\t\t\tcase Constants.LIGHT_SNOW:\n\t\t\tcase Constants.MODERATE_SNOW:\n\t\t\tcase Constants.HEAVY_SNOW:\n\t\t\tcase Constants.SNOW_SHOWER:\n\t\t\t\treturn R.drawable.bg_snow_night;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t// 如果是白天\n\t\tswitch (type) {\n\t\tcase Constants.SUNNY:\n\t\t\treturn R.drawable.bg_fine_day;\n\t\tcase Constants.CLOUDY:\n\t\t\treturn R.drawable.bg_cloudy_day;\n\t\tcase Constants.OVERCAST:\n\t\t\treturn R.drawable.bg_overcast;\n\t\tcase Constants.FOGGY:\n\t\t\treturn R.drawable.bg_fog;\n\t\tcase Constants.SEVERE_STORM:\n\t\tcase Constants.HEAVY_STORM:\n//\t\tcase Constants.STORM:\n//\t\t\treturn R.raw.storm_d;\n\t\tcase Constants.THUNDERSHOWER:\n\t\t\treturn R.drawable.bg_thunder_storm;\n\t\tcase Constants.STORM:\n\t\tcase Constants.SHOWER:\n\t\tcase Constants.HEAVY_RAIN:\n\t\tcase Constants.MODERATE_RAIN:\n\t\tcase Constants.LIGHT_RAIN:\n\t\tcase Constants.SLEET:\n\t\t\treturn R.drawable.bg_rain;\n\t\tcase Constants.SNOWSTORM:\n\t\tcase Constants.SNOW_SHOWER:\n\t\tcase Constants.HEAVY_SNOW:\n\t\tcase Constants.MODERATE_SNOW:\n\t\tcase Constants.LIGHT_SNOW:\n\t\t\treturn R.drawable.bg_snow;\n\t\tcase Constants.STRONGSANDSTORM:\n\t\tcase Constants.SANDSTORM:\n\t\tcase Constants.SAND:\n\t\tcase Constants.BLOWING_SAND:\n\t\t\treturn R.drawable.bg_sand_storm;\n\t\tcase Constants.ICE_RAIN:\n\t\t\treturn R.drawable.bg_rain;\n\t\tcase Constants.DUST:\n\t\tcase Constants.HAZE:\n\t\t\treturn R.drawable.bg_haze;\n\n\t\tdefault:\n\t\t\treturn R.drawable.bg_na;\n\t\t}\n\t}\n\t/**\n\t * 获取天气模糊背景\n\t * @param type\n\t * @return\n\t */\n\tpublic static int getRawBlurBg(int type) {\n\t\tif (isNight(System.currentTimeMillis()))\n\t\t\tswitch (type) {\n\t\t\tcase Constants.SUNNY:\n\t\t\t\treturn R.drawable.bg_fine_night_blur;\n\t\t\tcase Constants.CLOUDY:\n\t\t\t\treturn R.drawable.bg_cloudy_night_blur;\n//\t\t\tcase Constants.FOGGY:\n//\t\t\t\treturn R.raw.foggy_n_blur;\n//\t\t\tcase Constants.HEAVY_RAIN:\n//\t\t\tcase Constants.LIGHT_RAIN:\n//\t\t\tcase Constants.MODERATE_RAIN:\n//\t\t\tcase Constants.SHOWER:\n//\t\t\tcase Constants.ICE_RAIN:\n//\t\t\t\treturn R.raw.rain_n_blur;\n//\t\t\tcase Constants.STORM:\n//\t\t\t\treturn R.raw.storm_n_blur;\n\t\t\tcase Constants.SNOWSTORM:\n\t\t\tcase Constants.LIGHT_SNOW:\n\t\t\tcase Constants.MODERATE_SNOW:\n\t\t\tcase Constants.HEAVY_SNOW:\n\t\t\tcase Constants.SNOW_SHOWER:\n\t\t\t\treturn R.drawable.bg_snow_night_blur;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t// 如果是白天\n\t\tswitch (type) {\n\t\tcase Constants.SUNNY:\n\t\t\treturn R.drawable.bg_fine_day_blur;\n\t\tcase Constants.CLOUDY:\n\t\t\treturn R.drawable.bg_cloudy_day_blur;\n\t\tcase Constants.OVERCAST:\n\t\t\treturn R.drawable.bg_overcast_blur;\n\t\tcase Constants.FOGGY:\n\t\t\treturn R.drawable.bg_fog_blur;\n\t\tcase Constants.SEVERE_STORM:\n\t\tcase Constants.HEAVY_STORM:\n//\t\tcase Constants.STORM:\n//\t\t\treturn R.raw.storm_d_blur;\n\t\tcase Constants.THUNDERSHOWER:\n\t\t\treturn R.drawable.bg_thunder_storm_blur;\n\t\tcase Constants.STORM:\n\t\tcase Constants.SHOWER:\n\t\tcase Constants.HEAVY_RAIN:\n\t\tcase Constants.MODERATE_RAIN:\n\t\tcase Constants.LIGHT_RAIN:\n\t\tcase Constants.SLEET:\n\t\t\treturn R.drawable.bg_rain_blur;\n\t\tcase Constants.SNOWSTORM:\n\t\tcase Constants.SNOW_SHOWER:\n\t\tcase Constants.HEAVY_SNOW:\n\t\tcase Constants.MODERATE_SNOW:\n\t\tcase Constants.LIGHT_SNOW:\n\t\t\treturn R.drawable.bg_snow_blur;\n\t\tcase Constants.STRONGSANDSTORM:\n\t\tcase Constants.SANDSTORM:\n\t\tcase Constants.SAND:\n\t\tcase Constants.BLOWING_SAND:\n\t\t\treturn R.drawable.bg_sand_storm_blur;\n\t\tcase Constants.ICE_RAIN:\n\t\t\treturn R.drawable.bg_rain_blur;\n\t\tcase Constants.DUST:\n\t\tcase Constants.HAZE:\n\t\t\treturn R.drawable.bg_haze_blur;\n\t\tdefault:\n\t\t\treturn R.drawable.bg_na_blur;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/db/CityProvider.java",
    "content": "package com.way.db;\n\nimport java.util.ArrayList;\n\nimport android.content.ContentProvider;\nimport android.content.ContentUris;\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.content.UriMatcher;\nimport android.database.Cursor;\nimport android.database.SQLException;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.database.sqlite.SQLiteQueryBuilder;\nimport android.net.Uri;\nimport android.provider.BaseColumns;\nimport android.text.TextUtils;\n\nimport com.way.common.util.L;\nimport com.way.common.util.SystemUtils;\n\npublic class CityProvider extends ContentProvider {\n\tprivate static final String TAG = \"CityProvider\";\n\n\tpublic static final String CITY_DB_NAME = \"city.db\";\n\tpublic static final String WRITE_TMPCITY = \"write_tmpcity\";\n\tpublic static final String AUTHORITY = \"com.way.yahoo.provider.Citys\";// 授权\n\tpublic static final String CITY_TABLE_NAME = \"city\";// 城市表名\n\tpublic static final String HOTCITY_TABLE_NAME = \"hotcity\";// 热门城市表名\n\tpublic static final String TMPCITY_TABLE_NAME = \"tmpcity\";// 临时城市表名\n\n\tpublic static final Uri CITY_CONTENT_URI = Uri.parse(\"content://\"\n\t\t\t+ AUTHORITY + \"/\" + CITY_TABLE_NAME);// 城市uri\n\tpublic static final Uri HOTCITY_CONTENT_URI = Uri.parse(\"content://\"\n\t\t\t+ AUTHORITY + \"/\" + HOTCITY_TABLE_NAME);// 热门城市 uri\n\tpublic static final Uri TMPCITY_CONTENT_URI = Uri.parse(\"content://\"\n\t\t\t+ AUTHORITY + \"/\" + TMPCITY_TABLE_NAME);// 临时城市 uri\n\n\tprivate static final UriMatcher URI_MATCHER = new UriMatcher(\n\t\t\tUriMatcher.NO_MATCH);// 匹配\n\n\tprivate static final int CITYS = 1;// 多个城市查询\n\tprivate static final int CITY_ID = 2;// 单个城市查询\n\tprivate static final int HOTCITYS = 3;// 多个热门城市查询\n\tprivate static final int HOTCITY_ID = 4;// 单个热门城市查询\n\tprivate static final int TMPCITYS = 5;// 多个临时查询\n\tprivate static final int TMPCITY_ID = 6;// 单个临时查询\n\tstatic {\n\t\tURI_MATCHER.addURI(AUTHORITY, \"city\", CITYS);\n\t\tURI_MATCHER.addURI(AUTHORITY, \"city/#\", CITY_ID);\n\n\t\tURI_MATCHER.addURI(AUTHORITY, \"hotcity\", HOTCITYS);\n\t\tURI_MATCHER.addURI(AUTHORITY, \"hotcity/#\", HOTCITY_ID);\n\n\t\tURI_MATCHER.addURI(AUTHORITY, \"tmpcity\", TMPCITYS);\n\t\tURI_MATCHER.addURI(AUTHORITY, \"tmpcity/#\", TMPCITY_ID);\n\t}\n\n\tprivate SQLiteDatabase mSqLiteDatabase;\n\n\t// private CityProvider() {\n\t// }\n\tpublic static void createTmpCityTable(Context context) {\n\t\tSQLiteDatabase db = context.openOrCreateDatabase(\n\t\t\t\tSystemUtils.getDBFilePath(context), Context.MODE_PRIVATE, null);\n\t\tL.i(\"liweiping\", \"create table tmpcity ....\");\n\t\tdb.execSQL(\"CREATE table IF NOT EXISTS \"\n\t\t\t\t+ TMPCITY_TABLE_NAME\n\t\t\t\t+ \" (_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, postID TEXT,\"\n\t\t\t\t+ \" refreshTime TEXT, isLocation TEXT, pubTime TEXT, weatherInfo TEXT, orderIndex INTEGER)\");\n\t}\n\n\t@Override\n\tpublic boolean onCreate() {\n\t\tmSqLiteDatabase = getContext().openOrCreateDatabase(\n\t\t\t\tSystemUtils.getDBFilePath(getContext()), Context.MODE_PRIVATE,\n\t\t\t\tnull);\n\t\tL.i(\"liweiping\", \"create db....\");\n\t\treturn true;\n\t}\n\n\tprivate static void infoLog(String data) {\n\t\tL.i(TAG, data);\n\t}\n\n\t@Override\n\tpublic Cursor query(Uri uri, String[] projection, String selection,\n\t\t\tString[] selectionArgs, String sortOrder) {\n\t\tSQLiteQueryBuilder qBuilder = new SQLiteQueryBuilder();\n\t\tint match = URI_MATCHER.match(uri);\n\t\t// 允许对三个表进行查询\n\t\tswitch (match) {\n\t\tcase CITYS:\n\t\t\tqBuilder.setTables(CITY_TABLE_NAME);\n\t\t\tbreak;\n\t\tcase CITY_ID:\n\t\t\tqBuilder.setTables(CITY_TABLE_NAME);\n\t\t\tqBuilder.appendWhere(\"_id=\");\n\t\t\tqBuilder.appendWhere(uri.getPathSegments().get(1));\n\t\t\tbreak;\n\t\tcase HOTCITYS:\n\t\t\tqBuilder.setTables(HOTCITY_TABLE_NAME);\n\t\t\tbreak;\n\t\tcase HOTCITY_ID:\n\t\t\tqBuilder.setTables(HOTCITY_TABLE_NAME);\n\t\t\tqBuilder.appendWhere(\"_id=\");\n\t\t\tqBuilder.appendWhere(uri.getPathSegments().get(1));\n\t\t\tbreak;\n\t\tcase TMPCITYS:\n\t\t\tqBuilder.setTables(TMPCITY_TABLE_NAME);\n\t\t\tbreak;\n\t\tcase TMPCITY_ID:\n\t\t\tqBuilder.setTables(TMPCITY_TABLE_NAME);\n\t\t\tqBuilder.appendWhere(\"_id=\");\n\t\t\tqBuilder.appendWhere(uri.getPathSegments().get(1));\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthrow new IllegalArgumentException(\"Unknown URL \" + uri);\n\t\t}\n\n\t\tString orderBy;\n\t\tif (TextUtils.isEmpty(sortOrder)) {\n\t\t\torderBy = CityConstants.DEFAULT_SORT_ORDER;\n\t\t} else {\n\t\t\torderBy = sortOrder;\n\t\t}\n\n\t\tCursor ret = qBuilder.query(mSqLiteDatabase, projection, selection,\n\t\t\t\tselectionArgs, null, null, orderBy);\n\n\t\tif (ret == null) {\n\t\t\tinfoLog(\"CityProvider.query: failed\");\n\t\t} else {\n\t\t\tret.setNotificationUri(getContext().getContentResolver(), uri);\n\t\t}\n\t\treturn ret;\n\t}\n\n\t@Override\n\tpublic String getType(Uri uri) {\n\t\tint match = URI_MATCHER.match(uri);\n\t\tswitch (match) {\n\t\tcase CITYS:\n\t\t\treturn CityConstants.CONTENT_TYPE;\n\t\tcase CITY_ID:\n\t\t\treturn CityConstants.CONTENT_ITEM_TYPE;\n\t\tdefault:\n\t\t\tthrow new IllegalArgumentException(\"Unknown URL\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic Uri insert(Uri uri, ContentValues initialValues) {\n\t\t// 只允许存储到临时城市表\n\t\tif (URI_MATCHER.match(uri) != TMPCITYS) {\n\t\t\tthrow new IllegalArgumentException(\"Cannot insert into URL: \" + uri);\n\t\t}\n\n\t\tContentValues values = (initialValues != null) ? new ContentValues(\n\t\t\t\tinitialValues) : new ContentValues();\n\n\t\t// for (String colName : CityConstants.getRequiredColumns()) {\n\t\t// if (values.containsKey(colName) == false) {\n\t\t// throw new IllegalArgumentException(\"Missing column: \" + colName);\n\t\t// }\n\t\t// }\n\n\t\tlong rowId = mSqLiteDatabase.insert(TMPCITY_TABLE_NAME,\n\t\t\t\tCityConstants.REFRESH_TIME, values);\n\n\t\tif (rowId < 0) {\n\t\t\tthrow new SQLException(\"Failed to insert row into \" + uri);\n\t\t}\n\n\t\tUri noteUri = ContentUris.withAppendedId(TMPCITY_CONTENT_URI, rowId);\n\t\tgetContext().getContentResolver().notifyChange(noteUri, null);// 发出通知\n\t\treturn noteUri;\n\t}\n\n\t@Override\n\tpublic int delete(Uri uri, String selection, String[] selectionArgs) {\n\t\tSQLiteDatabase db = mSqLiteDatabase;\n\t\tint count;\n\t\t// 只允许删除临时表中的数据\n\t\tswitch (URI_MATCHER.match(uri)) {\n\t\tcase TMPCITYS:\n\t\t\tcount = db.delete(TMPCITY_TABLE_NAME, selection, selectionArgs);\n\t\t\tbreak;\n\t\tcase TMPCITY_ID:\n\t\t\tString segment = uri.getPathSegments().get(1);\n\n\t\t\tif (TextUtils.isEmpty(selection)) {\n\t\t\t\tselection = \"_id=\" + segment;\n\t\t\t} else {\n\t\t\t\tselection = \"_id=\" + segment + \" AND (\" + selection + \")\";\n\t\t\t}\n\t\t\tcount = db.delete(TMPCITY_TABLE_NAME, selection, selectionArgs);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthrow new IllegalArgumentException(\"Cannot delete from URL: \" + uri);\n\t\t}\n\t\tgetContext().getContentResolver().notifyChange(uri, null);\n\t\treturn count;\n\t}\n\n\t@Override\n\tpublic int update(Uri uri, ContentValues values, String selection,\n\t\t\tString[] selectionArgs) {\n\t\tint count;\n\t\tlong rowId = 0;\n\t\tint match = URI_MATCHER.match(uri);\n\t\tSQLiteDatabase db = mSqLiteDatabase;\n\t\t// 可以更新热门城市表和临时城市表\n\t\tswitch (match) {\n//\t\tcase HOTCITYS:\n//\t\t\tcount = db.update(HOTCITY_TABLE_NAME, values, selection,\n//\t\t\t\t\tselectionArgs);\n//\t\t\tbreak;\n//\t\tcase HOTCITY_ID:\n//\t\t\tString hotSegment = uri.getPathSegments().get(1);\n//\t\t\trowId = Long.parseLong(hotSegment);\n//\t\t\tcount = db.update(HOTCITY_TABLE_NAME, values, \"_id=\" + rowId, null);\n//\t\t\tbreak;\n\t\tcase TMPCITYS:\n\t\t\tcount = db.update(TMPCITY_TABLE_NAME, values, selection,\n\t\t\t\t\tselectionArgs);\n\t\t\tbreak;\n\t\tcase TMPCITY_ID:\n\t\t\tString tmpSegment = uri.getPathSegments().get(1);\n\t\t\trowId = Long.parseLong(tmpSegment);\n\t\t\tcount = db.update(TMPCITY_TABLE_NAME, values, \"_id=\" + rowId, null);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthrow new UnsupportedOperationException(\"Cannot update URL: \" + uri);\n\t\t}\n\t\tinfoLog(\"*** notifyChange() rowId: \" + rowId + \" url \" + uri);\n\t\tgetContext().getContentResolver().notifyChange(uri, null);\n\t\treturn count;\n\t}\n\n\tpublic static final class CityConstants implements BaseColumns {\n\t\tpublic static final String CONTENT_TYPE = \"vnd.android.cursor.dir/city\";\n\t\tpublic static final String CONTENT_ITEM_TYPE = \"vnd.android.cursor.item/city\";\n\n\t\tpublic static final String ID = \"_id\";\n\t\tpublic static final String PROVINCE = \"province\";\n\t\tpublic static final String CITY = \"city\";\n\t\tpublic static final String NAME = \"name\";\n\t\tpublic static final String PINYIN = \"pinyin\";\n\t\tpublic static final String PY = \"py\";\n\t\tpublic static final String PHONE_CODE = \"phoneCode\";\n\t\tpublic static final String AREA_CODE = \"areaCode\";\n\t\tpublic static final String LONGITUDE = \"longitude\";\n\t\tpublic static final String LATITUDE = \"latitude\";\n\t\tpublic static final String POST_ID = \"postID\";\n\n\t\tpublic static final String REFRESH_TIME = \"refreshTime\";// 临时城市列表刷新时间\n\t\tpublic static final String PUB_TIME = \"pubTime\";// 临时城市列表刷新时间\n\t\tpublic static final String WEATHER_INFO = \"weatherInfo\";// 临时城市列表刷新时间\n\t\tpublic static final String ISLOCATION = \"isLocation\";\n\t\tpublic static final String ORDER_INDEX = \"orderIndex\";\n\n\t\tpublic static final String DEFAULT_SORT_ORDER = \"_id ASC\"; // 默认按照_id排序\n\n\t\tprivate CityConstants() {\n\t\t}\n\n\t\tpublic static ArrayList<String> getRequiredColumns() {\n\t\t\tArrayList<String> tmpList = new ArrayList<String>();\n\t\t\t// tmpList.add(PROVINCE);\n\t\t\t// tmpList.add(CITY);\n\t\t\ttmpList.add(NAME);\n\t\t\t// tmpList.add(PINYIN);\n\n\t\t\t// tmpList.add(PY);\n\t\t\t// tmpList.add(PHONE_CODE);\n\t\t\t// tmpList.add(AREA_CODE);\n\t\t\t// tmpList.add(LONGITUDE);\n\t\t\t// tmpList.add(LATITUDE);\n\t\t\ttmpList.add(POST_ID);\n\t\t\ttmpList.add(REFRESH_TIME);\n\t\t\treturn tmpList;\n\t\t}\n\n\t\tpublic static String[] getCityDefaultProjection() {\n\t\t\treturn new String[] { PROVINCE, CITY, NAME, PINYIN, PY, PHONE_CODE,\n\t\t\t\t\tAREA_CODE, POST_ID };\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/fragment/BaseFragment.java",
    "content": "package com.way.fragment;\n\n\nimport java.io.Serializable;\n\nimport android.app.Activity;\nimport android.app.Fragment;\nimport android.os.Bundle;\nimport android.text.TextUtils;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.way.yahoo.App;\nimport com.way.yahoo.R;\n\n/**\n * Created by wangdan on 15-1-16.\n */\npublic abstract class BaseFragment extends Fragment implements ITaskManager {\n    static final String TAG = BaseFragment.class.getSimpleName();\n\n    protected enum ABaseTaskState {\n        none, prepare, falid, success, finished, canceled\n    }\n\n    private ViewGroup rootView;// 根视图\n    private TaskManager taskManager;// 管理线程\n\n    View loadingLayout;// 加载中视图\n    View loadFailureLayout;// 加载失败视图\n    View contentLayout;// 内容视图\n    View emptyLayout;// 空视图\n\n    // 标志是否ContentView是否为空\n    private boolean contentEmpty = true;\n\n    @Override\n    public void onAttach(Activity activity) {\n        super.onAttach(activity);\n    }\n\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        taskManager = new TaskManager();\n        if (savedInstanceState != null)\n            taskManager.restore(savedInstanceState);\n    }\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        if (inflateContentView() > 0) {\n            rootView = (ViewGroup) inflater.inflate(inflateContentView(), null);\n            rootView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));\n\n            _layoutInit(inflater, savedInstanceState);\n\n            layoutInit(inflater, savedInstanceState);\n\n            return rootView;\n        }\n\n        return super.onCreateView(inflater, container, savedInstanceState);\n    }\n\n    /**\n     * 根视图\n     *\n     * @return\n     */\n    ViewGroup getRootView() {\n        return rootView;\n    }\n\n    @Override\n    public void onActivityCreated(Bundle savedInstanceState) {\n        super.onActivityCreated(savedInstanceState);\n\n        if (savedInstanceState == null)\n            requestData();\n    }\n\n    /**\n     * Action的home被点击了\n     *\n     * @return\n     */\n    public boolean onHomeClick() {\n        return onBackClick();\n    }\n\n    /**\n     * 返回按键被点击了\n     *\n     * @return\n     */\n    public boolean onBackClick() {\n        return false;\n    }\n\n    /**\n     * 初次创建时默认会调用一次\n     */\n    public void requestData() {\n\n    }\n\n    /**\n     * A*Fragment重写这个方法\n     *\n     * @param inflater\n     * @param savedInstanceSate\n     */\n    void _layoutInit(LayoutInflater inflater, Bundle savedInstanceSate) {\n        loadingLayout = rootView.findViewById(R.id.layoutLoading);\n        loadFailureLayout = rootView.findViewById(R.id.layoutLoadFailed);\n        contentLayout = rootView.findViewById(R.id.layoutContent);\n        emptyLayout = rootView.findViewById(R.id.layoutEmpty);\n        if (emptyLayout != null) {\n            View reloadView = emptyLayout.findViewById(R.id.layoutReload);\n            if (reloadView != null)\n                setViewOnClick(reloadView);\n        }\n\n        if (loadFailureLayout != null) {\n            View reloadView = loadFailureLayout.findViewById(R.id.layoutReload);\n            if (reloadView != null)\n                setViewOnClick(reloadView);\n        }\n\n        setViewVisiable(loadingLayout, View.GONE);\n        setViewVisiable(loadFailureLayout, View.GONE);\n        setViewVisiable(emptyLayout, View.GONE);\n        if (isContentEmpty()) {\n//            if (savedInstanceSate != null) {\n//                requestData();\n//            }\n//            else {\n                setViewVisiable(emptyLayout, View.VISIBLE);\n                setViewVisiable(contentLayout, View.GONE);\n//            }\n        }\n        else {\n            setViewVisiable(contentLayout, View.VISIBLE);\n        }\n    }\n\n    /**\n     * 子类重写这个方法，初始化视图\n     *\n     * @param inflater\n     * @param savedInstanceSate\n     */\n    protected void layoutInit(LayoutInflater inflater, Bundle savedInstanceSate) {\n\n    }\n\n    protected View findViewById(int viewId) {\n        if (rootView == null)\n            return null;\n\n        return rootView.findViewById(viewId);\n    }\n\n    abstract protected int inflateContentView();\n\n    public void setContentEmpty(boolean empty) {\n        this.contentEmpty = empty;\n    }\n\n    public boolean isContentEmpty() {\n        return contentEmpty;\n    }\n\n    /**\n     * 视图点击回调，子类重写\n     *\n     * @param view\n     */\n    public void onViewClicked(View view) {\n        if (view.getId() == R.id.layoutReload)\n            requestData();\n//        else if (view.getId() == R.id.layoutRefresh)\n//            requestData();\n    }\n\n    void setViewVisiable(View v, int visibility) {\n        if (v != null)\n            v.setVisibility(visibility);\n    }\n\n    /**\n     * 根据{@link ABaseTask} 的加载状态，刷新视图\n     *\n     * @param state\n     *\n     * @param tag\n     */\n    protected void taskStateChanged(ABaseTaskState state, Serializable tag) {\n        // 开始Task\n        if (state == ABaseTaskState.prepare) {\n            if (isContentEmpty()) {\n                setViewVisiable(loadingLayout, View.VISIBLE);\n\n                setViewVisiable(contentLayout, View.GONE);\n            }\n            else {\n                setViewVisiable(loadingLayout, View.GONE);\n\n                setViewVisiable(contentLayout, View.VISIBLE);\n            }\n\n            setViewVisiable(emptyLayout, View.GONE);\n            setViewVisiable(loadFailureLayout, View.GONE);\n        }\n        // Task成功\n        else if (state == ABaseTaskState.success) {\n            setViewVisiable(loadingLayout, View.GONE);\n\n            if (isContentEmpty()) {\n                setViewVisiable(emptyLayout, View.VISIBLE);\n            }\n            else {\n                setViewVisiable(contentLayout, View.VISIBLE);\n            }\n        }\n        // 取消Task\n        else if (state == ABaseTaskState.canceled) {\n            if (isContentEmpty()) {\n                setViewVisiable(loadingLayout, View.GONE);\n                setViewVisiable(emptyLayout, View.VISIBLE);\n            }\n        }\n        // Task失败\n        else if (state == ABaseTaskState.falid) {\n            if (isContentEmpty()) {\n                setViewVisiable(emptyLayout, View.GONE);\n                setViewVisiable(loadingLayout, View.GONE);\n                setViewVisiable(loadFailureLayout, View.VISIBLE);\n                if (tag != null && loadFailureLayout != null)\n                    setTextViewValue(loadFailureLayout, R.id.txtLoadFailed, tag.toString());\n            }\n        }\n        // Task结束\n        else if (state == ABaseTaskState.finished) {\n\n        }\n    }\n\tpublic void setTextViewValue(View container, int txtId, String content) {\n\t\t((TextView) container.findViewById(txtId)).setText(content);\n\t}\n    /**\n     * 以Toast形式显示一个消息\n     *\n     * @param msg\n     */\n    protected void showMessage(CharSequence msg) {\n        if (!TextUtils.isEmpty(msg))\n        \tToast.makeText(App.getApplication(), msg.toString(), Toast.LENGTH_SHORT).show();\n    }\n\n    /**\n     * 参照{@linkplain #showMessage(String)}\n     *\n     * @param msgId\n     */\n    protected void showMessage(int msgId) {\n        if (getActivity() != null)\n            showMessage(getString(msgId));\n    }\n\n    /**\n     * Fragment主要的刷新任务线程，定义任务加载流程，耦合Fragment各个状态下的视图刷新方法\n     * {@link ABaseFragment#taskStateChanged(ABaseTaskState, Object)}\n     *\n     * @author wangdan\n     *\n     * @param <Params>\n     * @param <Progress>\n     * @param <Result>\n     */\n    protected abstract class ABaseTask<Params, Progress, Result> extends WorkTask<Params, Progress, Result> {\n\n        public ABaseTask(String taskId) {\n            super(taskId, BaseFragment.this);\n        }\n\n        @Override\n        protected void onPrepare() {\n            super.onPrepare();\n\n            taskStateChanged(ABaseTaskState.prepare, null);\n        }\n\n        @Override\n        protected void onSuccess(Result result) {\n            super.onSuccess(result);\n\n            // 默认加载数据成功，且ContentView有数据展示\n            BaseFragment.this.setContentEmpty(resultIsEmpty(result));\n\n            taskStateChanged(ABaseTaskState.success, null);\n        }\n\n        @Override\n        protected void onFailure(TaskException exception) {\n            super.onFailure(exception);\n\n            taskStateChanged(ABaseTaskState.falid, exception.getMessage());\n        }\n\n        @Override\n        protected void onFinished() {\n            super.onFinished();\n\n            taskStateChanged(ABaseTaskState.finished, null);\n        }\n\n        /**\n         * 返回数据是否空\n         *\n         * @param result\n         * @return\n         */\n        protected boolean resultIsEmpty(Result result) {\n            return result == null ? true : false;\n        }\n\n    }\n\n    @Override\n    public void onDestroy() {\n        try {\n            // 4.1.1必报错，不知道为什么\n            super.onDestroy();\n        } catch (Exception e) {\n            //Logger.logExc(e);\n        }\n\n        removeAllTask(true);\n    }\n\n    @Override\n    public void onDetach() {\n        super.onDetach();\n\n    }\n\n    @Override\n    final public void addTask(@SuppressWarnings(\"rawtypes\") WorkTask task) {\n        taskManager.addTask(task);\n    }\n\n    @Override\n    final public void removeTask(String taskId, boolean cancelIfRunning) {\n        taskManager.removeTask(taskId, cancelIfRunning);\n    }\n\n    @Override\n    final public void removeAllTask(boolean cancelIfRunning) {\n        taskManager.removeAllTask(cancelIfRunning);\n    }\n\n    @Override\n    final public int getTaskCount(String taskId) {\n        return taskManager.getTaskCount(taskId);\n    }\n\n    protected void cleatTaskCount(String taskId) {\n        taskManager.cleatTaskCount(taskId);\n    }\n\n    protected void setViewOnClick(View v) {\n        if (v == null)\n            return;\n\n        v.setOnClickListener(innerOnClickListener);\n    }\n\n    View.OnClickListener innerOnClickListener = new View.OnClickListener() {\n\n        @Override\n        public void onClick(View v) {\n            onViewClicked(v);\n        }\n\n    };\n\n    @Override\n    public void onSaveInstanceState(Bundle outState) {\n        super.onSaveInstanceState(outState);\n\n        if (taskManager != null)\n            taskManager.save(outState);\n    }\n\n    protected ITaskManager getTaskManager() {\n        return taskManager;\n    }\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/fragment/IExceptionDeclare.java",
    "content": "package com.way.fragment;\n\npublic interface IExceptionDeclare {\n\n\tpublic String declareMessage(String code);\n\t\n}\n\n"
  },
  {
    "path": "WayHoo/src/com/way/fragment/ITaskManager.java",
    "content": "package com.way.fragment;\n\npublic interface ITaskManager {\n\n\tpublic void addTask(WorkTask task);\n\n\tpublic void removeTask(String taskId, boolean cancelIfRunning);\n\n\tpublic void removeAllTask(boolean cancelIfRunning);\n\t\n\tpublic int getTaskCount(String taskId);\n\t\n}"
  },
  {
    "path": "WayHoo/src/com/way/fragment/TaskException.java",
    "content": "package com.way.fragment;\n\n\nimport android.content.res.Resources;\nimport android.text.TextUtils;\n\nimport com.way.yahoo.App;\nimport com.way.yahoo.R;\n\n/**\n * 应用的异常申明<br/>\n * 1、包含四种基本环境类型错误申明<br/>\n * 2、业务类型异常，如果没有设置msg字段，请初始化Declare获取msg信息\n *\n */\npublic class TaskException extends Exception {\n\n\tprivate static final long serialVersionUID = -6262214243381380676L;\n\n\tpublic enum TaskError {\n\t\t// 无网络链接\n\t\tnoneNetwork, \n\t\t// 连接超时\n\t\ttimeout, \n\t\t// 响应超时\n\t\tsocketTimeout,\n\t\t// 返回数据不合法\n\t\tresultIllegal\n\t}\n\t\n\tprivate String code;\n\n    private String msg;\n\t\n\tprivate static IExceptionDeclare exceptionDeclare;\n\t\n\tpublic TaskException(String code) {\n\t\tthis.code = code;\n\t}\n\n    public TaskException(String code, String msg) {\n        this(code);\n        this.msg = msg;\n    }\n\n    public String getCode() {\n        return code;\n    }\n\n\t@Override\n\tpublic String getMessage() {\n        if (!TextUtils.isEmpty(msg))\n            return msg;\n\n\t\tif (!TextUtils.isEmpty(code) && exceptionDeclare != null) {\n\t\t\tString msg = exceptionDeclare.declareMessage(code);\n\t\t\tif (!TextUtils.isEmpty(msg)) {\n\t\t\t\treturn msg;\n\t\t\t}\n\t\t}\n\n        try {\n            Resources res = App.getApplication().getResources();\n\n            TaskError error = TaskError.valueOf(code);\n            if (error == TaskError.noneNetwork)\n                msg = res.getString(R.string.comm_error_noneNetwork);\n            else if (error == TaskError.socketTimeout || error == TaskError.timeout)\n                msg = res.getString(R.string.comm_error_timeout);\n            else if (error == TaskError.resultIllegal)\n                msg = res.getString(R.string.comm_error_resultIllegal);\n            if (!TextUtils.isEmpty(msg))\n                return msg;\n        } catch (Exception e) {\n        }\n\n\t\treturn super.getMessage() + \"\";\n\t}\n\t\n\tpublic static void config(IExceptionDeclare declare) {\n\t\tTaskException.exceptionDeclare = declare;\n\t}\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/fragment/TaskManager.java",
    "content": "package com.way.fragment;\n\nimport java.lang.ref.WeakReference;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.Set;\n\nimport android.os.Bundle;\nimport android.text.TextUtils;\nimport android.util.Log;\n\n\npublic class TaskManager implements ITaskManager {\n\t\n\tstatic final String TAG = \"TaskManager\";\n\n\tprivate LinkedHashMap<String, WeakReference<WorkTask>> taskCache;\n\tprivate HashMap<String, Integer> taskCountMap;\n\n\tpublic TaskManager() {\n\t\ttaskCountMap = new HashMap<String, Integer>();\n\t\ttaskCache = new LinkedHashMap<String, WeakReference<WorkTask>>();\n\t}\n\n\t@Override\n\tpublic void addTask(WorkTask task) {\n\t\tif (task != null && !TextUtils.isEmpty(task.getTaskId())) {\n\t\t\tint count = taskCountMap.keySet().contains(task.getTaskId()) ? taskCountMap.get(task.getTaskId()) : 0;\n\t\t\ttaskCountMap.put(task.getTaskId(), ++count);\n\t\t\tcancelExistTask(task.getTaskId(), true);\n\n\t\t\ttaskCache.put(task.getTaskId(), new WeakReference<WorkTask>(task));\n\n\t\t\tLog.d(TAG, String.format(\"addTask() --->%s\", task.getTaskId()));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void removeTask(String taskId, boolean cancelIfRunning) {\n\t\tcancelExistTask(taskId, cancelIfRunning);\n\t}\n\n\t@Override\n\tpublic void removeAllTask(boolean mayInterruptIfRunning) {\n\t\tSet<String> keySet = taskCache.keySet();\n\t\tfor (String key : keySet) {\n\t\t\tWorkTask task = getTaskById(key);\n\t\t\tif (task != null)\n\t\t\t\ttask.cancel(mayInterruptIfRunning);\n\t\t}\n\t\ttaskCache.clear();\n\t}\n\n\tprivate void cancelExistTask(String taskId, boolean mayInterruptIfRunning) {\n\t\tWorkTask existTask = getTaskById(taskId);\n\n\t\tif (existTask != null)\n\t\t\tLog.d(TAG, String.format(\"interrupt exist task --->%s\", taskId));\n\n\t\tif (existTask != null)\n\t\t\texistTask.cancel(mayInterruptIfRunning);\n\n\t\ttaskCache.remove(taskId);\n\t}\n\n\tprivate WorkTask getTaskById(String taskId) {\n\t\tWeakReference<WorkTask> existTaskRef = taskCache.get(taskId);\n\t\tif (existTaskRef != null)\n\t\t\treturn existTaskRef.get();\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic int getTaskCount(String taskId) {\n\t\tif(TextUtils.isEmpty(taskId))\n\t\t\treturn 0;\n\t\t\n\t\treturn taskCountMap.keySet().contains(taskId) ? taskCountMap.get(taskId) : 0;\n\t}\n\t\n\tpublic void cleatTaskCount(String taskId) {\n\t\tif(!TextUtils.isEmpty(taskId))\n\t\t\ttaskCountMap.remove(taskId);\n\t}\n\t\n\tpublic void save(Bundle outState) {\n\t\toutState.putSerializable(\"map\", taskCountMap);\n\t}\n\t\n\tpublic void restore(Bundle savedInstanceState) {\n\t\tif (savedInstanceState.getSerializable(\"map\") != null) {\n\t\t\ttaskCountMap = (HashMap<String, Integer>) savedInstanceState.getSerializable(\"map\");\n\t\t}\n\t}\n\n}\n\n"
  },
  {
    "path": "WayHoo/src/com/way/fragment/WeatherFragment.java",
    "content": "package com.way.fragment;\n\nimport java.io.Serializable;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\n\nimport org.json.JSONException;\n\nimport android.app.Fragment;\nimport android.content.ContentResolver;\nimport android.content.ContentValues;\nimport android.database.Cursor;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.support.v4.widget.SwipeRefreshLayout;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AbsListView;\nimport android.widget.AbsListView.LayoutParams;\nimport android.widget.AbsListView.OnScrollListener;\nimport android.widget.ImageView;\nimport android.widget.ListView;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.android.volley.DefaultRetryPolicy;\nimport com.android.volley.toolbox.RequestFuture;\nimport com.android.volley.toolbox.StringRequest;\nimport com.way.adapter.WeatherListAdapter;\nimport com.way.beans.City;\nimport com.way.common.util.NetUtil;\nimport com.way.common.util.SystemUtils;\nimport com.way.common.util.TimeUtils;\nimport com.way.common.util.WeatherIconUtils;\nimport com.way.db.CityProvider;\nimport com.way.db.CityProvider.CityConstants;\nimport com.way.fragment.BaseFragment.ABaseTask;\nimport com.way.weather.plugin.bean.Forecast;\nimport com.way.weather.plugin.bean.RealTime;\nimport com.way.weather.plugin.bean.WeatherInfo;\nimport com.way.weather.plugin.spider.WeatherSpider;\nimport com.way.yahoo.App;\nimport com.way.yahoo.MainActivity;\nimport com.way.yahoo.R;\n\npublic class WeatherFragment extends Fragment implements ITaskManager,\n\t\tSwipeRefreshLayout.OnRefreshListener {\n\tpublic static final String ARG_CITY = \"city\";\n\tpublic static final String ALPHA_KEY = \"alpha\";\n\tprivate Handler mHandler = new Handler();\n\tprivate SwipeRefreshLayout mSwipeRefreshLayout;\n\tprivate ListView mListView;\n\tprivate WeatherListAdapter mWeatherAdapter;\n\tprivate ImageView mNormalImageView;\n\tprivate ImageView mBlurredImageView;\n\tprivate View mListHeaderView;\n\n\tprivate int mHeaderHeight = -1;\n\n\t// 当前天气的View\n\tprivate ImageView mCurWeatherIV;\n\tprivate TextView mCurWeatherTV;\n\tprivate TextView mCurHighTempTV;\n\tprivate TextView mCurLowTempTV;\n\tprivate TextView mCurFeelsTempTV;\n\tprivate TextView mCurWeatherCopyTV;\n\n\tprivate ContentResolver mContentResolver;\n\tprivate MainActivity mActivity;\n\tprivate City mCurCity;\n\n\tpublic WeatherFragment() {\n\t}\n\n\tpublic static WeatherFragment newInstance(City city) {\n\t\tWeatherFragment fragment = new WeatherFragment();\n\t\tBundle bundle = new Bundle();\n\t\tbundle.putParcelable(ARG_CITY, city);\n\t\tfragment.setArguments(bundle);\n\t\treturn fragment;\n\t}\n\n\t@Override\n\tpublic void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\ttaskManager = new TaskManager();\n\t\tif (savedInstanceState != null)\n\t\t\ttaskManager.restore(savedInstanceState);\n\t\tmActivity = (MainActivity) getActivity();\n\t\tmContentResolver = getActivity().getContentResolver();\n\t}\n\n\tprivate View mRootView;\n\t// 分别表示当前Fragment是否可见,是否已准备(表示已经走过onCreateView方法)以及是否数据已加载\n\tprivate boolean isVisible = false;\n\tprivate boolean isPrepared = false;\n\tprivate boolean isLoaded = false;\n\n\t/**\n\t * 不提供覆写，需监听可见性的子类可覆写{@link #onFragmentVisible()}和\n\t * {@link #onFragmentInvisible()}方法\n\t * \n\t * @param isVisibleToUser\n\t *            当前Fragment的可见性\n\t */\n\t@Override\n\tpublic final void setUserVisibleHint(boolean isVisibleToUser) {\n\t\tsuper.setUserVisibleHint(isVisibleToUser);\n\n\t\tisVisible = isVisibleToUser;\n\t\tif (getUserVisibleHint()) {\n\t\t\tmHandler.removeCallbacks(delayRefresh);\n\t\t\tmHandler.postDelayed(delayRefresh, 500);\n\t\t} else {\n\t\t\tmHandler.removeCallbacks(delayRefresh);\n\t\t}\n\t}\n\n\t/**\n\t * 在Fragment可见时进行判断是否载入数据\n\t */\n\tprivate void onLoadedData() {\n\t\tif (!isPrepared /* || !isVisible */)\n\t\t\treturn;\n\t\tif (isLoaded && !isNeedRequestNet()) {\n\t\t\treturn;\n\t\t} else {\n\t\t\trequestData(false);\n\t\t}\n\t}\n\n\tprivate boolean isNeedRequestNet() {\n\t\tif (!isVisible)\n\t\t\treturn false;\n\t\tint netState = NetUtil.getNetworkState(getActivity());\n\t\tif (netState == NetUtil.NETWORN_NONE) {\n\t\t\treturn false;\n\t\t}\n\t\tlong refreshTime = getRefreshTime();\n\t\tif (netState == NetUtil.NETWORN_WIFI) {\n\t\t\treturn ((System.currentTimeMillis() - refreshTime) > (1000 * 60 * 30));// wifi网络30分钟过期\n\t\t}\n\t\tif (netState == NetUtil.NETWORN_MOBILE) {\n\t\t\treturn ((System.currentTimeMillis() - refreshTime) > (1000 * 60 * 60 * 2));// 移动网络两个小时过期\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic View onCreateView(LayoutInflater inflater, ViewGroup container,\n\t\t\tBundle savedInstanceState) {\n\t\tif (mRootView == null) {\n\t\t\tmRootView = inflater.inflate(R.layout.weather_fragment, container,\n\t\t\t\t\tfalse);\n\t\t\tinitViews(mRootView, savedInstanceState);\n\t\t\tisPrepared = true;\n\t\t\tisLoaded = false;\n\t\t\t// if (isVisible) {\n\t\t\tmHandler.removeCallbacks(delayRefresh);\n\t\t\tmHandler.post(delayRefresh);\n\n\t\t\t// } else {\n\t\t\t// loadWeatherInfoFromLocal();\n\t\t\t// }\n\t\t} else {\n\t\t\t// ViewGroup mRootParent = (ViewGroup) mRootView.getParent();\n\t\t\t// if (mRootParent != null) {\n\t\t\t// mRootParent.removeView(mRootView);\n\t\t\t// }\n\t\t\t// onLoadedData();\n\t\t}\n\t\treturn mRootView;\n\t}\n\n\tRunnable delayRefresh = new Runnable() {\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tonLoadedData();\n\t\t}\n\t};\n\n\tRunnable startRefreshAnim = new Runnable() {\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tif (!mSwipeRefreshLayout.isRefreshing())\n\t\t\t\tmSwipeRefreshLayout.setRefreshing(true);\n\t\t}\n\t};\n\tRunnable stopRefreshAnim = new Runnable() {\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tif (mSwipeRefreshLayout.isRefreshing())\n\t\t\t\tmSwipeRefreshLayout.setRefreshing(false);\n\t\t}\n\t};\n\n\t@Override\n\tpublic void onDestroyView() {\n\t\tsuper.onDestroyView();\n\t\tif (isNeedDestroy()) {\n\t\t\tmRootView = null;\n\t\t\tisVisible = false;\n\t\t\tisPrepared = false;\n\t\t\tisLoaded = false;\n\t\t}\n\t}\n\n\t/**\n\t * 如果此Fragment占用的数据量过大，可覆写此方法返回true， 表示需要当Fragment无效时进行数据清理，然后覆写\n\t * {@link #onClearDataset()}方法 对占用内存的数据进行清理和对视图控件引用的释放(否则内存不会释放)，\n\t * 这样此Fragment再次回到台前时会重新加载所有的数据\n\t * \n\t * @return true表示子类选择了数据销毁，当Fragment不可见的时候，这样下次Fragment可见时会重新加载数据\n\t */\n\tprotected boolean isNeedDestroy() {\n\t\treturn false;\n\t}\n\n\t/**\n\t * 初始化所有的view\n\t * \n\t * @param view\n\t */\n\tprivate void initViews(View view, Bundle savedInstanceState) {\n\t\tmSwipeRefreshLayout = (SwipeRefreshLayout) view\n\t\t\t\t.findViewById(R.id.swiperefresh);\n\t\tmSwipeRefreshLayout.setOnRefreshListener(this);\n\t\tmListView = (ListView) view.findViewById(R.id.drag_list);\n\n\t\tmNormalImageView = (ImageView) view\n\t\t\t\t.findViewById(R.id.weather_background);\n\t\tmBlurredImageView = (ImageView) view\n\t\t\t\t.findViewById(R.id.weather_background_blurred);\n\t\tif (savedInstanceState != null) {\n\t\t\tfloat alpha = savedInstanceState.getFloat(ALPHA_KEY);\n\t\t\tmBlurredImageView.setAlpha(alpha);\n\t\t} else {\n\t\t\tmBlurredImageView.setAlpha(0f);// 设置默认模糊背景为透明\n\t\t}\n\n\t\tmListHeaderView = LayoutInflater.from(getActivity()).inflate(\n\t\t\t\tR.layout.weather_current_condition, null);\n\t\t// 获取屏幕高度\n\t\tint displayHeight = SystemUtils.getDisplayHeight(getActivity());\n\t\t// HeaderView高度=屏幕高度-标题栏高度\n\t\tmHeaderHeight = displayHeight\n\t\t\t\t- getResources().getDimensionPixelSize(\n\t\t\t\t\t\tR.dimen.abs__action_bar_default_height);\n\t\tmListHeaderView.setLayoutParams(new LayoutParams(\n\t\t\t\tLayoutParams.MATCH_PARENT, mHeaderHeight));\n\t\t// 计算背景View的高度，适当比屏幕高度多一点，\n\t\t// 之所以多1/8是为了后面滑动ListView时背景能跟随滑动。\n\t\t// int backgroundHeight = displayHeight + mHeaderHeight / 8;\n\t\t// mNormalImageView.setLayoutParams(new FrameLayout.LayoutParams(\n\t\t// FrameLayout.LayoutParams.MATCH_PARENT, backgroundHeight));\n\t\t// mBlurredImageView.setLayoutParams(new FrameLayout.LayoutParams(\n\t\t// FrameLayout.LayoutParams.MATCH_PARENT, backgroundHeight));\n\n\t\tmListView.addHeaderView(mListHeaderView, null, false);// 给ListView添加HeaderView\n\t\tmWeatherAdapter = new WeatherListAdapter(getActivity());\n\t\tmListView.setAdapter(mWeatherAdapter);\n\t\tmListView.setOnScrollListener(mOnScrollListener);// 监听滑动\n\t\tinitCurWeatherViews(view);\n\n\t}\n\n\t/**\n\t * 初始化当前天气的view，即ListView的HeaderView\n\t * \n\t * @param view\n\t */\n\tprivate void initCurWeatherViews(View view) {\n\t\tmCurWeatherIV = (ImageView) view.findViewById(R.id.main_icon);\n\t\tmCurWeatherTV = (TextView) view.findViewById(R.id.weather_description);\n\t\tmCurHighTempTV = (TextView) view.findViewById(R.id.temp_high);\n\t\tmCurLowTempTV = (TextView) view.findViewById(R.id.temp_low);\n\t\tmCurFeelsTempTV = (TextView) view.findViewById(R.id.temperature);\n\t\tmCurWeatherCopyTV = (TextView) view.findViewById(R.id.copyright);\n\t}\n\n\t@Override\n\tpublic void onResume() {\n\t\tsuper.onResume();\n\t}\n\n\t// ListView滑动监听，更新背景模糊度和移动距离\n\tprivate OnScrollListener mOnScrollListener = new OnScrollListener() {\n\t\t@Override\n\t\tpublic void onScroll(AbsListView view, int firstVisibleItem,\n\t\t\t\tint visibleItemCount, int totalItemCount) {\n\t\t\tView topChild = view.getChildAt(0);// 获取ListView的第一个View\n\t\t\tif (topChild == null) {\n\t\t\t\tonNewScroll(0);\n\t\t\t} else if (topChild != mListHeaderView) {\n\t\t\t\tonNewScroll(mListHeaderView.getHeight());\n\t\t\t} else {\n\t\t\t\tonNewScroll(-topChild.getTop());\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void onScrollStateChanged(AbsListView view, int scrollState) {\n\t\t\t// do nothing\n\t\t}\n\t};\n\n\t/**\n\t * 更新背景模糊度和移动距离\n\t * \n\t * @param scrollPosition\n\t */\n\tprivate void onNewScroll(int scrollPosition) {\n\t\t// 控制模糊背景的alpha值\n\t\tfloat ratio = Math.min(1.5f * (-mListHeaderView.getTop())\n\t\t\t\t/ mHeaderHeight, 1.0f);\n\t\t// Apply on the ImageView if needed\n\t\tmBlurredImageView.setAlpha(ratio);\n\n\t\t// 控制背景滑动距离\n\t\t// int dampedScroll = Math.round(scrollPosition * 0.125f);\n\t\t// int offset = mLastDampedScroll - dampedScroll;\n\t\t// mBlurredImageView.offsetTopAndBottom(offset);\n\t\t// mNormalImageView.offsetTopAndBottom(offset);\n\t\t// L.i(\"liweiping\", \"offset = \" + offset);\n\t\t// mLastDampedScroll = dampedScroll;\n\t}\n\n\tprivate WeatherInfo loadWeatherInfoFromLocal() throws TaskException {\n\t\tif (mCurCity == null)\n\t\t\tmCurCity = getArguments().getParcelable(ARG_CITY);\n\t\ttry {\n\t\t\tWeatherInfo weatherInfo = WeatherSpider.getWeatherInfo(mActivity,\n\t\t\t\t\tmCurCity.getPostID(), mCurCity.getWeatherInfoStr());\n\t\t\tif (!WeatherSpider.isEmpty(weatherInfo)) {\n\t\t\t\treturn weatherInfo;\n\t\t\t}\n\t\t} catch (JSONException e) {\n\t\t\tthrow new TaskException(\n\t\t\t\t\tTaskException.TaskError.resultIllegal.toString());\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate WeatherInfo loadWeatherInfoFromNetwork() throws TaskException {\n\t\tif (NetUtil.getNetworkState(mActivity) == NetUtil.NETWORN_NONE)\n\t\t\tthrow new TaskException(\n\t\t\t\t\tTaskException.TaskError.noneNetwork.toString());\n\t\tif (mCurCity == null)\n\t\t\tmCurCity = getArguments().getParcelable(ARG_CITY);\n\t\tfinal String postID = mCurCity.getPostID();\n\t\treturn WeatherSpider.getWeatherInfo(postID);\n\n\t}\n\n\tprotected void save2Database(WeatherInfo weatherInfo, String postID,\n\t\t\tString response) {\n\t\tlong pubTime = weatherInfo.getRealTime().getPub_time();\n\t\tlong savePubTime = getPubTime(postID);\n\t\tif (pubTime != savePubTime) {\n\t\t\tContentValues contentValues = new ContentValues();\n\t\t\tcontentValues.put(CityConstants.REFRESH_TIME,\n\t\t\t\t\tSystem.currentTimeMillis());\n\t\t\tcontentValues.put(CityConstants.PUB_TIME, pubTime);\n\t\t\tcontentValues.put(CityConstants.WEATHER_INFO, response);\n\t\t\tmContentResolver.update(CityProvider.TMPCITY_CONTENT_URI,\n\t\t\t\t\tcontentValues, CityConstants.POST_ID + \"=?\",\n\t\t\t\t\tnew String[] { postID });\n\t\t}\n\t}\n\n\tprivate long getPubTime(String postID) {\n\t\tCursor c = mContentResolver.query(CityProvider.TMPCITY_CONTENT_URI,\n\t\t\t\tnew String[] { CityConstants.PUB_TIME }, CityConstants.POST_ID\n\t\t\t\t\t\t+ \"=?\", new String[] { postID }, null);\n\n\t\tlong time = 0L;\n\t\tif (c.moveToFirst())\n\t\t\ttime = c.getLong(c.getColumnIndex(CityConstants.PUB_TIME));\n\t\treturn time;\n\t}\n\n\tprivate long getRefreshTime() {\n\t\tif (mCurCity == null)\n\t\t\tmCurCity = getArguments().getParcelable(ARG_CITY);\n\t\tCursor c = mContentResolver.query(CityProvider.TMPCITY_CONTENT_URI,\n\t\t\t\tnew String[] { CityConstants.REFRESH_TIME },\n\t\t\t\tCityConstants.POST_ID + \"=?\",\n\t\t\t\tnew String[] { mCurCity.getPostID() }, null);\n\n\t\tlong time = 0L;\n\t\tif (c.moveToFirst())\n\t\t\ttime = c.getLong(c.getColumnIndex(CityConstants.REFRESH_TIME));\n\t\treturn time;\n\t}\n\n\t/**\n\t * 更新天气信息界面\n\t */\n\tprivate void updateWeatherView(WeatherInfo weatherInfo) {\n\n\t\tif (WeatherSpider.isEmpty(weatherInfo)) {\n\t\t\tToast.makeText(mActivity, \"刷新失败...\", Toast.LENGTH_SHORT).show();\n\t\t\treturn;\n\t\t}\n\t\tisLoaded = true;\n\t\tif (weatherInfo.isNewData())\n\t\t\tToast.makeText(mActivity, mCurCity.getName() + \" 刷新成功...\",\n\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\tRealTime realTime = weatherInfo.getRealTime();\n\t\tForecast forecast = weatherInfo.getForecast();\n\n\t\tint type = realTime.getAnimation_type();\n\t\tmNormalImageView.setImageResource(WeatherIconUtils\n\t\t\t\t.getWeatherNromalBg(type));\n\t\tmBlurredImageView.setImageResource(WeatherIconUtils\n\t\t\t\t.getWeatherBlurBg(type));\n\t\tmCurWeatherIV.setImageResource(WeatherIconUtils.getWeatherIcon(type));\n\t\tmCurWeatherTV.setText(realTime.getWeather_name());\n\t\tmCurFeelsTempTV.setText(realTime.getTemp() + \"\");\n\t\tmCurHighTempTV.setText(forecast.getTmpHigh(1) + \"°\");\n\t\tmCurLowTempTV.setText(forecast.getTmpLow(1) + \"°\");\n\t\tmCurWeatherCopyTV.setText(TimeUtils.getDay(getPubTime(mCurCity\n\t\t\t\t.getPostID())) + \"发布\");\n\n\t\tmWeatherAdapter.setWeather(weatherInfo);\n\t}\n\n\t@Override\n\tpublic void onRefresh() {\n\t\trequestData(true);\n\t}\n\n\tpublic void requestData(boolean force) {\n\t\tif (mCurCity == null)\n\t\t\tmCurCity = getArguments().getParcelable(ARG_CITY);\n\t\tnew WeatherTask(mCurCity.getPostID()).execute(force);\n\t}\n\n\tclass WeatherTask extends WorkTask<Boolean, Void, WeatherInfo> {\n\t\tpublic WeatherTask(String postID) {\n\t\t\tsuper(postID, WeatherFragment.this);\n\t\t}\n\n\t\t@Override\n\t\tprotected void onPrepare() {\n\t\t\tsuper.onPrepare();\n\t\t\ttaskStateChanged(ABaseTaskState.prepare, null);\n\t\t}\n\n\t\t@Override\n\t\tpublic WeatherInfo workInBackground(Boolean... params)\n\t\t\t\tthrows TaskException {\n\t\t\tmWeatherAdapter.initViews();\n\t\t\tboolean isForce = params[0];\n\t\t\tWeatherInfo weatherInfo = null;\n\t\t\tif (isNeedRequestNet() || isForce) {\n\t\t\t\ttry {\n\t\t\t\t\tweatherInfo = loadWeatherInfoFromNetwork();\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t}\n\t\t\t\tif (WeatherSpider.isEmpty(weatherInfo)) {\n\t\t\t\t\tweatherInfo = loadWeatherInfoFromLocal();\n\t\t\t\t\tweatherInfo.setNewData(false);\n\t\t\t\t} else {\n\t\t\t\t\tweatherInfo.setNewData(true);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tweatherInfo = loadWeatherInfoFromLocal();\n\t\t\t}\n\t\t\tif (WeatherSpider.isEmpty(weatherInfo))\n\t\t\t\tweatherInfo = loadWeatherInfoFromLocal();\n\t\t\treturn weatherInfo;\n\t\t}\n\n\t\t@Override\n\t\tprotected void onSuccess(WeatherInfo result) {\n\t\t\tsuper.onSuccess(result);\n\t\t\tupdateWeatherView(result);\n\t\t\ttaskStateChanged(ABaseTaskState.success, null);\n\t\t}\n\n\t\t@Override\n\t\tprotected void onFailure(TaskException exception) {\n\t\t\tsuper.onFailure(exception);\n\t\t\ttaskStateChanged(ABaseTaskState.falid, exception.getMessage());\n\t\t}\n\n\t\t@Override\n\t\tprotected void onFinished() {\n\t\t\tsuper.onFinished();\n\t\t\ttaskStateChanged(ABaseTaskState.finished, null);\n\t\t}\n\t}\n\n\t/**\n\t * 根据{@link ABaseTask} 的加载状态，刷新视图\n\t *\n\t * @param state\n\t *\n\t * @param tag\n\t */\n\tprotected void taskStateChanged(ABaseTaskState state, Serializable tag) {\n\t\t// 开始Task\n\t\tif (state == ABaseTaskState.prepare) {\n\t\t\tmHandler.removeCallbacks(startRefreshAnim);\n\t\t\tmHandler.post(startRefreshAnim);\n\t\t}\n\t\t// Task成功\n\t\telse if (state == ABaseTaskState.success) {\n\t\t\tmHandler.removeCallbacks(stopRefreshAnim);\n\t\t\tmHandler.postDelayed(stopRefreshAnim, 1000);\n\t\t}\n\t\t// 取消Task\n\t\telse if (state == ABaseTaskState.canceled) {\n\t\t\tmHandler.removeCallbacks(stopRefreshAnim);\n\t\t\tmHandler.postDelayed(stopRefreshAnim, 1000);\n\t\t}\n\t\t// Task失败\n\t\telse if (state == ABaseTaskState.falid) {\n\t\t\tToast.makeText(mActivity, \"刷新失败...\" + tag, Toast.LENGTH_SHORT)\n\t\t\t\t\t.show();\n\t\t\tmHandler.removeCallbacks(stopRefreshAnim);\n\t\t\tmHandler.postDelayed(stopRefreshAnim, 1000);\n\t\t}\n\t\t// Task结束\n\t\telse if (state == ABaseTaskState.finished) {\n\t\t\tmHandler.removeCallbacks(stopRefreshAnim);\n\t\t\tmHandler.postDelayed(stopRefreshAnim, 1000);\n\n\t\t}\n\t}\n\n\tprivate TaskManager taskManager;// 管理线程\n\n\t@Override\n\tpublic void onSaveInstanceState(Bundle outState) {\n\t\tsuper.onSaveInstanceState(outState);\n\n\t\tif (taskManager != null) {\n\t\t\ttaskManager.save(outState);\n\t\t\toutState.putFloat(ALPHA_KEY, mBlurredImageView.getAlpha());\n\t\t}\n\t}\n\n\tprotected ITaskManager getTaskManager() {\n\t\treturn taskManager;\n\t}\n\n\t@Override\n\tpublic void addTask(WorkTask task) {\n\t\ttaskManager.addTask(task);\n\t}\n\n\t@Override\n\tpublic void removeTask(String taskId, boolean cancelIfRunning) {\n\t\ttaskManager.removeTask(taskId, cancelIfRunning);\n\t}\n\n\t@Override\n\tpublic void removeAllTask(boolean cancelIfRunning) {\n\t\ttaskManager.removeAllTask(cancelIfRunning);\n\t}\n\n\t@Override\n\tpublic int getTaskCount(String taskId) {\n\t\treturn taskManager.getTaskCount(taskId);\n\t}\n\n\tprotected enum ABaseTaskState {\n\t\tnone, prepare, falid, success, finished, canceled\n\t}\n\n\tpublic void refreshUI() {\n\t\tLog.i(\"refreshUI\", \"refreshUI isVisible = \" + isVisible\n\t\t\t\t+ \", isPrepared = \" + isPrepared);\n\t\t// onLoadedData();\n\t}\n\n\tpublic void releaseImageViewByIds() {\n\t\t// TODO Auto-generated method stub\n\n\t}\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/fragment/WorkTask.java",
    "content": "package com.way.fragment;\n\nimport java.util.ArrayDeque;\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.CancellationException;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.FutureTask;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport android.os.Handler;\nimport android.os.Message;\nimport android.text.TextUtils;\nimport android.util.Log;\n\n\npublic abstract class WorkTask<Params, Progress, Result> {\n\tprivate static final String TAG = \"AisenTask\";\n\n\t/**\n\t * 加载图片默认是10个线程\n\t */\n\tprivate static final int CORE_IMAGE_POOL_SIZE = 10;\n\n\t/**\n\t * 默认核心线程是5个\n\t */\n\tprivate static final int CORE_POOL_SIZE = 5;\n\t/**\n\t * 默认执行最大线程是128个\n\t */\n\tprivate static final int MAXIMUM_POOL_SIZE = 128;\n\tprivate static final int KEEP_ALIVE = 1;\n\n\tprivate TaskException exception;\n\n\tprivate static final ThreadFactory sThreadFactory = new ThreadFactory() {\n\t\tprivate final AtomicInteger mCount = new AtomicInteger(1);\n\n\t\tpublic Thread newThread(Runnable r) {\n\t\t\treturn new Thread(r, \"AsyncTask #\" + mCount.getAndIncrement());\n\t\t}\n\t};\n\n\t/**\n\t * 执行队列，默认是10个，超过10个后会开启新的线程，如果已运行线程大于 {@link #MAXIMUM_POOL_SIZE}，执行异常策略\n\t */\n\tprivate static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(10);\n\n\t/**\n\t * 默认线程池，最大执行{@link #CORE_POOL_SIZE}+{@link #MAXIMUM_POOL_SIZE}个线程\n\t */\n\tpublic static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS,\n\t\t\tsPoolWorkQueue, sThreadFactory);\n\n\t/**\n\t * 固定大小为{@link #CORE_IMAGE_POOL_SIZE}的线程池<br/>\n\t * 无界线程池，可以加载无限个线程\n\t */\n\tpublic static final Executor IMAGE_POOL_EXECUTOR = Executors.newFixedThreadPool(CORE_IMAGE_POOL_SIZE, sThreadFactory);\n\n\t/**\n\t * An {@link Executor} that executes tasks one at a time in serial order.\n\t * This serialization is global to a particular process.\n\t */\n\tpublic static final Executor SERIAL_EXECUTOR = new SerialExecutor();\n\n\tprivate static final int MESSAGE_POST_RESULT = 0x1;\n\tprivate static final int MESSAGE_POST_PROGRESS = 0x2;\n\n\tprivate static final InternalHandler sHandler = new InternalHandler();\n\n\tprivate static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;\n\tprivate final WorkerRunnable<Params, Result> mWorker;\n\tprivate final FutureTask<Result> mFuture;\n\n\tprivate volatile Status mStatus = Status.PENDING;\n\n\tprivate final AtomicBoolean mTaskInvoked = new AtomicBoolean();\n\n\tprivate static class SerialExecutor implements Executor {\n\t\tfinal ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();\n\t\tRunnable mActive;\n\n\t\tpublic synchronized void execute(final Runnable r) {\n\t\t\tmTasks.offer(new Runnable() {\n\t\t\t\tpublic void run() {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tr.run();\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tscheduleNext();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t\tif (mActive == null) {\n\t\t\t\tscheduleNext();\n\t\t\t}\n\t\t}\n\n\t\tprotected synchronized void scheduleNext() {\n\t\t\tif ((mActive = mTasks.poll()) != null) {\n\t\t\t\tTHREAD_POOL_EXECUTOR.execute(mActive);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic enum Status {\n\t\t/**\n\t\t * Indicates that the task has not been executed yet.\n\t\t */\n\t\tPENDING,\n\t\t/**\n\t\t * Indicates that the task is running.\n\t\t */\n\t\tRUNNING,\n\t\t/**\n\t\t * Indicates that {@link SyswealTask#onPostExecute} has finished.\n\t\t */\n\t\tFINISHED,\n\t}\n\n\t/** @hide Used to force static handler to be created. */\n\tpublic static void init() {\n\t\tsHandler.getLooper();\n\t}\n\n\tprivate static void setDefaultExecutor(Executor exec) {\n\t\tsDefaultExecutor = exec;\n\t}\n\n\tprivate String taskId;\n\n\tpublic String getTaskId() {\n\t\treturn taskId;\n\t}\n\n\tpublic void setTaskId(String taskId) {\n\t\tthis.taskId = taskId;\n\t}\n\n\tpublic WorkTask(String taskId, ITaskManager taskManager) {\n\t\tthis();\n\t\tthis.taskId = taskId;\n\t\ttaskManager.addTask(this);\n\t}\n\n\tpublic WorkTask() {\n\t\tmWorker = new WorkerRunnable<Params, Result>() {\n\t\t\tpublic Result call() throws Exception {\n\t\t\t\tmTaskInvoked.set(true);\n\n\t\t\t\tandroid.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);\n\t\t\t\treturn postResult(doInBackground(mParams));\n\t\t\t}\n\t\t};\n\n\t\tmFuture = new FutureTask<Result>(mWorker) {\n\t\t\t@Override\n\t\t\tprotected void done() {\n\t\t\t\ttry {\n\t\t\t\t\tfinal Result result = get();\n\n\t\t\t\t\tpostResultIfNotInvoked(result);\n\t\t\t\t} catch (InterruptedException e) {\n\t\t\t\t\tandroid.util.Log.w(TAG, e);\n\t\t\t\t} catch (ExecutionException e) {\n\t\t\t\t\tthrow new RuntimeException(\"An error occured while executing doInBackground()\", e.getCause());\n\t\t\t\t} catch (CancellationException e) {\n\t\t\t\t\tpostResultIfNotInvoked(null);\n\t\t\t\t} catch (Throwable t) {\n\t\t\t\t\tthrow new RuntimeException(\"An error occured while executing \" + \"doInBackground()\", t);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate void postResultIfNotInvoked(Result result) {\n\t\tfinal boolean wasTaskInvoked = mTaskInvoked.get();\n\t\tif (!wasTaskInvoked) {\n\t\t\tpostResult(result);\n\t\t}\n\t}\n\n\tprivate Result postResult(Result result) {\n\t\tMessage message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result));\n\t\tmessage.sendToTarget();\n\t\treturn result;\n\t}\n\n\tpublic final Status getStatus() {\n\t\treturn mStatus;\n\t}\n\n\t/**\n\t * 线程开始执行\n\t */\n\tprotected void onPrepare() {\n\n\t}\n\n\t/**\n\t * {@link #workInBackground(Object...)} 发生异常\n\t */\n\tprotected void onFailure(TaskException exception) {\n\n\t}\n\n\t/**\n\t * 没有抛出异常，且<tt>Result</tt>不为<tt>Null</tt>\n\t */\n\tprotected void onSuccess(Result result) {\n\n\t}\n\n\tprotected Params[] getParams() {\n\t\treturn mWorker.mParams;\n\t}\n\n\t/**\n\t * 线程结束，不管线程结束是什么状态，都会执行这个方法\n\t */\n\tprotected void onFinished() {\n\n\t}\n\n\t/**\n\t * 异步执行方法\n\t * \n\t * @param params\n\t * @return\n\t * @throws TaskException\n\t */\n\tabstract public Result workInBackground(Params... params) throws TaskException;\n\n\tprivate Result doInBackground(Params... params) {\n\t\tLog.d(TAG, String.format(\"%s --->doInBackground()\", TextUtils.isEmpty(taskId) ? \"run \" : (taskId + \" run \")));\n\n\t\ttry {\n\t\t\treturn workInBackground(params);\n\t\t} catch (TaskException e) {\n\t\t\te.printStackTrace();\n\t\t\texception = e;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tfinal protected void onPreExecute() {\n\t\tLog.d(TAG, String.format(\"%s --->onTaskStarted()\", TextUtils.isEmpty(taskId) ? \"run \" : (taskId + \" run \")));\n\t\tonPrepare();\n\t}\n\n\tfinal protected void onPostExecute(Result result) {\n\t\tif (exception == null) {\n//\t\t\tif (result == null) {\n//\t\t\t\tLogger.d(TAG, String.format(\"%s --->resultIsNull()\", TextUtils.isEmpty(taskId) ? \"run \" : (taskId + \" run \")));\n//\t\t\t\tresultIsNull();\n//\t\t\t} else {\n\t\t\tLog.d(TAG, String.format(\"%s --->onTaskSuccess()\", TextUtils.isEmpty(taskId) ? \"run \" : (taskId + \" run \")));\n\t\t\t\tonSuccess(result);\n//\t\t\t}\n\t\t}\n\t\telse if (exception != null) {\n\t\t\tLog.d(\n\t\t\t\t\tTAG,\n\t\t\t\t\tString.format(\"%s --->onTaskFailed(), \\nError msg --->\", TextUtils.isEmpty(taskId) ? \"run \" : (taskId + \" run \"),\n\t\t\t\t\t\t\texception.getMessage()));\n\t\t\tonFailure(exception);\n\t\t}\n\n\t\tLog.d(TAG, String.format(\"%s --->onTaskComplete()\", TextUtils.isEmpty(taskId) ? \"run \" : (taskId + \" run \")));\n\t\tonFinished();\n\t}\n\n\tprotected void onProgressUpdate(Progress... values) {\n\t}\n\n\tprotected void onCancelled(Result result) {\n\t\tonCancelled();\n\t}\n\n\tprotected void onCancelled() {\n\t\tLog.d(TAG, String.format(\"%s --->onTaskComplete()\", TextUtils.isEmpty(taskId) ? \"run \" : (taskId + \" run \")));\n\t\tonFinished();\n\t}\n\n\tpublic final boolean isCancelled() {\n\t\treturn mFuture.isCancelled();\n\t}\n\n\tpublic final boolean cancel(boolean mayInterruptIfRunning) {\n\t\treturn mFuture.cancel(mayInterruptIfRunning);\n\t}\n\n\tpublic final Result get() throws InterruptedException, ExecutionException {\n\t\treturn mFuture.get();\n\t}\n\n\tpublic final Result get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {\n\t\treturn mFuture.get(timeout, unit);\n\t}\n\n\t/**\n\t * 连续执行线程，所有的线程都是按照队列一个一个执行下去的\n\t * \n\t * @param params\n\t * @return\n\t */\n\tpublic final WorkTask<Params, Progress, Result> executeOnSerialExecutor(Params... params) {\n\t\treturn executeOnExecutor(SERIAL_EXECUTOR, params);\n\t}\n\n\t/**\n\t * 加载图片的线程池\n\t * \n\t * @param params\n\t * @return\n\t */\n\tpublic final WorkTask<Params, Progress, Result> executrOnImageExecutor(Params... params) {\n\t\treturn executeOnExecutor(IMAGE_POOL_EXECUTOR, params);\n\t}\n\n\t/**\n\t * 默认线程池{@link #THREAD_POOL_EXECUTOR}\n\t * \n\t * @param params\n\t * @return\n\t */\n\tpublic final WorkTask<Params, Progress, Result> execute(Params... params) {\n\t\treturn executeOnExecutor(THREAD_POOL_EXECUTOR, params);\n\t}\n\n\tpublic final WorkTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) {\n\t\tif (mStatus != Status.PENDING) {\n\t\t\tswitch (mStatus) {\n\t\t\tcase RUNNING:\n\t\t\t\tthrow new IllegalStateException(\"Cannot execute task:\" + \" the task is already running.\");\n\t\t\tcase FINISHED:\n\t\t\t\tthrow new IllegalStateException(\"Cannot execute task:\" + \" the task has already been executed \"\n\t\t\t\t\t\t+ \"(a task can be executed only once)\");\n\t\t\t}\n\t\t}\n\n\t\tmStatus = Status.RUNNING;\n\n\t\tonPreExecute();\n\n\t\tmWorker.mParams = params;\n\t\texec.execute(mFuture);\n\n\t\treturn this;\n\t}\n\n\tpublic static void execute(Runnable runnable) {\n\t\tsDefaultExecutor.execute(runnable);\n\t}\n\n\tprotected final void publishProgress(Progress... values) {\n\t\tif (!isCancelled()) {\n\t\t\tsHandler.obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget();\n\t\t}\n\t}\n\n\tprivate void finish(Result result) {\n\t\tif (isCancelled()) {\n\t\t\tonCancelled(result);\n\t\t} else {\n\t\t\tonPostExecute(result);\n\t\t}\n\t\tmStatus = Status.FINISHED;\n\t}\n\n\tprivate static class InternalHandler extends Handler {\n\t\t@SuppressWarnings({ \"unchecked\" })\n\t\t@Override\n\t\tpublic void handleMessage(Message msg) {\n\t\t\tAsyncTaskResult result = (AsyncTaskResult) msg.obj;\n\t\t\tswitch (msg.what) {\n\t\t\tcase MESSAGE_POST_RESULT:\n\t\t\t\t// There is only one result\n\t\t\t\tresult.mTask.finish(result.mData[0]);\n\t\t\t\tbreak;\n\t\t\tcase MESSAGE_POST_PROGRESS:\n\t\t\t\tresult.mTask.onProgressUpdate(result.mData);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {\n\t\tParams[] mParams;\n\t}\n\n\tprivate static class AsyncTaskResult<Data> {\n\t\tfinal WorkTask mTask;\n\t\tfinal Data[] mData;\n\n\t\tAsyncTaskResult(WorkTask task, Data... data) {\n\t\t\tmTask = task;\n\t\t\tmData = data;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/observablescrollview/CacheFragmentStatePagerAdapter.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.way.observablescrollview;\n\nimport android.os.Bundle;\nimport android.os.Parcelable;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.app.FragmentStatePagerAdapter;\nimport android.util.SparseArray;\nimport android.view.ViewGroup;\n\n/**\n * FragmentStatePagerAdapter that caches each pages.\n * FragmentStatePagerAdapter is also originally caches pages,\n * but its keys are not public nor documented, so depending\n * on how it create cache key is dangerous.\n * This adapter caches pages by itself and provide getter method to the cache.\n */\npublic abstract class CacheFragmentStatePagerAdapter extends FragmentStatePagerAdapter {\n\n    private static final String STATE_SUPER_STATE = \"superState\";\n    private static final String STATE_PAGES = \"pages\";\n    private static final String STATE_PAGE_INDEX_PREFIX = \"pageIndex:\";\n    private static final String STATE_PAGE_KEY_PREFIX = \"page:\";\n\n    private FragmentManager mFm;\n    private SparseArray<Fragment> mPages;\n\n    public CacheFragmentStatePagerAdapter(FragmentManager fm) {\n        super(fm);\n        mPages = new SparseArray<Fragment>();\n        mFm = fm;\n    }\n\n    @Override\n    public Parcelable saveState() {\n        Parcelable p = super.saveState();\n        Bundle bundle = new Bundle();\n        bundle.putParcelable(STATE_SUPER_STATE, p);\n\n        bundle.putInt(STATE_PAGES, mPages.size());\n        if (0 < mPages.size()) {\n            for (int i = 0; i < mPages.size(); i++) {\n                int position = mPages.keyAt(i);\n                bundle.putInt(createCacheIndex(i), position);\n                Fragment f = mPages.get(position);\n                mFm.putFragment(bundle, createCacheKey(position), f);\n            }\n        }\n        return bundle;\n    }\n\n    @Override\n    public void restoreState(Parcelable state, ClassLoader loader) {\n        Bundle bundle = (Bundle) state;\n        int pages = bundle.getInt(STATE_PAGES);\n        if (0 < pages) {\n            for (int i = 0; i < pages; i++) {\n                int position = bundle.getInt(createCacheIndex(i));\n                Fragment f = mFm.getFragment(bundle, createCacheKey(position));\n                mPages.put(position, f);\n            }\n        }\n\n        Parcelable p = bundle.getParcelable(STATE_SUPER_STATE);\n        super.restoreState(p, loader);\n    }\n\n    /**\n     * Get a new Fragment instance.\n     * Each fragments are automatically cached in this method,\n     * so you don't have to do it by yourself.\n     * If you want to implement instantiation of Fragments,\n     * you should override {@link #createItem(int)} instead.\n     *\n     * {@inheritDoc}\n     *\n     * @param position position of the item in the adapter\n     * @return fragment instance\n     */\n    @Override\n    public Fragment getItem(int position) {\n        Fragment f = createItem(position);\n        // We should cache fragments manually to access to them later\n        mPages.put(position, f);\n        return f;\n    }\n\n    @Override\n    public void destroyItem(ViewGroup container, int position, Object object) {\n        if (0 <= mPages.indexOfKey(position)) {\n            mPages.remove(position);\n        }\n        super.destroyItem(container, position, object);\n    }\n\n    /**\n     * Get the item at the specified position in the adapter.\n     *\n     * @param position position of the item in the adapter\n     * @return fragment instance\n     */\n    public Fragment getItemAt(int position) {\n        return mPages.get(position);\n    }\n\n    /**\n     * Create a new Fragment instance.\n     * This is called inside {@link #getItem(int)}.\n     *\n     * @param position position of the item in the adapter\n     * @return fragment instance\n     */\n    protected abstract Fragment createItem(int position);\n\n    /**\n     * Create an index string for caching Fragment pages.\n     *\n     * @param index index of the item in the adapter\n     * @return key string for caching Fragment pages\n     */\n    protected String createCacheIndex(int index) {\n        return STATE_PAGE_INDEX_PREFIX + index;\n    }\n\n    /**\n     * Create a key string for caching Fragment pages.\n     *\n     * @param position position of the item in the adapter\n     * @return key string for caching Fragment pages\n     */\n    protected String createCacheKey(int position) {\n        return STATE_PAGE_KEY_PREFIX + position;\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/observablescrollview/ObservableGridView.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.way.observablescrollview;\n\nimport android.content.Context;\nimport android.os.Build;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.util.AttributeSet;\nimport android.util.SparseIntArray;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AbsListView;\nimport android.widget.GridView;\n\n/**\n * GridView that its scroll position can be observed.\n */\npublic class ObservableGridView extends GridView implements Scrollable {\n\n    // Fields that should be saved onSaveInstanceState\n    private int mPrevFirstVisiblePosition;\n    private int mPrevFirstVisibleChildHeight = -1;\n    private int mPrevScrolledChildrenHeight;\n    private int mPrevScrollY;\n    private int mScrollY;\n    private SparseIntArray mChildrenHeights;\n\n    // Fields that don't need to be saved onSaveInstanceState\n    private ObservableScrollViewCallbacks mCallbacks;\n    private ScrollState mScrollState;\n    private boolean mFirstScroll;\n    private boolean mDragging;\n    private boolean mIntercepted;\n    private MotionEvent mPrevMoveEvent;\n    private ViewGroup mTouchInterceptionViewGroup;\n\n    private OnScrollListener mOriginalScrollListener;\n    private OnScrollListener mScrollListener = new OnScrollListener() {\n        @Override\n        public void onScrollStateChanged(AbsListView view, int scrollState) {\n            if (mOriginalScrollListener != null) {\n                mOriginalScrollListener.onScrollStateChanged(view, scrollState);\n            }\n        }\n\n        @Override\n        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {\n            if (mOriginalScrollListener != null) {\n                mOriginalScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);\n            }\n            // AbsListView#invokeOnItemScrollListener calls onScrollChanged(0, 0, 0, 0)\n            // on Android 4.0+, but Android 2.3 is not. (Android 3.0 is unknown)\n            // So call it with onScrollListener.\n            onScrollChanged();\n        }\n    };\n\n    public ObservableGridView(Context context) {\n        super(context);\n        init();\n    }\n\n    public ObservableGridView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    public ObservableGridView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        init();\n    }\n\n    @Override\n    public void onRestoreInstanceState(Parcelable state) {\n        SavedState ss = (SavedState) state;\n        mPrevFirstVisiblePosition = ss.prevFirstVisiblePosition;\n        mPrevFirstVisibleChildHeight = ss.prevFirstVisibleChildHeight;\n        mPrevScrolledChildrenHeight = ss.prevScrolledChildrenHeight;\n        mPrevScrollY = ss.prevScrollY;\n        mScrollY = ss.scrollY;\n        mChildrenHeights = ss.childrenHeights;\n        super.onRestoreInstanceState(ss.getSuperState());\n    }\n\n    @Override\n    public Parcelable onSaveInstanceState() {\n        Parcelable superState = super.onSaveInstanceState();\n        SavedState ss = new SavedState(superState);\n        ss.prevFirstVisiblePosition = mPrevFirstVisiblePosition;\n        ss.prevFirstVisibleChildHeight = mPrevFirstVisibleChildHeight;\n        ss.prevScrolledChildrenHeight = mPrevScrolledChildrenHeight;\n        ss.prevScrollY = mPrevScrollY;\n        ss.scrollY = mScrollY;\n        ss.childrenHeights = mChildrenHeights;\n        return ss;\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        if (mCallbacks != null) {\n            switch (ev.getActionMasked()) {\n                case MotionEvent.ACTION_DOWN:\n                    // Whether or not motion events are consumed by children,\n                    // flag initializations which are related to ACTION_DOWN events should be executed.\n                    // Because if the ACTION_DOWN is consumed by children and only ACTION_MOVEs are\n                    // passed to parent (this view), the flags will be invalid.\n                    // Also, applications might implement initialization codes to onDownMotionEvent,\n                    // so call it here.\n                    mFirstScroll = mDragging = true;\n                    mCallbacks.onDownMotionEvent();\n                    break;\n            }\n        }\n        return super.onInterceptTouchEvent(ev);\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent ev) {\n        if (mCallbacks != null) {\n            switch (ev.getActionMasked()) {\n                case MotionEvent.ACTION_UP:\n                case MotionEvent.ACTION_CANCEL:\n                    mIntercepted = false;\n                    mDragging = false;\n                    mCallbacks.onUpOrCancelMotionEvent(mScrollState);\n                    break;\n                case MotionEvent.ACTION_MOVE:\n                    if (mPrevMoveEvent == null) {\n                        mPrevMoveEvent = ev;\n                    }\n                    float diffY = ev.getY() - mPrevMoveEvent.getY();\n                    mPrevMoveEvent = MotionEvent.obtainNoHistory(ev);\n                    if (getCurrentScrollY() - diffY <= 0) {\n                        // Can't scroll anymore.\n\n                        if (mIntercepted) {\n                            // Already dispatched ACTION_DOWN event to parents, so stop here.\n                            return false;\n                        }\n\n                        // Apps can set the interception target other than the direct parent.\n                        final ViewGroup parent;\n                        if (mTouchInterceptionViewGroup == null) {\n                            parent = (ViewGroup) getParent();\n                        } else {\n                            parent = mTouchInterceptionViewGroup;\n                        }\n\n                        // Get offset to parents. If the parent is not the direct parent,\n                        // we should aggregate offsets from all of the parents.\n                        float offsetX = 0;\n                        float offsetY = 0;\n                        for (View v = this; v != null && v != parent; v = (View) v.getParent()) {\n                            offsetX += v.getLeft() - v.getScrollX();\n                            offsetY += v.getTop() - v.getScrollY();\n                        }\n                        final MotionEvent event = MotionEvent.obtainNoHistory(ev);\n                        event.offsetLocation(offsetX, offsetY);\n\n                        if (parent.onInterceptTouchEvent(event)) {\n                            mIntercepted = true;\n\n                            // If the parent wants to intercept ACTION_MOVE events,\n                            // we pass ACTION_DOWN event to the parent\n                            // as if these touch events just have began now.\n                            event.setAction(MotionEvent.ACTION_DOWN);\n\n                            // Return this onTouchEvent() first and set ACTION_DOWN event for parent\n                            // to the queue, to keep events sequence.\n                            post(new Runnable() {\n                                @Override\n                                public void run() {\n                                    parent.dispatchTouchEvent(event);\n                                }\n                            });\n                            return false;\n                        }\n                        // Even when this can't be scrolled anymore,\n                        // simply returning false here may cause subView's click,\n                        // so delegate it to super.\n                        return super.onTouchEvent(ev);\n                    }\n                    break;\n            }\n        }\n        return super.onTouchEvent(ev);\n    }\n\n    @Override\n    public void setOnScrollListener(OnScrollListener l) {\n        // Don't set l to super.setOnScrollListener().\n        // l receives all events through mScrollListener.\n        mOriginalScrollListener = l;\n    }\n\n    @Override\n    public void setScrollViewCallbacks(ObservableScrollViewCallbacks listener) {\n        mCallbacks = listener;\n    }\n\n    @Override\n    public void setTouchInterceptionViewGroup(ViewGroup viewGroup) {\n        mTouchInterceptionViewGroup = viewGroup;\n    }\n\n    @Override\n    public void scrollVerticallyTo(int y) {\n        scrollTo(0, y);\n    }\n\n    @Override\n    public int getCurrentScrollY() {\n        return mScrollY;\n    }\n\n    private void init() {\n        mChildrenHeights = new SparseIntArray();\n        super.setOnScrollListener(mScrollListener);\n    }\n\n    private int getNumColumnsCompat() {\n        if (Build.VERSION.SDK_INT >= 11) {\n            return getNumColumns();\n        } else {\n            int columns = 0;\n            if (getChildCount() > 0) {\n                int width = getChildAt(0).getMeasuredWidth();\n                if (width > 0) {\n                    columns = getWidth() / width;\n                }\n            }\n            return columns > 0 ? columns : AUTO_FIT;\n        }\n    }\n\n    private void onScrollChanged() {\n        if (mCallbacks != null) {\n            if (getChildCount() > 0) {\n                int firstVisiblePosition = getFirstVisiblePosition();\n                for (int i = getFirstVisiblePosition(), j = 0; i <= getLastVisiblePosition(); i++, j++) {\n                    if (mChildrenHeights.indexOfKey(i) < 0 || getChildAt(j).getHeight() != mChildrenHeights.get(i)) {\n                        if (i % getNumColumnsCompat() == 0) {\n                            mChildrenHeights.put(i, getChildAt(j).getHeight());\n                        }\n                    }\n                }\n\n                View firstVisibleChild = getChildAt(0);\n                if (firstVisibleChild != null) {\n                    if (mPrevFirstVisiblePosition < firstVisiblePosition) {\n                        // scroll down\n                        int skippedChildrenHeight = 0;\n                        if (firstVisiblePosition - mPrevFirstVisiblePosition != 1) {\n                            for (int i = firstVisiblePosition - 1; i > mPrevFirstVisiblePosition; i--) {\n                                if (0 < mChildrenHeights.indexOfKey(i)) {\n                                    skippedChildrenHeight += mChildrenHeights.get(i);\n                                }\n                            }\n                        }\n                        mPrevScrolledChildrenHeight += mPrevFirstVisibleChildHeight + skippedChildrenHeight;\n                        mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight();\n                    } else if (firstVisiblePosition < mPrevFirstVisiblePosition) {\n                        // scroll up\n                        int skippedChildrenHeight = 0;\n                        if (mPrevFirstVisiblePosition - firstVisiblePosition != 1) {\n                            for (int i = mPrevFirstVisiblePosition - 1; i > firstVisiblePosition; i--) {\n                                if (0 < mChildrenHeights.indexOfKey(i)) {\n                                    skippedChildrenHeight += mChildrenHeights.get(i);\n                                }\n                            }\n                        }\n                        mPrevScrolledChildrenHeight -= firstVisibleChild.getHeight() + skippedChildrenHeight;\n                        mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight();\n                    } else if (firstVisiblePosition == 0) {\n                        mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight();\n                    }\n                    if (mPrevFirstVisibleChildHeight < 0) {\n                        mPrevFirstVisibleChildHeight = 0;\n                    }\n                    mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop();\n                    mPrevFirstVisiblePosition = firstVisiblePosition;\n\n                    mCallbacks.onScrollChanged(mScrollY, mFirstScroll, mDragging);\n                    if (mFirstScroll) {\n                        mFirstScroll = false;\n                    }\n\n                    if (mPrevScrollY < mScrollY) {\n                        mScrollState = ScrollState.UP;\n                    } else if (mScrollY < mPrevScrollY) {\n                        mScrollState = ScrollState.DOWN;\n                    } else {\n                        mScrollState = ScrollState.STOP;\n                    }\n                    mPrevScrollY = mScrollY;\n                }\n            }\n        }\n    }\n\n    static class SavedState extends BaseSavedState {\n        int prevFirstVisiblePosition;\n        int prevFirstVisibleChildHeight = -1;\n        int prevScrolledChildrenHeight;\n        int prevScrollY;\n        int scrollY;\n        SparseIntArray childrenHeights;\n\n        /**\n         * Called by onSaveInstanceState.\n         */\n        SavedState(Parcelable superState) {\n            super(superState);\n        }\n\n        /**\n         * Called by CREATOR.\n         */\n        private SavedState(Parcel in) {\n            super(in);\n            prevFirstVisiblePosition = in.readInt();\n            prevFirstVisibleChildHeight = in.readInt();\n            prevScrolledChildrenHeight = in.readInt();\n            prevScrollY = in.readInt();\n            scrollY = in.readInt();\n            childrenHeights = new SparseIntArray();\n            final int numOfChildren = in.readInt();\n            if (0 < numOfChildren) {\n                for (int i = 0; i < numOfChildren; i++) {\n                    final int key = in.readInt();\n                    final int value = in.readInt();\n                    childrenHeights.put(key, value);\n                }\n            }\n        }\n\n        @Override\n        public void writeToParcel(Parcel out, int flags) {\n            super.writeToParcel(out, flags);\n            out.writeInt(prevFirstVisiblePosition);\n            out.writeInt(prevFirstVisibleChildHeight);\n            out.writeInt(prevScrolledChildrenHeight);\n            out.writeInt(prevScrollY);\n            out.writeInt(scrollY);\n            final int numOfChildren = childrenHeights == null ? 0 : childrenHeights.size();\n            out.writeInt(numOfChildren);\n            if (0 < numOfChildren) {\n                for (int i = 0; i < numOfChildren; i++) {\n                    out.writeInt(childrenHeights.keyAt(i));\n                    out.writeInt(childrenHeights.valueAt(i));\n                }\n            }\n        }\n\n        public static final Parcelable.Creator<SavedState> CREATOR\n                = new Parcelable.Creator<SavedState>() {\n            @Override\n            public SavedState createFromParcel(Parcel in) {\n                return new SavedState(in);\n            }\n\n            @Override\n            public SavedState[] newArray(int size) {\n                return new SavedState[size];\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/observablescrollview/ObservableListView.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.way.observablescrollview;\n\nimport android.content.Context;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.util.AttributeSet;\nimport android.util.SparseIntArray;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AbsListView;\nimport android.widget.ListView;\n\n/**\n * ListView that its scroll position can be observed.\n */\npublic class ObservableListView extends ListView implements Scrollable {\n\n    // Fields that should be saved onSaveInstanceState\n    private int mPrevFirstVisiblePosition;\n    private int mPrevFirstVisibleChildHeight = -1;\n    private int mPrevScrolledChildrenHeight;\n    private int mPrevScrollY;\n    private int mScrollY;\n    private SparseIntArray mChildrenHeights;\n\n    // Fields that don't need to be saved onSaveInstanceState\n    private ObservableScrollViewCallbacks mCallbacks;\n    private ScrollState mScrollState;\n    private boolean mFirstScroll;\n    private boolean mDragging;\n    private boolean mIntercepted;\n    private MotionEvent mPrevMoveEvent;\n    private ViewGroup mTouchInterceptionViewGroup;\n\n    private OnScrollListener mOriginalScrollListener;\n    private OnScrollListener mScrollListener = new OnScrollListener() {\n        @Override\n        public void onScrollStateChanged(AbsListView view, int scrollState) {\n            if (mOriginalScrollListener != null) {\n                mOriginalScrollListener.onScrollStateChanged(view, scrollState);\n            }\n        }\n\n        @Override\n        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {\n            if (mOriginalScrollListener != null) {\n                mOriginalScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);\n            }\n            // AbsListView#invokeOnItemScrollListener calls onScrollChanged(0, 0, 0, 0)\n            // on Android 4.0+, but Android 2.3 is not. (Android 3.0 is unknown)\n            // So call it with onScrollListener.\n            onScrollChanged();\n        }\n    };\n\n    public ObservableListView(Context context) {\n        super(context);\n        init();\n    }\n\n    public ObservableListView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    public ObservableListView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        init();\n    }\n\n    @Override\n    public void onRestoreInstanceState(Parcelable state) {\n        SavedState ss = (SavedState) state;\n        mPrevFirstVisiblePosition = ss.prevFirstVisiblePosition;\n        mPrevFirstVisibleChildHeight = ss.prevFirstVisibleChildHeight;\n        mPrevScrolledChildrenHeight = ss.prevScrolledChildrenHeight;\n        mPrevScrollY = ss.prevScrollY;\n        mScrollY = ss.scrollY;\n        mChildrenHeights = ss.childrenHeights;\n        super.onRestoreInstanceState(ss.getSuperState());\n    }\n\n    @Override\n    public Parcelable onSaveInstanceState() {\n        Parcelable superState = super.onSaveInstanceState();\n        SavedState ss = new SavedState(superState);\n        ss.prevFirstVisiblePosition = mPrevFirstVisiblePosition;\n        ss.prevFirstVisibleChildHeight = mPrevFirstVisibleChildHeight;\n        ss.prevScrolledChildrenHeight = mPrevScrolledChildrenHeight;\n        ss.prevScrollY = mPrevScrollY;\n        ss.scrollY = mScrollY;\n        ss.childrenHeights = mChildrenHeights;\n        return ss;\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        if (mCallbacks != null) {\n            switch (ev.getActionMasked()) {\n                case MotionEvent.ACTION_DOWN:\n                    // Whether or not motion events are consumed by children,\n                    // flag initializations which are related to ACTION_DOWN events should be executed.\n                    // Because if the ACTION_DOWN is consumed by children and only ACTION_MOVEs are\n                    // passed to parent (this view), the flags will be invalid.\n                    // Also, applications might implement initialization codes to onDownMotionEvent,\n                    // so call it here.\n                    mFirstScroll = mDragging = true;\n                    mCallbacks.onDownMotionEvent();\n                    break;\n            }\n        }\n        return super.onInterceptTouchEvent(ev);\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent ev) {\n        if (mCallbacks != null) {\n            switch (ev.getActionMasked()) {\n                case MotionEvent.ACTION_UP:\n                case MotionEvent.ACTION_CANCEL:\n                    mIntercepted = false;\n                    mDragging = false;\n                    mCallbacks.onUpOrCancelMotionEvent(mScrollState);\n                    break;\n                case MotionEvent.ACTION_MOVE:\n                    if (mPrevMoveEvent == null) {\n                        mPrevMoveEvent = ev;\n                    }\n                    float diffY = ev.getY() - mPrevMoveEvent.getY();\n                    mPrevMoveEvent = MotionEvent.obtainNoHistory(ev);\n                    if (getCurrentScrollY() - diffY <= 0) {\n                        // Can't scroll anymore.\n\n                        if (mIntercepted) {\n                            // Already dispatched ACTION_DOWN event to parents, so stop here.\n                            return false;\n                        }\n\n                        // Apps can set the interception target other than the direct parent.\n                        final ViewGroup parent;\n                        if (mTouchInterceptionViewGroup == null) {\n                            parent = (ViewGroup) getParent();\n                        } else {\n                            parent = mTouchInterceptionViewGroup;\n                        }\n\n                        // Get offset to parents. If the parent is not the direct parent,\n                        // we should aggregate offsets from all of the parents.\n                        float offsetX = 0;\n                        float offsetY = 0;\n                        for (View v = this; v != null && v != parent; v = (View) v.getParent()) {\n                            offsetX += v.getLeft() - v.getScrollX();\n                            offsetY += v.getTop() - v.getScrollY();\n                        }\n                        final MotionEvent event = MotionEvent.obtainNoHistory(ev);\n                        event.offsetLocation(offsetX, offsetY);\n\n                        if (parent.onInterceptTouchEvent(event)) {\n                            mIntercepted = true;\n\n                            // If the parent wants to intercept ACTION_MOVE events,\n                            // we pass ACTION_DOWN event to the parent\n                            // as if these touch events just have began now.\n                            event.setAction(MotionEvent.ACTION_DOWN);\n\n                            // Return this onTouchEvent() first and set ACTION_DOWN event for parent\n                            // to the queue, to keep events sequence.\n                            post(new Runnable() {\n                                @Override\n                                public void run() {\n                                    parent.dispatchTouchEvent(event);\n                                }\n                            });\n                            return false;\n                        }\n                        // Even when this can't be scrolled anymore,\n                        // simply returning false here may cause subView's click,\n                        // so delegate it to super.\n                        return super.onTouchEvent(ev);\n                    }\n                    break;\n            }\n        }\n        return super.onTouchEvent(ev);\n    }\n\n    @Override\n    public void setOnScrollListener(OnScrollListener l) {\n        // Don't set l to super.setOnScrollListener().\n        // l receives all events through mScrollListener.\n        mOriginalScrollListener = l;\n    }\n\n    @Override\n    public void setScrollViewCallbacks(ObservableScrollViewCallbacks listener) {\n        mCallbacks = listener;\n    }\n\n    @Override\n    public void setTouchInterceptionViewGroup(ViewGroup viewGroup) {\n        mTouchInterceptionViewGroup = viewGroup;\n    }\n\n    @Override\n    public void scrollVerticallyTo(int y) {\n        View firstVisibleChild = getChildAt(0);\n        if (firstVisibleChild != null) {\n            int baseHeight = firstVisibleChild.getHeight();\n            int position = y / baseHeight;\n            setSelection(position);\n        }\n    }\n\n    @Override\n    public int getCurrentScrollY() {\n        return mScrollY;\n    }\n\n    private void init() {\n        mChildrenHeights = new SparseIntArray();\n        super.setOnScrollListener(mScrollListener);\n    }\n\n    private void onScrollChanged() {\n        if (mCallbacks != null) {\n            if (getChildCount() > 0) {\n                int firstVisiblePosition = getFirstVisiblePosition();\n                for (int i = getFirstVisiblePosition(), j = 0; i <= getLastVisiblePosition(); i++, j++) {\n                    if (mChildrenHeights.indexOfKey(i) < 0 || getChildAt(j).getHeight() != mChildrenHeights.get(i)) {\n                        mChildrenHeights.put(i, getChildAt(j).getHeight());\n                    }\n                }\n\n                View firstVisibleChild = getChildAt(0);\n                if (firstVisibleChild != null) {\n                    if (mPrevFirstVisiblePosition < firstVisiblePosition) {\n                        // scroll down\n                        int skippedChildrenHeight = 0;\n                        if (firstVisiblePosition - mPrevFirstVisiblePosition != 1) {\n                            for (int i = firstVisiblePosition - 1; i > mPrevFirstVisiblePosition; i--) {\n                                if (0 < mChildrenHeights.indexOfKey(i)) {\n                                    skippedChildrenHeight += mChildrenHeights.get(i);\n                                } else {\n                                    // Approximate each item's height to the first visible child.\n                                    // It may be incorrect, but without this, scrollY will be broken\n                                    // when scrolling from the bottom.\n                                    skippedChildrenHeight += firstVisibleChild.getHeight();\n                                }\n                            }\n                        }\n                        mPrevScrolledChildrenHeight += mPrevFirstVisibleChildHeight + skippedChildrenHeight;\n                        mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight();\n                    } else if (firstVisiblePosition < mPrevFirstVisiblePosition) {\n                        // scroll up\n                        int skippedChildrenHeight = 0;\n                        if (mPrevFirstVisiblePosition - firstVisiblePosition != 1) {\n                            for (int i = mPrevFirstVisiblePosition - 1; i > firstVisiblePosition; i--) {\n                                if (0 < mChildrenHeights.indexOfKey(i)) {\n                                    skippedChildrenHeight += mChildrenHeights.get(i);\n                                } else {\n                                    // Approximate each item's height to the first visible child.\n                                    // It may be incorrect, but without this, scrollY will be broken\n                                    // when scrolling from the bottom.\n                                    skippedChildrenHeight += firstVisibleChild.getHeight();\n                                }\n                            }\n                        }\n                        mPrevScrolledChildrenHeight -= firstVisibleChild.getHeight() + skippedChildrenHeight;\n                        mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight();\n                    } else if (firstVisiblePosition == 0) {\n                        mPrevFirstVisibleChildHeight = firstVisibleChild.getHeight();\n                    }\n                    if (mPrevFirstVisibleChildHeight < 0) {\n                        mPrevFirstVisibleChildHeight = 0;\n                    }\n                    mScrollY = mPrevScrolledChildrenHeight - firstVisibleChild.getTop();\n                    mPrevFirstVisiblePosition = firstVisiblePosition;\n\n                    mCallbacks.onScrollChanged(mScrollY, mFirstScroll, mDragging);\n                    if (mFirstScroll) {\n                        mFirstScroll = false;\n                    }\n\n                    if (mPrevScrollY < mScrollY) {\n                        mScrollState = ScrollState.UP;\n                    } else if (mScrollY < mPrevScrollY) {\n                        mScrollState = ScrollState.DOWN;\n                    } else {\n                        mScrollState = ScrollState.STOP;\n                    }\n                    mPrevScrollY = mScrollY;\n                }\n            }\n        }\n    }\n\n    static class SavedState extends BaseSavedState {\n        int prevFirstVisiblePosition;\n        int prevFirstVisibleChildHeight = -1;\n        int prevScrolledChildrenHeight;\n        int prevScrollY;\n        int scrollY;\n        SparseIntArray childrenHeights;\n\n        /**\n         * Called by onSaveInstanceState.\n         */\n        SavedState(Parcelable superState) {\n            super(superState);\n        }\n\n        /**\n         * Called by CREATOR.\n         */\n        private SavedState(Parcel in) {\n            super(in);\n            prevFirstVisiblePosition = in.readInt();\n            prevFirstVisibleChildHeight = in.readInt();\n            prevScrolledChildrenHeight = in.readInt();\n            prevScrollY = in.readInt();\n            scrollY = in.readInt();\n            childrenHeights = new SparseIntArray();\n            final int numOfChildren = in.readInt();\n            if (0 < numOfChildren) {\n                for (int i = 0; i < numOfChildren; i++) {\n                    final int key = in.readInt();\n                    final int value = in.readInt();\n                    childrenHeights.put(key, value);\n                }\n            }\n        }\n\n        @Override\n        public void writeToParcel(Parcel out, int flags) {\n            super.writeToParcel(out, flags);\n            out.writeInt(prevFirstVisiblePosition);\n            out.writeInt(prevFirstVisibleChildHeight);\n            out.writeInt(prevScrolledChildrenHeight);\n            out.writeInt(prevScrollY);\n            out.writeInt(scrollY);\n            final int numOfChildren = childrenHeights == null ? 0 : childrenHeights.size();\n            out.writeInt(numOfChildren);\n            if (0 < numOfChildren) {\n                for (int i = 0; i < numOfChildren; i++) {\n                    out.writeInt(childrenHeights.keyAt(i));\n                    out.writeInt(childrenHeights.valueAt(i));\n                }\n            }\n        }\n\n        public static final Parcelable.Creator<SavedState> CREATOR\n                = new Parcelable.Creator<SavedState>() {\n            @Override\n            public SavedState createFromParcel(Parcel in) {\n                return new SavedState(in);\n            }\n\n            @Override\n            public SavedState[] newArray(int size) {\n                return new SavedState[size];\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/observablescrollview/ObservableScrollView.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.way.observablescrollview;\n\nimport android.content.Context;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ScrollView;\n\n/**\n * ScrollView that its scroll position can be observed.\n */\npublic class ObservableScrollView extends ScrollView implements Scrollable {\n\n    // Fields that should be saved onSaveInstanceState\n    private int mPrevScrollY;\n    private int mScrollY;\n\n    // Fields that don't need to be saved onSaveInstanceState\n    private ObservableScrollViewCallbacks mCallbacks;\n    private ScrollState mScrollState;\n    private boolean mFirstScroll;\n    private boolean mDragging;\n    private boolean mIntercepted;\n    private MotionEvent mPrevMoveEvent;\n    private ViewGroup mTouchInterceptionViewGroup;\n\n    public ObservableScrollView(Context context) {\n        super(context);\n    }\n\n    public ObservableScrollView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public ObservableScrollView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n    }\n\n    @Override\n    public void onRestoreInstanceState(Parcelable state) {\n        SavedState ss = (SavedState) state;\n        mPrevScrollY = ss.prevScrollY;\n        mScrollY = ss.scrollY;\n        super.onRestoreInstanceState(ss.getSuperState());\n    }\n\n    @Override\n    public Parcelable onSaveInstanceState() {\n        Parcelable superState = super.onSaveInstanceState();\n        SavedState ss = new SavedState(superState);\n        ss.prevScrollY = mPrevScrollY;\n        ss.scrollY = mScrollY;\n        return ss;\n    }\n\n    @Override\n    protected void onScrollChanged(int l, int t, int oldl, int oldt) {\n        super.onScrollChanged(l, t, oldl, oldt);\n        if (mCallbacks != null) {\n            mScrollY = t;\n\n            mCallbacks.onScrollChanged(t, mFirstScroll, mDragging);\n            if (mFirstScroll) {\n                mFirstScroll = false;\n            }\n\n            if (mPrevScrollY < t) {\n                mScrollState = ScrollState.UP;\n            } else if (t < mPrevScrollY) {\n                mScrollState = ScrollState.DOWN;\n                //} else {\n                // Keep previous state while dragging.\n                // Never makes it STOP even if scrollY not changed.\n                // Before Android 4.4, onTouchEvent calls onScrollChanged directly for ACTION_MOVE,\n                // which makes mScrollState always STOP when onUpOrCancelMotionEvent is called.\n                // STOP state is now meaningless for ScrollView.\n            }\n            mPrevScrollY = t;\n        }\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        if (mCallbacks != null) {\n            switch (ev.getActionMasked()) {\n                case MotionEvent.ACTION_DOWN:\n                    // Whether or not motion events are consumed by children,\n                    // flag initializations which are related to ACTION_DOWN events should be executed.\n                    // Because if the ACTION_DOWN is consumed by children and only ACTION_MOVEs are\n                    // passed to parent (this view), the flags will be invalid.\n                    // Also, applications might implement initialization codes to onDownMotionEvent,\n                    // so call it here.\n                    mFirstScroll = mDragging = true;\n                    mCallbacks.onDownMotionEvent();\n                    break;\n            }\n        }\n        return super.onInterceptTouchEvent(ev);\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent ev) {\n        if (mCallbacks != null) {\n            switch (ev.getActionMasked()) {\n                case MotionEvent.ACTION_UP:\n                case MotionEvent.ACTION_CANCEL:\n                    mIntercepted = false;\n                    mDragging = false;\n                    mCallbacks.onUpOrCancelMotionEvent(mScrollState);\n                    break;\n                case MotionEvent.ACTION_MOVE:\n                    if (mPrevMoveEvent == null) {\n                        mPrevMoveEvent = ev;\n                    }\n                    float diffY = ev.getY() - mPrevMoveEvent.getY();\n                    mPrevMoveEvent = MotionEvent.obtainNoHistory(ev);\n                    if (getCurrentScrollY() - diffY <= 0) {\n                        // Can't scroll anymore.\n\n                        if (mIntercepted) {\n                            // Already dispatched ACTION_DOWN event to parents, so stop here.\n                            return false;\n                        }\n\n                        // Apps can set the interception target other than the direct parent.\n                        final ViewGroup parent;\n                        if (mTouchInterceptionViewGroup == null) {\n                            parent = (ViewGroup) getParent();\n                        } else {\n                            parent = mTouchInterceptionViewGroup;\n                        }\n\n                        // Get offset to parents. If the parent is not the direct parent,\n                        // we should aggregate offsets from all of the parents.\n                        float offsetX = 0;\n                        float offsetY = 0;\n                        for (View v = this; v != null && v != parent; v = (View) v.getParent()) {\n                            offsetX += v.getLeft() - v.getScrollX();\n                            offsetY += v.getTop() - v.getScrollY();\n                        }\n                        final MotionEvent event = MotionEvent.obtainNoHistory(ev);\n                        event.offsetLocation(offsetX, offsetY);\n\n                        if (parent.onInterceptTouchEvent(event)) {\n                            mIntercepted = true;\n\n                            // If the parent wants to intercept ACTION_MOVE events,\n                            // we pass ACTION_DOWN event to the parent\n                            // as if these touch events just have began now.\n                            event.setAction(MotionEvent.ACTION_DOWN);\n\n                            // Return this onTouchEvent() first and set ACTION_DOWN event for parent\n                            // to the queue, to keep events sequence.\n                            post(new Runnable() {\n                                @Override\n                                public void run() {\n                                    parent.dispatchTouchEvent(event);\n                                }\n                            });\n                            return false;\n                        }\n                        // Even when this can't be scrolled anymore,\n                        // simply returning false here may cause subView's click,\n                        // so delegate it to super.\n                        return super.onTouchEvent(ev);\n                    }\n                    break;\n            }\n        }\n        return super.onTouchEvent(ev);\n    }\n\n    @Override\n    public void setScrollViewCallbacks(ObservableScrollViewCallbacks listener) {\n        mCallbacks = listener;\n    }\n\n    @Override\n    public void setTouchInterceptionViewGroup(ViewGroup viewGroup) {\n        mTouchInterceptionViewGroup = viewGroup;\n    }\n\n    @Override\n    public void scrollVerticallyTo(int y) {\n        scrollTo(0, y);\n    }\n\n    @Override\n    public int getCurrentScrollY() {\n        return mScrollY;\n    }\n\n    static class SavedState extends BaseSavedState {\n        int prevScrollY;\n        int scrollY;\n\n        /**\n         * Called by onSaveInstanceState.\n         */\n        SavedState(Parcelable superState) {\n            super(superState);\n        }\n\n        /**\n         * Called by CREATOR.\n         */\n        private SavedState(Parcel in) {\n            super(in);\n            prevScrollY = in.readInt();\n            scrollY = in.readInt();\n        }\n\n        @Override\n        public void writeToParcel(Parcel out, int flags) {\n            super.writeToParcel(out, flags);\n            out.writeInt(prevScrollY);\n            out.writeInt(scrollY);\n        }\n\n        public static final Parcelable.Creator<SavedState> CREATOR\n                = new Parcelable.Creator<SavedState>() {\n            @Override\n            public SavedState createFromParcel(Parcel in) {\n                return new SavedState(in);\n            }\n\n            @Override\n            public SavedState[] newArray(int size) {\n                return new SavedState[size];\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/observablescrollview/ObservableScrollViewCallbacks.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.way.observablescrollview;\n\n/**\n * Callbacks for Scrollable widgets.\n */\npublic interface ObservableScrollViewCallbacks {\n    /**\n     * Called when the scroll change events occurred.\n     * This won't be called just after the view is laid out, so if you'd like to\n     * initialize the position of your views with this method, you should call this manually\n     * or invoke scroll as appropriate.\n     *\n     * @param scrollY     scroll position in Y axis\n     * @param firstScroll true when this is called for the first time in the consecutive motion events\n     * @param dragging    true when the view is dragged and false when the view is scrolled in the inertia\n     */\n    void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging);\n\n    /**\n     * Called when the down motion event occurred.\n     */\n    void onDownMotionEvent();\n\n    /**\n     * Called when the dragging ended or canceled.\n     *\n     * @param scrollState state to indicate the scroll direction\n     */\n    void onUpOrCancelMotionEvent(ScrollState scrollState);\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/observablescrollview/ObservableWebView.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.way.observablescrollview;\n\nimport android.content.Context;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.webkit.WebView;\n\n/**\n * WebView that its scroll position can be observed.\n */\npublic class ObservableWebView extends WebView implements Scrollable {\n\n    // Fields that should be saved onSaveInstanceState\n    private int mPrevScrollY;\n    private int mScrollY;\n\n    // Fields that don't need to be saved onSaveInstanceState\n    private ObservableScrollViewCallbacks mCallbacks;\n    private ScrollState mScrollState;\n    private boolean mFirstScroll;\n    private boolean mDragging;\n    private boolean mIntercepted;\n    private MotionEvent mPrevMoveEvent;\n    private ViewGroup mTouchInterceptionViewGroup;\n\n    public ObservableWebView(Context context) {\n        super(context);\n    }\n\n    public ObservableWebView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public ObservableWebView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n    }\n\n    @Override\n    public void onRestoreInstanceState(Parcelable state) {\n        SavedState ss = (SavedState) state;\n        mPrevScrollY = ss.prevScrollY;\n        mScrollY = ss.scrollY;\n        super.onRestoreInstanceState(ss.getSuperState());\n    }\n\n    @Override\n    public Parcelable onSaveInstanceState() {\n        Parcelable superState = super.onSaveInstanceState();\n        SavedState ss = new SavedState(superState);\n        ss.prevScrollY = mPrevScrollY;\n        ss.scrollY = mScrollY;\n        return ss;\n    }\n\n    @Override\n    protected void onScrollChanged(int l, int t, int oldl, int oldt) {\n        super.onScrollChanged(l, t, oldl, oldt);\n        if (mCallbacks != null) {\n            mScrollY = t;\n\n            mCallbacks.onScrollChanged(t, mFirstScroll, mDragging);\n            if (mFirstScroll) {\n                mFirstScroll = false;\n            }\n\n            if (mPrevScrollY < t) {\n                mScrollState = ScrollState.UP;\n            } else if (t < mPrevScrollY) {\n                mScrollState = ScrollState.DOWN;\n            } else {\n                mScrollState = ScrollState.STOP;\n            }\n            mPrevScrollY = t;\n        }\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        if (mCallbacks != null) {\n            switch (ev.getActionMasked()) {\n                case MotionEvent.ACTION_DOWN:\n                    // Whether or not motion events are consumed by children,\n                    // flag initializations which are related to ACTION_DOWN events should be executed.\n                    // Because if the ACTION_DOWN is consumed by children and only ACTION_MOVEs are\n                    // passed to parent (this view), the flags will be invalid.\n                    // Also, applications might implement initialization codes to onDownMotionEvent,\n                    // so call it here.\n                    mFirstScroll = mDragging = true;\n                    mCallbacks.onDownMotionEvent();\n                    break;\n            }\n        }\n        return super.onInterceptTouchEvent(ev);\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent ev) {\n        if (mCallbacks != null) {\n            switch (ev.getActionMasked()) {\n                case MotionEvent.ACTION_DOWN:\n                    break;\n                case MotionEvent.ACTION_UP:\n                case MotionEvent.ACTION_CANCEL:\n                    mIntercepted = false;\n                    mDragging = false;\n                    mCallbacks.onUpOrCancelMotionEvent(mScrollState);\n                    break;\n                case MotionEvent.ACTION_MOVE:\n                    if (mPrevMoveEvent == null) {\n                        mPrevMoveEvent = ev;\n                    }\n                    float diffY = ev.getY() - mPrevMoveEvent.getY();\n                    mPrevMoveEvent = MotionEvent.obtainNoHistory(ev);\n                    if (getCurrentScrollY() - diffY <= 0) {\n                        // Can't scroll anymore.\n\n                        if (mIntercepted) {\n                            // Already dispatched ACTION_DOWN event to parents, so stop here.\n                            return false;\n                        }\n\n                        // Apps can set the interception target other than the direct parent.\n                        final ViewGroup parent;\n                        if (mTouchInterceptionViewGroup == null) {\n                            parent = (ViewGroup) getParent();\n                        } else {\n                            parent = mTouchInterceptionViewGroup;\n                        }\n\n                        // Get offset to parents. If the parent is not the direct parent,\n                        // we should aggregate offsets from all of the parents.\n                        float offsetX = 0;\n                        float offsetY = 0;\n                        for (View v = this; v != null && v != parent; v = (View) v.getParent()) {\n                            offsetX += v.getLeft() - v.getScrollX();\n                            offsetY += v.getTop() - v.getScrollY();\n                        }\n                        final MotionEvent event = MotionEvent.obtainNoHistory(ev);\n                        event.offsetLocation(offsetX, offsetY);\n\n                        if (parent.onInterceptTouchEvent(event)) {\n                            mIntercepted = true;\n\n                            // If the parent wants to intercept ACTION_MOVE events,\n                            // we pass ACTION_DOWN event to the parent\n                            // as if these touch events just have began now.\n                            event.setAction(MotionEvent.ACTION_DOWN);\n\n                            // Return this onTouchEvent() first and set ACTION_DOWN event for parent\n                            // to the queue, to keep events sequence.\n                            post(new Runnable() {\n                                @Override\n                                public void run() {\n                                    parent.dispatchTouchEvent(event);\n                                }\n                            });\n                            return false;\n                        }\n                        // Even when this can't be scrolled anymore,\n                        // simply returning false here may cause subView's click,\n                        // so delegate it to super.\n                        return super.onTouchEvent(ev);\n                    }\n                    break;\n            }\n        }\n        return super.onTouchEvent(ev);\n    }\n\n    @Override\n    public void setScrollViewCallbacks(ObservableScrollViewCallbacks listener) {\n        mCallbacks = listener;\n    }\n\n    @Override\n    public void setTouchInterceptionViewGroup(ViewGroup viewGroup) {\n        mTouchInterceptionViewGroup = viewGroup;\n    }\n\n    @Override\n    public void scrollVerticallyTo(int y) {\n        scrollTo(0, y);\n    }\n\n    @Override\n    public int getCurrentScrollY() {\n        return mScrollY;\n    }\n\n    static class SavedState extends BaseSavedState {\n        int prevScrollY;\n        int scrollY;\n\n        /**\n         * Called by onSaveInstanceState.\n         */\n        SavedState(Parcelable superState) {\n            super(superState);\n        }\n\n        /**\n         * Called by CREATOR.\n         */\n        private SavedState(Parcel in) {\n            super(in);\n            prevScrollY = in.readInt();\n            scrollY = in.readInt();\n        }\n\n        @Override\n        public void writeToParcel(Parcel out, int flags) {\n            super.writeToParcel(out, flags);\n            out.writeInt(prevScrollY);\n            out.writeInt(scrollY);\n        }\n\n        public static final Parcelable.Creator<SavedState> CREATOR\n                = new Parcelable.Creator<SavedState>() {\n            @Override\n            public SavedState createFromParcel(Parcel in) {\n                return new SavedState(in);\n            }\n\n            @Override\n            public SavedState[] newArray(int size) {\n                return new SavedState[size];\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/observablescrollview/ScrollState.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.way.observablescrollview;\n\n/**\n * Constants that indicates the scroll state of the Scrollable widgets.\n */\npublic enum ScrollState {\n    /**\n     * Widget is stopped.\n     * This state does not always mean that this widget have never been scrolled.\n     */\n    STOP,\n\n    /**\n     * Widget is scrolled up by swiping it down.\n     */\n    UP,\n\n    /**\n     * Widget is scrolled down by swiping it up.\n     */\n    DOWN,\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/observablescrollview/ScrollUtils.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.way.observablescrollview;\n\nimport android.os.Build;\nimport android.view.View;\nimport android.view.ViewTreeObserver;\n\n/**\n * Utilities for creating scrolling effects.\n */\npublic final class ScrollUtils {\n\n    private ScrollUtils() {\n    }\n\n    /**\n     * Return a float value within the range.\n     * This is just a wrapper for Math.min() and Math.max().\n     * This may be useful if you feel it confusing (\"Which is min and which is max?\").\n     *\n     * @param value    the target value\n     * @param minValue minimum value. If value is less than this, minValue will be returned\n     * @param maxValue maximum value. If value is greater than this, maxValue will be returned\n     * @return float value limited to the range\n     */\n    public static float getFloat(final float value, final float minValue, final float maxValue) {\n        return Math.min(maxValue, Math.max(minValue, value));\n    }\n\n    /**\n     * Create a color integer value with specified alpha.\n     * This may be useful to change alpha value of background color.\n     *\n     * @param alpha     alpha value from 0.0f to 1.0f.\n     * @param baseColor base color. alpha value will be ignored.\n     * @return a color with alpha made from base color\n     */\n    public static int getColorWithAlpha(float alpha, int baseColor) {\n        int a = Math.min(255, Math.max(0, (int) (alpha * 255))) << 24;\n        int rgb = 0x00ffffff & baseColor;\n        return a + rgb;\n    }\n\n    /**\n     * Add an OnGlobalLayoutListener for the view.\n     * This is just a convenience method for using {@code ViewTreeObserver.OnGlobalLayoutListener()}.\n     * This also handles removing listener when onGlobalLayout is called.\n     *\n     * @param view     the target view to add global layout listener\n     * @param runnable runnable to be executed after the view is laid out\n     */\n    public static void addOnGlobalLayoutListener(final View view, final Runnable runnable) {\n        ViewTreeObserver vto = view.getViewTreeObserver();\n        vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {\n            @Override\n            public void onGlobalLayout() {\n                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n                    view.getViewTreeObserver().removeGlobalOnLayoutListener(this);\n                } else {\n                    view.getViewTreeObserver().removeOnGlobalLayoutListener(this);\n                }\n                runnable.run();\n            }\n        });\n    }\n\n    /**\n     * Mix two colors.\n     * {@code toColor} will be {@code toAlpha/1} percent,\n     * and {@code fromColor} will be {@code (1-toAlpha)/1} percent.\n     *\n     * @param fromColor first color to be mixed\n     * @param toColor   second color to be mixed\n     * @param toAlpha   alpha value of toColor, 0.0f to 1.0f.\n     * @return mixed color value in ARGB. Alpha is fixed value (255).\n     */\n    public static int mixColors(int fromColor, int toColor, float toAlpha) {\n        float[] fromCmyk = ScrollUtils.cmykFromRgb(fromColor);\n        float[] toCmyk = ScrollUtils.cmykFromRgb(toColor);\n        float[] result = new float[4];\n        for (int i = 0; i < 4; i++) {\n            result[i] = Math.min(1, fromCmyk[i] * (1 - toAlpha) + toCmyk[i] * toAlpha);\n        }\n        return 0xff000000 + (0x00ffffff & ScrollUtils.rgbFromCmyk(result));\n    }\n\n    /**\n     * Convert RGB color to CMYK color.\n     *\n     * @param rgbColor target color\n     * @return CMYK array\n     */\n    public static float[] cmykFromRgb(int rgbColor) {\n        int red = (0xff0000 & rgbColor) >> 16;\n        int green = (0xff00 & rgbColor) >> 8;\n        int blue = (0xff & rgbColor);\n        float black = Math.min(1.0f - red / 255.0f, Math.min(1.0f - green / 255.0f, 1.0f - blue / 255.0f));\n        float cyan = 1.0f;\n        float magenta = 1.0f;\n        float yellow = 1.0f;\n        if (black != 1.0f) {\n            // black 1.0 causes zero divide\n            cyan = (1.0f - (red / 255.0f) - black) / (1.0f - black);\n            magenta = (1.0f - (green / 255.0f) - black) / (1.0f - black);\n            yellow = (1.0f - (blue / 255.0f) - black) / (1.0f - black);\n        }\n        return new float[]{cyan, magenta, yellow, black};\n    }\n\n    /**\n     * Convert CYMK color to RGB color.\n     * This method doesn't check f cmyk is not null or have 4 elements in array.\n     *\n     * @param cmyk target CYMK color. Each value should be between 0.0f to 1.0f,\n     *             and should be set in this order: cyan, magenta, yellow, black.\n     * @return ARGB color. Alpha is fixed value (255).\n     */\n    public static int rgbFromCmyk(float[] cmyk) {\n        float cyan = cmyk[0];\n        float magenta = cmyk[1];\n        float yellow = cmyk[2];\n        float black = cmyk[3];\n        int red = (int) ((1.0f - Math.min(1.0f, cyan * (1.0f - black) + black)) * 255);\n        int green = (int) ((1.0f - Math.min(1.0f, magenta * (1.0f - black) + black)) * 255);\n        int blue = (int) ((1.0f - Math.min(1.0f, yellow * (1.0f - black) + black)) * 255);\n        return ((0xff & red) << 16) + ((0xff & green) << 8) + (0xff & blue);\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/observablescrollview/Scrollable.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.way.observablescrollview;\n\nimport android.view.ViewGroup;\n\n/**\n * Provides common API for observable and scrollable widgets.\n */\npublic interface Scrollable {\n    /**\n     * Sets a callback listener.\n     *\n     * @param listener listener to set\n     */\n    void setScrollViewCallbacks(ObservableScrollViewCallbacks listener);\n\n    /**\n     * Scrolls vertically to the absolute Y.\n     * Implemented classes are expected to scroll to the exact Y pixels from the top,\n     * but it depends on the type of the widget.\n     *\n     * @param y vertical position to scroll to\n     */\n    void scrollVerticallyTo(int y);\n\n    /**\n     * Returns the current Y of the scrollable view.\n     *\n     * @return current Y pixel\n     */\n    int getCurrentScrollY();\n\n    /**\n     * Sets a touch motion event delegation ViewGroup.\n     * This is used to pass motion events back to parent view.\n     * It's up to the implementation classes whether or not it works.\n     *\n     * @param viewGroup ViewGroup object to dispatch motion events\n     */\n    void setTouchInterceptionViewGroup(ViewGroup viewGroup);\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/observablescrollview/TouchInterceptionFrameLayout.java",
    "content": "/*\n * Copyright 2014 Soichiro Kashima\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.way.observablescrollview;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.graphics.PointF;\nimport android.graphics.Rect;\nimport android.os.Build;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.widget.FrameLayout;\n\n/**\n * A layout that delegates interception of touch motion events.\n * This layout is provided to move the container of Scrollable views using scroll position.\n * Please note that this class overrides or uses touch events API such as onTouchEvent,\n * onInterceptTouchEvent and dispatchTouchEvent,\n * so be careful when you handle touches with this layout.\n */\npublic class TouchInterceptionFrameLayout extends FrameLayout {\n\n    /**\n     * Callbacks for TouchInterceptionFrameLayout.\n     */\n    public interface TouchInterceptionListener {\n        /**\n         * Determines whether the layout should intercept this event.\n         *\n         * @param ev     motion event\n         * @param moving true if this event is ACTION_MOVE type\n         * @param diffX  difference between previous X and current X, if moving is true\n         * @param diffY  difference between previous Y and current Y, if moving is true\n         * @return true if the layout should intercept\n         */\n        boolean shouldInterceptTouchEvent(MotionEvent ev, boolean moving, float diffX, float diffY);\n\n        /**\n         * Called if the down motion event is intercepted by this layout.\n         *\n         * @param ev motion event\n         */\n        void onDownMotionEvent(MotionEvent ev);\n\n        /**\n         * Called if the move motion event is intercepted by this layout.\n         *\n         * @param ev    motion event\n         * @param diffX difference between previous X and current X\n         * @param diffY difference between previous Y and current Y\n         */\n        void onMoveMotionEvent(MotionEvent ev, float diffX, float diffY);\n\n        /**\n         * Called if the up (or cancel) motion event is intercepted by this layout.\n         *\n         * @param ev motion event\n         */\n        void onUpOrCancelMotionEvent(MotionEvent ev);\n    }\n\n    private boolean mIntercepting;\n    private boolean mDownMotionEventPended;\n    private boolean mBeganFromDownMotionEvent;\n    private boolean mChildrenEventsCanceled;\n    private PointF mInitialPoint;\n    private MotionEvent mPendingDownMotionEvent;\n    private TouchInterceptionListener mTouchInterceptionListener;\n\n    public TouchInterceptionFrameLayout(Context context) {\n        super(context);\n    }\n\n    public TouchInterceptionFrameLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public TouchInterceptionFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    @TargetApi(Build.VERSION_CODES.LOLLIPOP)\n    public TouchInterceptionFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {\n        super(context, attrs, defStyleAttr, defStyleRes);\n    }\n\n    public void setScrollInterceptionListener(TouchInterceptionListener listener) {\n        mTouchInterceptionListener = listener;\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        if (mTouchInterceptionListener == null) {\n            return false;\n        }\n\n        // In here, we must initialize touch state variables\n        // and ask if we should intercept this event.\n        // Whether we should intercept or not is kept for the later event handling.\n        switch (ev.getActionMasked()) {\n            case MotionEvent.ACTION_DOWN:\n                mInitialPoint = new PointF(ev.getX(), ev.getY());\n                mPendingDownMotionEvent = MotionEvent.obtainNoHistory(ev);\n                mDownMotionEventPended = true;\n                mIntercepting = mTouchInterceptionListener.shouldInterceptTouchEvent(ev, false, 0, 0);\n                mBeganFromDownMotionEvent = mIntercepting;\n                mChildrenEventsCanceled = false;\n                return mIntercepting;\n            case MotionEvent.ACTION_MOVE:\n                // ACTION_MOVE will be passed suddenly, so initialize to avoid exception.\n                if (mInitialPoint == null) {\n                    mInitialPoint = new PointF(ev.getX(), ev.getY());\n                }\n\n                // diffX and diffY are the origin of the motion, and should be difference\n                // from the position of the ACTION_DOWN event occurred.\n                float diffX = ev.getX() - mInitialPoint.x;\n                float diffY = ev.getY() - mInitialPoint.y;\n                mIntercepting = mTouchInterceptionListener.shouldInterceptTouchEvent(ev, true, diffX, diffY);\n                return mIntercepting;\n        }\n        return false;\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent ev) {\n        if (mTouchInterceptionListener != null) {\n            switch (ev.getActionMasked()) {\n                case MotionEvent.ACTION_DOWN:\n                    if (mIntercepting) {\n                        mTouchInterceptionListener.onDownMotionEvent(ev);\n                        duplicateTouchEventForChildren(ev);\n                        return true;\n                    }\n                    break;\n                case MotionEvent.ACTION_MOVE:\n                    // ACTION_MOVE will be passed suddenly, so initialize to avoid exception.\n                    if (mInitialPoint == null) {\n                        mInitialPoint = new PointF(ev.getX(), ev.getY());\n                    }\n\n                    // diffX and diffY are the origin of the motion, and should be difference\n                    // from the position of the ACTION_DOWN event occurred.\n                    float diffX = ev.getX() - mInitialPoint.x;\n                    float diffY = ev.getY() - mInitialPoint.y;\n                    mIntercepting = mTouchInterceptionListener.shouldInterceptTouchEvent(ev, true, diffX, diffY);\n                    if (mIntercepting) {\n                        // If this layout didn't receive ACTION_DOWN motion event,\n                        // we should generate ACTION_DOWN event with current position.\n                        if (!mBeganFromDownMotionEvent) {\n                            mBeganFromDownMotionEvent = true;\n\n                            MotionEvent event = MotionEvent.obtainNoHistory(mPendingDownMotionEvent);\n                            event.setLocation(ev.getX(), ev.getY());\n                            mTouchInterceptionListener.onDownMotionEvent(event);\n\n                            mInitialPoint = new PointF(ev.getX(), ev.getY());\n                            diffX = diffY = 0;\n                        }\n\n                        // Children's touches should be canceled\n                        if (!mChildrenEventsCanceled) {\n                            mChildrenEventsCanceled = true;\n                            duplicateTouchEventForChildren(obtainMotionEvent(ev, MotionEvent.ACTION_CANCEL));\n                        }\n\n                        mTouchInterceptionListener.onMoveMotionEvent(ev, diffX, diffY);\n\n                        // If next mIntercepting become false,\n                        // then we should generate fake ACTION_DOWN event.\n                        // Therefore we set pending flag to true as if this is a down motion event.\n                        mDownMotionEventPended = true;\n\n                        // Whether or not this event is consumed by the listener,\n                        // assume it consumed because we declared to intercept the event.\n                        return true;\n                    } else {\n                        if (mDownMotionEventPended) {\n                            mDownMotionEventPended = false;\n                            MotionEvent event = MotionEvent.obtainNoHistory(mPendingDownMotionEvent);\n                            event.setLocation(ev.getX(), ev.getY());\n                            duplicateTouchEventForChildren(ev, event);\n                        } else {\n                            duplicateTouchEventForChildren(ev);\n                        }\n\n                        // If next mIntercepting become true,\n                        // then we should generate fake ACTION_DOWN event.\n                        // Therefore we set beganFromDownMotionEvent flag to false\n                        // as if we haven't received a down motion event.\n                        mBeganFromDownMotionEvent = false;\n\n                        // Reserve children's click cancellation here if they've already canceled\n                        mChildrenEventsCanceled = false;\n                    }\n                    break;\n                case MotionEvent.ACTION_UP:\n                case MotionEvent.ACTION_CANCEL:\n                    mBeganFromDownMotionEvent = false;\n                    if (mIntercepting) {\n                        mTouchInterceptionListener.onUpOrCancelMotionEvent(ev);\n                    }\n\n                    // Children's touches should be canceled regardless of\n                    // whether or not this layout intercepted the consecutive motion events.\n                    if (!mChildrenEventsCanceled) {\n                        mChildrenEventsCanceled = true;\n                        if (mDownMotionEventPended) {\n                            mDownMotionEventPended = false;\n                            MotionEvent event = MotionEvent.obtainNoHistory(mPendingDownMotionEvent);\n                            event.setLocation(ev.getX(), ev.getY());\n                            duplicateTouchEventForChildren(ev, event);\n                        } else {\n                            duplicateTouchEventForChildren(ev);\n                        }\n                    }\n                    return true;\n            }\n        }\n        return super.onTouchEvent(ev);\n    }\n\n    private MotionEvent obtainMotionEvent(MotionEvent base, int action) {\n        MotionEvent ev = MotionEvent.obtainNoHistory(base);\n        ev.setAction(action);\n        return ev;\n    }\n\n    /**\n     * Duplicate touch events to child views.\n     * We want to dispatch a down motion event and the move events to\n     * child views, but calling dispatchTouchEvent() causes StackOverflowError.\n     * Therefore we do it manually.\n     *\n     * @param ev            motion event to be passed to children\n     * @param pendingEvents pending events like ACTION_DOWN. This will be passed to the children before ev\n     */\n    private void duplicateTouchEventForChildren(MotionEvent ev, MotionEvent... pendingEvents) {\n        if (ev == null) {\n            return;\n        }\n        for (int i = getChildCount() - 1; 0 <= i; i--) {\n            View childView = getChildAt(i);\n            if (childView != null) {\n                Rect childRect = new Rect();\n                childView.getHitRect(childRect);\n                MotionEvent event = MotionEvent.obtainNoHistory(ev);\n                if (!childRect.contains((int) event.getX(), (int) event.getY())) {\n                    continue;\n                }\n                float offsetX = -childView.getLeft();\n                float offsetY = -childView.getTop();\n                boolean consumed = false;\n                if (pendingEvents != null) {\n                    for (MotionEvent pe : pendingEvents) {\n                        if (pe != null) {\n                            MotionEvent peAdjusted = MotionEvent.obtainNoHistory(pe);\n                            peAdjusted.offsetLocation(offsetX, offsetY);\n                            consumed |= childView.dispatchTouchEvent(peAdjusted);\n                        }\n                    }\n                }\n                event.offsetLocation(offsetX, offsetY);\n                consumed |= childView.dispatchTouchEvent(event);\n                if (consumed) {\n                    break;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/ui/swipeback/SwipeBackActivity.java",
    "content": "package com.way.ui.swipeback;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.view.View;\n\npublic class SwipeBackActivity extends Activity implements\n\t\tSwipeBackActivityBase {\n\tprivate SwipeBackActivityHelper mHelper;\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tmHelper = new SwipeBackActivityHelper(this);\n\t\tmHelper.onActivityCreate();\n\t}\n\n\t@Override\n\tprotected void onPostCreate(Bundle savedInstanceState) {\n\t\tsuper.onPostCreate(savedInstanceState);\n\t\tmHelper.onPostCreate();\n\t}\n\n\t@Override\n\tpublic View findViewById(int id) {\n\t\tView v = super.findViewById(id);\n\t\tif (v == null && mHelper != null)\n\t\t\treturn mHelper.findViewById(id);\n\t\treturn v;\n\t}\n\n\t@Override\n\tpublic SwipeBackLayout getSwipeBackLayout() {\n\t\treturn mHelper.getSwipeBackLayout();\n\t}\n\n\t@Override\n\tpublic void setSwipeBackEnable(boolean enable) {\n\t\tgetSwipeBackLayout().setEnableGesture(enable);\n\t}\n\n\t@Override\n\tpublic void scrollToFinishActivity() {\n\t\tgetSwipeBackLayout().scrollToFinishActivity();\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/ui/swipeback/SwipeBackActivityBase.java",
    "content": "package com.way.ui.swipeback;\n\n/**\n * @author Yrom\n */\npublic interface SwipeBackActivityBase {\n    /**\n     * @return the SwipeBackLayout associated with this activity.\n     */\n    public abstract SwipeBackLayout getSwipeBackLayout();\n\n    public abstract void setSwipeBackEnable(boolean enable);\n\n    /**\n     * Scroll out contentView and finish the activity\n     */\n    public abstract void scrollToFinishActivity();\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/ui/swipeback/SwipeBackActivityHelper.java",
    "content": "\npackage com.way.ui.swipeback;\n\nimport java.lang.reflect.Method;\n\nimport android.annotation.TargetApi;\nimport android.app.Activity;\nimport android.app.ActivityOptions;\nimport android.graphics.drawable.ColorDrawable;\nimport android.os.Build;\nimport android.view.LayoutInflater;\nimport android.view.View;\n\n\n\n\n/**\n * @author Yrom\n */\npublic class SwipeBackActivityHelper {\n    private Activity mActivity;\n\n    private SwipeBackLayout mSwipeBackLayout;\n\n    public SwipeBackActivityHelper(Activity activity) {\n        mActivity = activity;\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    public void onActivityCreate() {\n        mActivity.getWindow().setBackgroundDrawable(new ColorDrawable(0));\n        mActivity.getWindow().getDecorView().setBackgroundDrawable(null);\n        mSwipeBackLayout = (SwipeBackLayout) LayoutInflater.from(mActivity).inflate(\n        \t\tcom.way.yahoo.R.layout.swipeback_layout, null);\n        mSwipeBackLayout.addSwipeListener(new SwipeBackLayout.SwipeListener() {\n            @Override\n            public void onScrollStateChange(int state, float scrollPercent) {\n                if (state == SwipeBackLayout.STATE_IDLE && scrollPercent == 0) {\n                    convertActivityFromTranslucent();\n                }\n            }\n\n            @Override\n            public void onEdgeTouch(int edgeFlag) {\n                convertActivityToTranslucent();\n            }\n\n            @Override\n            public void onScrollOverThreshold() {\n\n            }\n\n\t\t\t@Override\n\t\t\tpublic void onScrollLeftChange(int left) {\n\t\t\t\t\n\t\t\t}\n        });\n    }\n\n    public void onPostCreate() {\n        mSwipeBackLayout.attachToActivity(mActivity);\n        convertActivityFromTranslucent();\n    }\n\n    public View findViewById(int id) {\n        if (mSwipeBackLayout != null) {\n            return mSwipeBackLayout.findViewById(id);\n        }\n        return null;\n    }\n\n    public SwipeBackLayout getSwipeBackLayout() {\n        return mSwipeBackLayout;\n    }\n\n    /**\n     * Convert a translucent themed Activity\n     * {@link android.R.attr#windowIsTranslucent} to a fullscreen opaque\n     * Activity.\n     * <p>\n     * Call this whenever the background of a translucent Activity has changed\n     * to become opaque. Doing so will allow the {@link android.view.Surface} of\n     * the Activity behind to be released.\n     * <p>\n     * This call has no effect on non-translucent activities or on activities\n     * with the {@link android.R.attr#windowIsFloating} attribute.\n     */\n    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)\n\tpublic void convertActivityFromTranslucent() {\n        try {\n//            Method method = Activity.class.getDeclaredMethod(\"convertFromTranslucent\", null);\n//            method.setAccessible(true);\n//            method.invoke(mActivity, null);\n\t\t\tif (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {\n\t\t\t\tMethod method = Activity.class.getDeclaredMethod(\n\t\t\t\t\t\t\"convertToTranslucent\");\n\t\t\t\tmethod.setAccessible(true);\n\t\t\t\tmethod.invoke(mActivity, new Object[] { null });\n\t\t\t} else {\n\t\t\t\tMethod method = Activity.class.getDeclaredMethod(\n\t\t\t\t\t\t\"convertToTranslucent\", null, ActivityOptions.class);\n\t\t\t\tmethod.setAccessible(true);\n\t\t\t\tmethod.invoke(mActivity, new Object[] { null, null });\n\t\t\t}\n        } catch (Throwable t) {\n        }\n    }\n\n    /**\n     * Convert a translucent themed Activity\n     * {@link android.R.attr#windowIsTranslucent} back from opaque to\n     * translucent following a call to {@link #convertActivityFromTranslucent()}\n     * .\n     * <p>\n     * Calling this allows the Activity behind this one to be seen again. Once\n     * all such Activities have been redrawn\n     * <p>\n     * This call has no effect on non-translucent activities or on activities\n     * with the {@link android.R.attr#windowIsFloating} attribute.\n     */\n    public void convertActivityToTranslucent() {\n        try {\n            Class<?>[] classes = Activity.class.getDeclaredClasses();\n            Class<?> translucentConversionListenerClazz = null;\n            for (Class clazz : classes) {\n                if (clazz.getSimpleName().contains(\"TranslucentConversionListener\")) {\n                    translucentConversionListenerClazz = clazz;\n                }\n            }\n            Method method = Activity.class.getDeclaredMethod(\"convertToTranslucent\",\n                    translucentConversionListenerClazz);\n            method.setAccessible(true);\n            method.invoke(mActivity, new Object[] {\n                null\n            });\n        } catch (Throwable t) {\n        }\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/ui/swipeback/SwipeBackLayout.java",
    "content": "package com.way.ui.swipeback;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\n\r\nimport android.app.Activity;\r\nimport android.content.Context;\r\nimport android.content.res.TypedArray;\r\nimport android.graphics.Canvas;\r\nimport android.graphics.Rect;\r\nimport android.graphics.drawable.Drawable;\r\nimport android.support.v4.view.ViewCompat;\r\nimport android.util.AttributeSet;\r\nimport android.view.MotionEvent;\r\nimport android.view.View;\r\nimport android.view.ViewGroup;\r\nimport android.widget.FrameLayout;\r\n\r\nimport com.way.yahoo.R;\r\n\r\npublic class SwipeBackLayout extends FrameLayout {\r\n\t/**\r\n\t * Minimum velocity that will be detected as a fling\r\n\t */\r\n\tprivate static final int MIN_FLING_VELOCITY = 400; // dips per second\r\n\r\n\tprivate static final int DEFAULT_SCRIM_COLOR = 0x99000000;\r\n\r\n\t/**\r\n\t * Edge flag indicating that the left edge should be affected.\r\n\t */\r\n\tpublic static final int EDGE_LEFT = ViewDragHelper.EDGE_LEFT;\r\n\r\n\t/**\r\n\t * Edge flag indicating that the right edge should be affected.\r\n\t */\r\n\tpublic static final int EDGE_RIGHT = ViewDragHelper.EDGE_RIGHT;\r\n\r\n\t/**\r\n\t * Edge flag indicating that the bottom edge should be affected.\r\n\t */\r\n\tpublic static final int EDGE_BOTTOM = ViewDragHelper.EDGE_BOTTOM;\r\n\r\n\t/**\r\n\t * Edge flag set indicating all edges should be affected.\r\n\t */\r\n\tpublic static final int EDGE_ALL = EDGE_LEFT | EDGE_RIGHT | EDGE_BOTTOM;\r\n\r\n\t/**\r\n\t * A view is not currently being dragged or animating as a result of a\r\n\t * fling/snap.\r\n\t */\r\n\tpublic static final int STATE_IDLE = ViewDragHelper.STATE_IDLE;\r\n\r\n\t/**\r\n\t * A view is currently being dragged. The position is currently changing as\r\n\t * a result of user input or simulated user input.\r\n\t */\r\n\tpublic static final int STATE_DRAGGING = ViewDragHelper.STATE_DRAGGING;\r\n\r\n\t/**\r\n\t * A view is currently settling into place as a result of a fling or\r\n\t * predefined non-interactive motion.\r\n\t */\r\n\tpublic static final int STATE_SETTLING = ViewDragHelper.STATE_SETTLING;\r\n\r\n\t/**\r\n\t * Default threshold of scroll\r\n\t */\r\n\tprivate static final float DEFAULT_SCROLL_THRESHOLD = 0.3f;\r\n\r\n\tprivate static final int OVERSCROLL_DISTANCE = 10;\r\n\r\n\tprivate static final int[] EDGE_FLAGS = { EDGE_LEFT, EDGE_RIGHT,\r\n\t\t\tEDGE_BOTTOM, EDGE_ALL };\r\n\r\n\tprivate int mEdgeFlag;\r\n\r\n\t/**\r\n\t * Threshold of scroll, we will close the activity, when scrollPercent over\r\n\t * this value;\r\n\t */\r\n\tprivate float mScrollThreshold = DEFAULT_SCROLL_THRESHOLD;\r\n\r\n\tprivate Activity mActivity;\r\n\r\n\tprivate boolean mEnable = true;\r\n\r\n\tprivate View mContentView;\r\n\r\n\tprivate ViewDragHelper mDragHelper;\r\n\r\n\tprivate float mScrollPercent;\r\n\r\n\tprivate int mContentLeft;\r\n\r\n\tprivate int mContentTop;\r\n\r\n\t/**\r\n\t * The set of listeners to be sent events through.\r\n\t */\r\n\tprivate List<SwipeListener> mListeners;\r\n\r\n\tprivate Drawable mShadowLeft;\r\n\r\n\tprivate Drawable mShadowRight;\r\n\r\n\tprivate Drawable mShadowBottom;\r\n\r\n\tprivate float mScrimOpacity;\r\n\r\n\tprivate int mScrimColor = DEFAULT_SCRIM_COLOR;\r\n\r\n\tprivate boolean mInLayout;\r\n\r\n\tprivate Rect mTmpRect = new Rect();\r\n\r\n\t/**\r\n\t * Edge being dragged\r\n\t */\r\n\tprivate int mTrackingEdge;\r\n\r\n\tpublic SwipeBackLayout(Context context) {\r\n\t\tthis(context, null);\r\n\t}\r\n\r\n\tpublic SwipeBackLayout(Context context, AttributeSet attrs) {\r\n\t\tthis(context, attrs, R.attr.SwipeBackLayoutStyle);\r\n\t}\r\n\r\n\tpublic SwipeBackLayout(Context context, AttributeSet attrs, int defStyle) {\r\n\t\tsuper(context, attrs);\r\n\t\tmDragHelper = ViewDragHelper.create(this, new ViewDragCallback());\r\n\r\n\t\tTypedArray a = context.obtainStyledAttributes(attrs,\r\n\t\t\t\tR.styleable.SwipeBackLayout, defStyle, R.style.SwipeBackLayout);\r\n\r\n\t\tint edgeSize = a.getDimensionPixelSize(\r\n\t\t\t\tR.styleable.SwipeBackLayout_edge_size, -1);\r\n\t\tif (edgeSize > 0)\r\n\t\t\tsetEdgeSize(edgeSize);\r\n\t\tint mode = EDGE_FLAGS[a\r\n\t\t\t\t.getInt(R.styleable.SwipeBackLayout_edge_flag, 0)];\r\n\t\tsetEdgeTrackingEnabled(mode);\r\n\r\n\t\tint shadowLeft = a\r\n\t\t\t\t.getResourceId(R.styleable.SwipeBackLayout_shadow_left,\r\n\t\t\t\t\t\tR.drawable.shadow_left);\r\n\t\tint shadowRight = a.getResourceId(\r\n\t\t\t\tR.styleable.SwipeBackLayout_shadow_right,\r\n\t\t\t\tR.drawable.shadow_right);\r\n\t\tint shadowBottom = a.getResourceId(\r\n\t\t\t\tR.styleable.SwipeBackLayout_shadow_bottom,\r\n\t\t\t\tR.drawable.shadow_bottom);\r\n\t\tsetShadow(shadowLeft, EDGE_LEFT);\r\n\t\tsetShadow(shadowRight, EDGE_RIGHT);\r\n\t\tsetShadow(shadowBottom, EDGE_BOTTOM);\r\n\t\ta.recycle();\r\n\t\tfinal float density = getResources().getDisplayMetrics().density;\r\n\t\tfinal float minVel = MIN_FLING_VELOCITY * density;\r\n\t\tmDragHelper.setMinVelocity(minVel);\r\n\r\n\t}\r\n\r\n\t/**\r\n\t * Set up contentView which will be moved by user gesture\r\n\t * \r\n\t * @param view\r\n\t */\r\n\tprivate void setContentView(View view) {\r\n\t\tmContentView = view;\r\n\t}\r\n\r\n\tpublic void setEnableGesture(boolean enable) {\r\n\t\tmEnable = enable;\r\n\t}\r\n\r\n\t/**\r\n\t * Enable edge tracking for the selected edges of the parent view. The\r\n\t * callback's\r\n\t * {@link com.way.ui.swipeback.ViewDragHelper.Callback#onEdgeTouched(int, int)}\r\n\t * and\r\n\t * {@link com.way.ui.swipeback.ViewDragHelper.Callback#onEdgeDragStarted(int, int)}\r\n\t * methods will only be invoked for edges for which edge tracking has been\r\n\t * enabled.\r\n\t * \r\n\t * @param edgeFlags\r\n\t *            Combination of edge flags describing the edges to watch\r\n\t * @see #EDGE_LEFT\r\n\t * @see #EDGE_RIGHT\r\n\t * @see #EDGE_BOTTOM\r\n\t */\r\n\tpublic void setEdgeTrackingEnabled(int edgeFlags) {\r\n\t\tmEdgeFlag = edgeFlags;\r\n\t\tmDragHelper.setEdgeTrackingEnabled(mEdgeFlag);\r\n\t}\r\n\r\n\t/**\r\n\t * Set a color to use for the scrim that obscures primary content while a\r\n\t * drawer is open.\r\n\t * \r\n\t * @param color\r\n\t *            Color to use in 0xAARRGGBB format.\r\n\t */\r\n\tpublic void setScrimColor(int color) {\r\n\t\tmScrimColor = color;\r\n\t\tinvalidate();\r\n\t}\r\n\r\n\t/**\r\n\t * Set the size of an edge. This is the range in pixels along the edges of\r\n\t * this view that will actively detect edge touches or drags if edge\r\n\t * tracking is enabled.\r\n\t * \r\n\t * @param size\r\n\t *            The size of an edge in pixels\r\n\t */\r\n\tpublic void setEdgeSize(int size) {\r\n\t\tmDragHelper.setEdgeSize(size);\r\n\t}\r\n\r\n\t/**\r\n\t * Register a callback to be invoked when a swipe event is sent to this\r\n\t * view.\r\n\t * \r\n\t * @param listener\r\n\t *            the swipe listener to attach to this view\r\n\t * @deprecated use {@link #addSwipeListener} instead\r\n\t */\r\n\t@Deprecated\r\n\tpublic void setSwipeListener(SwipeListener listener) {\r\n\t\taddSwipeListener(listener);\r\n\t}\r\n\r\n\t/**\r\n\t * Add a callback to be invoked when a swipe event is sent to this view.\r\n\t * \r\n\t * @param listener\r\n\t *            the swipe listener to attach to this view\r\n\t */\r\n\tpublic void addSwipeListener(SwipeListener listener) {\r\n\t\tif (mListeners == null) {\r\n\t\t\tmListeners = new ArrayList<SwipeListener>();\r\n\t\t}\r\n\t\tmListeners.add(listener);\r\n\t}\r\n\r\n\t/**\r\n\t * Removes a listener from the set of listeners\r\n\t * \r\n\t * @param listener\r\n\t */\r\n\tpublic void removeSwipeListener(SwipeListener listener) {\r\n\t\tif (mListeners == null) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tmListeners.remove(listener);\r\n\t}\r\n\r\n\tpublic static interface SwipeListener {\r\n\t\t/**\r\n\t\t * Invoke when state change\r\n\t\t * \r\n\t\t * @param state\r\n\t\t *            flag to describe scroll state\r\n\t\t * @see #STATE_IDLE\r\n\t\t * @see #STATE_DRAGGING\r\n\t\t * @see #STATE_SETTLING\r\n\t\t * @param scrollPercent\r\n\t\t *            scroll percent of this view\r\n\t\t */\r\n\t\tpublic void onScrollStateChange(int state, float scrollPercent);\r\n\r\n\t\tpublic void onScrollLeftChange(int left);\r\n\r\n\t\t/**\r\n\t\t * Invoke when edge touched\r\n\t\t * \r\n\t\t * @param edgeFlag\r\n\t\t *            edge flag describing the edge being touched\r\n\t\t * @see #EDGE_LEFT\r\n\t\t * @see #EDGE_RIGHT\r\n\t\t * @see #EDGE_BOTTOM\r\n\t\t */\r\n\t\tpublic void onEdgeTouch(int edgeFlag);\r\n\r\n\t\t/**\r\n\t\t * Invoke when scroll percent over the threshold for the first time\r\n\t\t */\r\n\t\tpublic void onScrollOverThreshold();\r\n\t}\r\n\r\n\t/**\r\n\t * Set scroll threshold, we will close the activity, when scrollPercent over\r\n\t * this value\r\n\t * \r\n\t * @param threshold\r\n\t */\r\n\tpublic void setScrollThresHold(float threshold) {\r\n\t\tif (threshold >= 1.0f || threshold <= 0) {\r\n\t\t\tthrow new IllegalArgumentException(\r\n\t\t\t\t\t\"Threshold value should be between 0 and 1.0\");\r\n\t\t}\r\n\t\tmScrollThreshold = threshold;\r\n\t}\r\n\r\n\t/**\r\n\t * Set a drawable used for edge shadow.\r\n\t * \r\n\t * @param shadow\r\n\t *            Drawable to use\r\n\t * @param edgeFlags\r\n\t *            Combination of edge flags describing the edge to set\r\n\t * @see #EDGE_LEFT\r\n\t * @see #EDGE_RIGHT\r\n\t * @see #EDGE_BOTTOM\r\n\t */\r\n\tpublic void setShadow(Drawable shadow, int edgeFlag) {\r\n\t\tif ((edgeFlag & EDGE_LEFT) != 0) {\r\n\t\t\tmShadowLeft = shadow;\r\n\t\t} else if ((edgeFlag & EDGE_RIGHT) != 0) {\r\n\t\t\tmShadowRight = shadow;\r\n\t\t} else if ((edgeFlag & EDGE_BOTTOM) != 0) {\r\n\t\t\tmShadowBottom = shadow;\r\n\t\t}\r\n\t\tinvalidate();\r\n\t}\r\n\r\n\t/**\r\n\t * Set a drawable used for edge shadow.\r\n\t * \r\n\t * @param resId\r\n\t *            Resource of drawable to use\r\n\t * @param edgeFlags\r\n\t *            Combination of edge flags describing the edge to set\r\n\t * @see #EDGE_LEFT\r\n\t * @see #EDGE_RIGHT\r\n\t * @see #EDGE_BOTTOM\r\n\t */\r\n\tpublic void setShadow(int resId, int edgeFlag) {\r\n\t\tsetShadow(getResources().getDrawable(resId), edgeFlag);\r\n\t}\r\n\r\n\t/**\r\n\t * Scroll out contentView and finish the activity\r\n\t */\r\n\tpublic void scrollToFinishActivity() {\r\n\t\tfinal int childWidth = mContentView.getWidth();\r\n\t\tfinal int childHeight = mContentView.getHeight();\r\n\r\n\t\tint left = 0, top = 0;\r\n\t\tif ((mEdgeFlag & EDGE_LEFT) != 0) {\r\n\t\t\tleft = childWidth + mShadowLeft.getIntrinsicWidth()\r\n\t\t\t\t\t+ OVERSCROLL_DISTANCE;\r\n\t\t\tmTrackingEdge = EDGE_LEFT;\r\n\t\t} else if ((mEdgeFlag & EDGE_RIGHT) != 0) {\r\n\t\t\tleft = -childWidth - mShadowRight.getIntrinsicWidth()\r\n\t\t\t\t\t- OVERSCROLL_DISTANCE;\r\n\t\t\tmTrackingEdge = EDGE_RIGHT;\r\n\t\t} else if ((mEdgeFlag & EDGE_BOTTOM) != 0) {\r\n\t\t\ttop = -childHeight - mShadowBottom.getIntrinsicHeight()\r\n\t\t\t\t\t- OVERSCROLL_DISTANCE;\r\n\t\t\tmTrackingEdge = EDGE_BOTTOM;\r\n\t\t}\r\n\r\n\t\tmDragHelper.smoothSlideViewTo(mContentView, left, top);\r\n\t\tinvalidate();\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean onInterceptTouchEvent(MotionEvent event) {\r\n\t\tif (!mEnable) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\ttry {\r\n\t\t\treturn mDragHelper.shouldInterceptTouchEvent(event);\r\n\t\t} catch (ArrayIndexOutOfBoundsException e) {\r\n\t\t\t// FIXME: handle exception\r\n\t\t\t// issues #9\r\n\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean onTouchEvent(MotionEvent event) {\r\n\t\tif (!mEnable) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\tmDragHelper.processTouchEvent(event);\r\n\t\treturn true;\r\n\t}\r\n\r\n\t@Override\r\n\tprotected void onLayout(boolean changed, int left, int top, int right,\r\n\t\t\tint bottom) {\r\n\t\tmInLayout = true;\r\n\t\tif (mContentView != null)\r\n\t\t\tmContentView.layout(mContentLeft, mContentTop, mContentLeft\r\n\t\t\t\t\t+ mContentView.getMeasuredWidth(), mContentTop\r\n\t\t\t\t\t+ mContentView.getMeasuredHeight());\r\n\t\tmInLayout = false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void requestLayout() {\r\n\t\tif (!mInLayout) {\r\n\t\t\tsuper.requestLayout();\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tprotected boolean drawChild(Canvas canvas, View child, long drawingTime) {\r\n\t\tfinal boolean drawContent = child == mContentView;\r\n\t\tdrawShadow(canvas, child);\r\n\r\n\t\tboolean ret = super.drawChild(canvas, child, drawingTime);\r\n\t\tif (mScrimOpacity > 0 && drawContent\r\n\t\t\t\t&& mDragHelper.getViewDragState() != ViewDragHelper.STATE_IDLE) {\r\n\t\t\tdrawScrim(canvas, child);\r\n\t\t}\r\n\t\treturn ret;\r\n\t}\r\n\r\n\tprivate void drawScrim(Canvas canvas, View child) {\r\n\t\tfinal int baseAlpha = (mScrimColor & 0xff000000) >>> 24;\r\n\t\tfinal int alpha = (int) (baseAlpha * mScrimOpacity);\r\n\t\tfinal int color = alpha << 24 | (mScrimColor & 0xffffff);\r\n\r\n\t\tif ((mTrackingEdge & EDGE_LEFT) != 0) {\r\n\t\t\tcanvas.clipRect(0, 0, child.getLeft(), getHeight());\r\n\t\t} else if ((mTrackingEdge & EDGE_RIGHT) != 0) {\r\n\t\t\tcanvas.clipRect(child.getRight(), 0, getRight(), getHeight());\r\n\t\t} else if ((mTrackingEdge & EDGE_BOTTOM) != 0) {\r\n\t\t\tcanvas.clipRect(child.getLeft(), child.getBottom(), getRight(),\r\n\t\t\t\t\tgetHeight());\r\n\t\t}\r\n\t\tcanvas.drawColor(color);\r\n\t}\r\n\r\n\tprivate void drawShadow(Canvas canvas, View child) {\r\n\t\tfinal Rect childRect = mTmpRect;\r\n\t\tchild.getHitRect(childRect);\r\n\r\n\t\tif ((mEdgeFlag & EDGE_LEFT) != 0) {\r\n\t\t\tmShadowLeft.setBounds(\r\n\t\t\t\t\tchildRect.left - mShadowLeft.getIntrinsicWidth(),\r\n\t\t\t\t\tchildRect.top, childRect.left, childRect.bottom);\r\n\t\t\tmShadowLeft.draw(canvas);\r\n\t\t}\r\n\r\n\t\tif ((mEdgeFlag & EDGE_RIGHT) != 0) {\r\n\t\t\tmShadowRight.setBounds(childRect.right, childRect.top,\r\n\t\t\t\t\tchildRect.right + mShadowRight.getIntrinsicWidth(),\r\n\t\t\t\t\tchildRect.bottom);\r\n\t\t\tmShadowRight.draw(canvas);\r\n\t\t}\r\n\r\n\t\tif ((mEdgeFlag & EDGE_BOTTOM) != 0) {\r\n\t\t\tmShadowBottom.setBounds(childRect.left, childRect.bottom,\r\n\t\t\t\t\tchildRect.right,\r\n\t\t\t\t\tchildRect.bottom + mShadowBottom.getIntrinsicHeight());\r\n\t\t\tmShadowBottom.draw(canvas);\r\n\t\t}\r\n\t}\r\n\r\n\tpublic void attachToActivity(Activity activity) {\r\n\t\tmActivity = activity;\r\n\t\tTypedArray a = activity.getTheme().obtainStyledAttributes(\r\n\t\t\t\tnew int[] { android.R.attr.windowBackground });\r\n\t\tint background = a.getResourceId(0, 0);\r\n\t\ta.recycle();\r\n\r\n\t\tViewGroup decor = (ViewGroup) activity.getWindow().getDecorView();\r\n\t\tViewGroup decorChild = (ViewGroup) decor.getChildAt(0);\r\n\t\tdecorChild.setBackgroundResource(background);\r\n\t\tdecor.removeView(decorChild);\r\n\t\taddView(decorChild);\r\n\t\tsetContentView(decorChild);\r\n\t\tdecor.addView(this);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void computeScroll() {\r\n\t\tmScrimOpacity = 1 - mScrollPercent;\r\n\t\tif (mDragHelper.continueSettling(true)) {\r\n\t\t\tViewCompat.postInvalidateOnAnimation(this);\r\n\t\t}\r\n\t}\r\n\r\n\tprivate class ViewDragCallback extends ViewDragHelper.Callback {\r\n\t\tprivate boolean mIsScrollOverValid;\r\n\r\n\t\t@Override\r\n\t\tpublic boolean tryCaptureView(View view, int i) {\r\n\t\t\tboolean ret = mDragHelper.isEdgeTouched(mEdgeFlag, i);\r\n\t\t\tif (ret) {\r\n\t\t\t\tif (mDragHelper.isEdgeTouched(EDGE_LEFT, i)) {\r\n\t\t\t\t\tmTrackingEdge = EDGE_LEFT;\r\n\t\t\t\t} else if (mDragHelper.isEdgeTouched(EDGE_RIGHT, i)) {\r\n\t\t\t\t\tmTrackingEdge = EDGE_RIGHT;\r\n\t\t\t\t} else if (mDragHelper.isEdgeTouched(EDGE_BOTTOM, i)) {\r\n\t\t\t\t\tmTrackingEdge = EDGE_BOTTOM;\r\n\t\t\t\t}\r\n\t\t\t\tif (mListeners != null && !mListeners.isEmpty()) {\r\n\t\t\t\t\tfor (SwipeListener listener : mListeners) {\r\n\t\t\t\t\t\tlistener.onEdgeTouch(mTrackingEdge);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\tmIsScrollOverValid = true;\r\n\t\t\t}\r\n\t\t\tboolean directionCheck = false;\r\n\t\t\tif (mEdgeFlag == EDGE_LEFT || mEdgeFlag == EDGE_RIGHT) {\r\n\t\t\t\tdirectionCheck = !mDragHelper.checkTouchSlop(\r\n\t\t\t\t\t\tViewDragHelper.DIRECTION_VERTICAL, i);\r\n\t\t\t} else if (mEdgeFlag == EDGE_BOTTOM) {\r\n\t\t\t\tdirectionCheck = !mDragHelper.checkTouchSlop(\r\n\t\t\t\t\t\tViewDragHelper.DIRECTION_HORIZONTAL, i);\r\n\t\t\t} else if (mEdgeFlag == EDGE_ALL) {\r\n\t\t\t\tdirectionCheck = true;\r\n\t\t\t}\r\n\t\t\treturn ret & directionCheck;\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic int getViewHorizontalDragRange(View child) {\r\n\t\t\treturn mEdgeFlag & (EDGE_LEFT | EDGE_RIGHT);\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic int getViewVerticalDragRange(View child) {\r\n\t\t\treturn mEdgeFlag & EDGE_BOTTOM;\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic void onViewPositionChanged(View changedView, int left, int top,\r\n\t\t\t\tint dx, int dy) {\r\n\t\t\tfor (SwipeListener listener : mListeners) {\r\n\t\t\t\tlistener.onScrollLeftChange(left);\r\n\t\t\t}\r\n\t\t\tsuper.onViewPositionChanged(changedView, left, top, dx, dy);\r\n\t\t\tif ((mTrackingEdge & EDGE_LEFT) != 0) {\r\n\t\t\t\tmScrollPercent = Math.abs((float) left\r\n\t\t\t\t\t\t/ (mContentView.getWidth() + mShadowLeft\r\n\t\t\t\t\t\t\t\t.getIntrinsicWidth()));\r\n\t\t\t} else if ((mTrackingEdge & EDGE_RIGHT) != 0) {\r\n\t\t\t\tmScrollPercent = Math.abs((float) left\r\n\t\t\t\t\t\t/ (mContentView.getWidth() + mShadowRight\r\n\t\t\t\t\t\t\t\t.getIntrinsicWidth()));\r\n\t\t\t} else if ((mTrackingEdge & EDGE_BOTTOM) != 0) {\r\n\t\t\t\tmScrollPercent = Math.abs((float) top\r\n\t\t\t\t\t\t/ (mContentView.getHeight() + mShadowBottom\r\n\t\t\t\t\t\t\t\t.getIntrinsicHeight()));\r\n\t\t\t}\r\n\t\t\tmContentLeft = left;\r\n\t\t\tmContentTop = top;\r\n\t\t\tinvalidate();\r\n\t\t\tif (mScrollPercent < mScrollThreshold && !mIsScrollOverValid) {\r\n\t\t\t\tmIsScrollOverValid = true;\r\n\t\t\t}\r\n\t\t\tif (mListeners != null && !mListeners.isEmpty()\r\n\t\t\t\t\t&& mDragHelper.getViewDragState() == STATE_DRAGGING\r\n\t\t\t\t\t&& mScrollPercent >= mScrollThreshold && mIsScrollOverValid) {\r\n\t\t\t\tmIsScrollOverValid = false;\r\n\t\t\t\tfor (SwipeListener listener : mListeners) {\r\n\t\t\t\t\tlistener.onScrollOverThreshold();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (mScrollPercent >= 1) {\r\n\t\t\t\tif (!mActivity.isFinishing()) {\r\n\t\t\t\t\tmActivity.finish();\r\n\t\t\t\t\tmActivity.overridePendingTransition(0, 0);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic void onViewReleased(View releasedChild, float xvel, float yvel) {\r\n\t\t\tfinal int childWidth = releasedChild.getWidth();\r\n\t\t\tfinal int childHeight = releasedChild.getHeight();\r\n\r\n\t\t\tint left = 0, top = 0;\r\n\t\t\tif ((mTrackingEdge & EDGE_LEFT) != 0) {\r\n\t\t\t\tleft = xvel > 0 || xvel == 0\r\n\t\t\t\t\t\t&& mScrollPercent > mScrollThreshold ? childWidth\r\n\t\t\t\t\t\t+ mShadowLeft.getIntrinsicWidth() + OVERSCROLL_DISTANCE\r\n\t\t\t\t\t\t: 0;\r\n\t\t\t} else if ((mTrackingEdge & EDGE_RIGHT) != 0) {\r\n\t\t\t\tleft = xvel < 0 || xvel == 0\r\n\t\t\t\t\t\t&& mScrollPercent > mScrollThreshold ? -(childWidth\r\n\t\t\t\t\t\t+ mShadowLeft.getIntrinsicWidth() + OVERSCROLL_DISTANCE)\r\n\t\t\t\t\t\t: 0;\r\n\t\t\t} else if ((mTrackingEdge & EDGE_BOTTOM) != 0) {\r\n\t\t\t\ttop = yvel < 0 || yvel == 0\r\n\t\t\t\t\t\t&& mScrollPercent > mScrollThreshold ? -(childHeight\r\n\t\t\t\t\t\t+ mShadowBottom.getIntrinsicHeight() + OVERSCROLL_DISTANCE)\r\n\t\t\t\t\t\t: 0;\r\n\t\t\t}\r\n\r\n\t\t\tmDragHelper.settleCapturedViewAt(left, top);\r\n\t\t\tinvalidate();\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic int clampViewPositionHorizontal(View child, int left, int dx) {\r\n\t\t\tint ret = 0;\r\n\t\t\tif ((mTrackingEdge & EDGE_LEFT) != 0) {\r\n\t\t\t\tret = Math.min(child.getWidth(), Math.max(left, 0));\r\n\t\t\t} else if ((mTrackingEdge & EDGE_RIGHT) != 0) {\r\n\t\t\t\tret = Math.min(0, Math.max(left, -child.getWidth()));\r\n\t\t\t}\r\n\t\t\treturn ret;\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic int clampViewPositionVertical(View child, int top, int dy) {\r\n\t\t\tint ret = 0;\r\n\t\t\tif ((mTrackingEdge & EDGE_BOTTOM) != 0) {\r\n\t\t\t\tret = Math.min(0, Math.max(top, -child.getHeight()));\r\n\t\t\t}\r\n\t\t\treturn ret;\r\n\t\t}\r\n\r\n\t\t@Override\r\n\t\tpublic void onViewDragStateChanged(int state) {\r\n\t\t\tsuper.onViewDragStateChanged(state);\r\n\t\t\tif (mListeners != null && !mListeners.isEmpty()) {\r\n\t\t\t\tfor (SwipeListener listener : mListeners) {\r\n\t\t\t\t\tlistener.onScrollStateChange(state, mScrollPercent);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "WayHoo/src/com/way/ui/swipeback/ViewDragHelper.java",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.way.ui.swipeback;\n\nimport java.util.Arrays;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.support.v4.view.MotionEventCompat;\nimport android.support.v4.view.VelocityTrackerCompat;\nimport android.support.v4.view.ViewCompat;\nimport android.support.v4.widget.ScrollerCompat;\nimport android.view.MotionEvent;\nimport android.view.VelocityTracker;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.view.ViewGroup;\nimport android.view.animation.Interpolator;\n\n/**\n * ViewDragHelper is a utility class for writing custom ViewGroups. It offers a\n * number of useful operations and state tracking for allowing a user to drag\n * and reposition views within their parent ViewGroup.\n */\n@SuppressLint(\"NewApi\")\npublic class ViewDragHelper {\n\tprivate static final String TAG = \"ViewDragHelper\";\n\n\t/**\n\t * A null/invalid pointer ID.\n\t */\n\tpublic static final int INVALID_POINTER = -1;\n\n\t/**\n\t * A view is not currently being dragged or animating as a result of a\n\t * fling/snap.\n\t */\n\tpublic static final int STATE_IDLE = 0;\n\n\t/**\n\t * A view is currently being dragged. The position is currently changing as\n\t * a result of user input or simulated user input.\n\t */\n\tpublic static final int STATE_DRAGGING = 1;\n\n\t/**\n\t * A view is currently settling into place as a result of a fling or\n\t * predefined non-interactive motion.\n\t */\n\tpublic static final int STATE_SETTLING = 2;\n\n\t/**\n\t * Edge flag indicating that the left edge should be affected.\n\t */\n\tpublic static final int EDGE_LEFT = 1 << 0;\n\n\t/**\n\t * Edge flag indicating that the right edge should be affected.\n\t */\n\tpublic static final int EDGE_RIGHT = 1 << 1;\n\n\t/**\n\t * Edge flag indicating that the top edge should be affected.\n\t */\n\tpublic static final int EDGE_TOP = 1 << 2;\n\n\t/**\n\t * Edge flag indicating that the bottom edge should be affected.\n\t */\n\tpublic static final int EDGE_BOTTOM = 1 << 3;\n\n\t/**\n\t * Edge flag set indicating all edges should be affected.\n\t */\n\tpublic static final int EDGE_ALL = EDGE_LEFT | EDGE_TOP | EDGE_RIGHT\n\t\t\t| EDGE_BOTTOM;\n\n\t/**\n\t * Indicates that a check should occur along the horizontal axis\n\t */\n\tpublic static final int DIRECTION_HORIZONTAL = 1 << 0;\n\n\t/**\n\t * Indicates that a check should occur along the vertical axis\n\t */\n\tpublic static final int DIRECTION_VERTICAL = 1 << 1;\n\n\t/**\n\t * Indicates that a check should occur along all axes\n\t */\n\tpublic static final int DIRECTION_ALL = DIRECTION_HORIZONTAL\n\t\t\t| DIRECTION_VERTICAL;\n\n\tpublic static final int EDGE_SIZE = 20; // dp\n\n\tprivate static final int BASE_SETTLE_DURATION = 256; // ms\n\n\tprivate static final int MAX_SETTLE_DURATION = 600; // ms\n\n\t// Current drag state; idle, dragging or settling\n\tprivate int mDragState;\n\n\t// Distance to travel before a drag may begin\n\tprivate int mTouchSlop;\n\n\t// Last known position/pointer tracking\n\tprivate int mActivePointerId = INVALID_POINTER;\n\n\tprivate float[] mInitialMotionX;\n\n\tprivate float[] mInitialMotionY;\n\n\tprivate float[] mLastMotionX;\n\n\tprivate float[] mLastMotionY;\n\n\tprivate int[] mInitialEdgesTouched;\n\n\tprivate int[] mEdgeDragsInProgress;\n\n\tprivate int[] mEdgeDragsLocked;\n\n\tprivate int mPointersDown;\n\n\tprivate VelocityTracker mVelocityTracker;\n\n\tprivate float mMaxVelocity;\n\n\tprivate float mMinVelocity;\n\n\tprivate int mEdgeSize;\n\n\tprivate int mTrackingEdges;\n\n\tprivate ScrollerCompat mScroller;\n\n\tprivate final Callback mCallback;\n\n\tprivate View mCapturedView;\n\n\tprivate boolean mReleaseInProgress;\n\n\tprivate final ViewGroup mParentView;\n\n\t/**\n\t * A Callback is used as a communication channel with the ViewDragHelper\n\t * back to the parent view using it. <code>on*</code>methods are invoked on\n\t * siginficant events and several accessor methods are expected to provide\n\t * the ViewDragHelper with more information about the state of the parent\n\t * view upon request. The callback also makes decisions governing the range\n\t * and draggability of child views.\n\t */\n\tpublic static abstract class Callback {\n\t\t/**\n\t\t * Called when the drag state changes. See the <code>STATE_*</code>\n\t\t * constants for more information.\n\t\t * \n\t\t * @param state\n\t\t *            The new drag state\n\t\t * @see #STATE_IDLE\n\t\t * @see #STATE_DRAGGING\n\t\t * @see #STATE_SETTLING\n\t\t */\n\t\tpublic void onViewDragStateChanged(int state) {\n\t\t}\n\n\t\t/**\n\t\t * Called when the captured view's position changes as the result of a\n\t\t * drag or settle.\n\t\t * \n\t\t * @param changedView\n\t\t *            View whose position changed\n\t\t * @param left\n\t\t *            New X coordinate of the left edge of the view\n\t\t * @param top\n\t\t *            New Y coordinate of the top edge of the view\n\t\t * @param dx\n\t\t *            Change in X position from the last call\n\t\t * @param dy\n\t\t *            Change in Y position from the last call\n\t\t */\n\t\tpublic void onViewPositionChanged(View changedView, int left, int top,\n\t\t\t\tint dx, int dy) {\n\t\t}\n\n\t\t/**\n\t\t * Called when a child view is captured for dragging or settling. The ID\n\t\t * of the pointer currently dragging the captured view is supplied. If\n\t\t * activePointerId is identified as {@link #INVALID_POINTER} the capture\n\t\t * is programmatic instead of pointer-initiated.\n\t\t * \n\t\t * @param capturedChild\n\t\t *            Child view that was captured\n\t\t * @param activePointerId\n\t\t *            Pointer id tracking the child capture\n\t\t */\n\t\tpublic void onViewCaptured(View capturedChild, int activePointerId) {\n\t\t}\n\n\t\t/**\n\t\t * Called when the child view is no longer being actively dragged. The\n\t\t * fling velocity is also supplied, if relevant. The velocity values may\n\t\t * be clamped to system minimums or maximums.\n\t\t * <p>\n\t\t * Calling code may decide to fling or otherwise release the view to let\n\t\t * it settle into place. It should do so using\n\t\t * {@link #settleCapturedViewAt(int, int)} or\n\t\t * {@link #flingCapturedView(int, int, int, int)}. If the Callback\n\t\t * invokes one of these methods, the ViewDragHelper will enter\n\t\t * {@link #STATE_SETTLING} and the view capture will not fully end until\n\t\t * it comes to a complete stop. If neither of these methods is invoked\n\t\t * before <code>onViewReleased</code> returns, the view will stop in\n\t\t * place and the ViewDragHelper will return to {@link #STATE_IDLE}.\n\t\t * </p>\n\t\t * \n\t\t * @param releasedChild\n\t\t *            The captured child view now being released\n\t\t * @param xvel\n\t\t *            X velocity of the pointer as it left the screen in pixels\n\t\t *            per second.\n\t\t * @param yvel\n\t\t *            Y velocity of the pointer as it left the screen in pixels\n\t\t *            per second.\n\t\t */\n\t\tpublic void onViewReleased(View releasedChild, float xvel, float yvel) {\n\t\t}\n\n\t\t/**\n\t\t * Called when one of the subscribed edges in the parent view has been\n\t\t * touched by the user while no child view is currently captured.\n\t\t * \n\t\t * @param edgeFlags\n\t\t *            A combination of edge flags describing the edge(s)\n\t\t *            currently touched\n\t\t * @param pointerId\n\t\t *            ID of the pointer touching the described edge(s)\n\t\t * @see #EDGE_LEFT\n\t\t * @see #EDGE_TOP\n\t\t * @see #EDGE_RIGHT\n\t\t * @see #EDGE_BOTTOM\n\t\t */\n\t\tpublic void onEdgeTouched(int edgeFlags, int pointerId) {\n\t\t}\n\n\t\t/**\n\t\t * Called when the given edge may become locked. This can happen if an\n\t\t * edge drag was preliminarily rejected before beginning, but after\n\t\t * {@link #onEdgeTouched(int, int)} was called. This method should\n\t\t * return true to lock this edge or false to leave it unlocked. The\n\t\t * default behavior is to leave edges unlocked.\n\t\t * \n\t\t * @param edgeFlags\n\t\t *            A combination of edge flags describing the edge(s) locked\n\t\t * @return true to lock the edge, false to leave it unlocked\n\t\t */\n\t\tpublic boolean onEdgeLock(int edgeFlags) {\n\t\t\treturn false;\n\t\t}\n\n\t\t/**\n\t\t * Called when the user has started a deliberate drag away from one of\n\t\t * the subscribed edges in the parent view while no child view is\n\t\t * currently captured.\n\t\t * \n\t\t * @param edgeFlags\n\t\t *            A combination of edge flags describing the edge(s) dragged\n\t\t * @param pointerId\n\t\t *            ID of the pointer touching the described edge(s)\n\t\t * @see #EDGE_LEFT\n\t\t * @see #EDGE_TOP\n\t\t * @see #EDGE_RIGHT\n\t\t * @see #EDGE_BOTTOM\n\t\t */\n\t\tpublic void onEdgeDragStarted(int edgeFlags, int pointerId) {\n\t\t}\n\n\t\t/**\n\t\t * Called to determine the Z-order of child views.\n\t\t * \n\t\t * @param index\n\t\t *            the ordered position to query for\n\t\t * @return index of the view that should be ordered at position\n\t\t *         <code>index</code>\n\t\t */\n\t\tpublic int getOrderedChildIndex(int index) {\n\t\t\treturn index;\n\t\t}\n\n\t\t/**\n\t\t * Return the magnitude of a draggable child view's horizontal range of\n\t\t * motion in pixels. This method should return 0 for views that cannot\n\t\t * move horizontally.\n\t\t * \n\t\t * @param child\n\t\t *            Child view to check\n\t\t * @return range of horizontal motion in pixels\n\t\t */\n\t\tpublic int getViewHorizontalDragRange(View child) {\n\t\t\treturn 0;\n\t\t}\n\n\t\t/**\n\t\t * Return the magnitude of a draggable child view's vertical range of\n\t\t * motion in pixels. This method should return 0 for views that cannot\n\t\t * move vertically.\n\t\t * \n\t\t * @param child\n\t\t *            Child view to check\n\t\t * @return range of vertical motion in pixels\n\t\t */\n\t\tpublic int getViewVerticalDragRange(View child) {\n\t\t\treturn 0;\n\t\t}\n\n\t\t/**\n\t\t * Called when the user's input indicates that they want to capture the\n\t\t * given child view with the pointer indicated by pointerId. The\n\t\t * callback should return true if the user is permitted to drag the\n\t\t * given view with the indicated pointer.\n\t\t * <p>\n\t\t * ViewDragHelper may call this method multiple times for the same view\n\t\t * even if the view is already captured; this indicates that a new\n\t\t * pointer is trying to take control of the view.\n\t\t * </p>\n\t\t * <p>\n\t\t * If this method returns true, a call to\n\t\t * {@link #onViewCaptured(android.view.View, int)} will follow if the\n\t\t * capture is successful.\n\t\t * </p>\n\t\t * \n\t\t * @param child\n\t\t *            Child the user is attempting to capture\n\t\t * @param pointerId\n\t\t *            ID of the pointer attempting the capture\n\t\t * @return true if capture should be allowed, false otherwise\n\t\t */\n\t\tpublic abstract boolean tryCaptureView(View child, int pointerId);\n\n\t\t/**\n\t\t * Restrict the motion of the dragged child view along the horizontal\n\t\t * axis. The default implementation does not allow horizontal motion;\n\t\t * the extending class must override this method and provide the desired\n\t\t * clamping.\n\t\t * \n\t\t * @param child\n\t\t *            Child view being dragged\n\t\t * @param left\n\t\t *            Attempted motion along the X axis\n\t\t * @param dx\n\t\t *            Proposed change in position for left\n\t\t * @return The new clamped position for left\n\t\t */\n\t\tpublic int clampViewPositionHorizontal(View child, int left, int dx) {\n\t\t\treturn 0;\n\t\t}\n\n\t\t/**\n\t\t * Restrict the motion of the dragged child view along the vertical\n\t\t * axis. The default implementation does not allow vertical motion; the\n\t\t * extending class must override this method and provide the desired\n\t\t * clamping.\n\t\t * \n\t\t * @param child\n\t\t *            Child view being dragged\n\t\t * @param top\n\t\t *            Attempted motion along the Y axis\n\t\t * @param dy\n\t\t *            Proposed change in position for top\n\t\t * @return The new clamped position for top\n\t\t */\n\t\tpublic int clampViewPositionVertical(View child, int top, int dy) {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t/**\n\t * Interpolator defining the animation curve for mScroller\n\t */\n\tprivate static final Interpolator sInterpolator = new Interpolator() {\n\t\tpublic float getInterpolation(float t) {\n\t\t\tt -= 1.0f;\n\t\t\treturn t * t * t * t * t + 1.0f;\n\t\t}\n\t};\n\n\tprivate final Runnable mSetIdleRunnable = new Runnable() {\n\t\tpublic void run() {\n\t\t\tsetDragState(STATE_IDLE);\n\t\t}\n\t};\n\n\t/**\n\t * Factory method to create a new ViewDragHelper.\n\t * \n\t * @param forParent\n\t *            Parent view to monitor\n\t * @param cb\n\t *            Callback to provide information and receive events\n\t * @return a new ViewDragHelper instance\n\t */\n\tpublic static ViewDragHelper create(ViewGroup forParent, Callback cb) {\n\t\treturn new ViewDragHelper(forParent.getContext(), forParent, cb);\n\t}\n\n\t/**\n\t * Factory method to create a new ViewDragHelper.\n\t * \n\t * @param forParent\n\t *            Parent view to monitor\n\t * @param sensitivity\n\t *            Multiplier for how sensitive the helper should be about\n\t *            detecting the start of a drag. Larger values are more\n\t *            sensitive. 1.0f is normal.\n\t * @param cb\n\t *            Callback to provide information and receive events\n\t * @return a new ViewDragHelper instance\n\t */\n\tpublic static ViewDragHelper create(ViewGroup forParent, float sensitivity,\n\t\t\tCallback cb) {\n\t\tfinal ViewDragHelper helper = create(forParent, cb);\n\t\thelper.mTouchSlop = (int) (helper.mTouchSlop * (1 / sensitivity));\n\t\treturn helper;\n\t}\n\n\t/**\n\t * Apps should use ViewDragHelper.create() to get a new instance. This will\n\t * allow VDH to use internal compatibility implementations for different\n\t * platform versions.\n\t * \n\t * @param context\n\t *            Context to initialize config-dependent params from\n\t * @param forParent\n\t *            Parent view to monitor\n\t */\n\tprivate ViewDragHelper(Context context, ViewGroup forParent, Callback cb) {\n\t\tif (forParent == null) {\n\t\t\tthrow new IllegalArgumentException(\"Parent view may not be null\");\n\t\t}\n\t\tif (cb == null) {\n\t\t\tthrow new IllegalArgumentException(\"Callback may not be null\");\n\t\t}\n\n\t\tmParentView = forParent;\n\t\tmCallback = cb;\n\n\t\tfinal ViewConfiguration vc = ViewConfiguration.get(context);\n\t\tfinal float density = context.getResources().getDisplayMetrics().density;\n\t\tmEdgeSize = (int) (EDGE_SIZE * density + 0.5f);\n\n\t\tmTouchSlop = vc.getScaledTouchSlop();\n\t\tmMaxVelocity = vc.getScaledMaximumFlingVelocity();\n\t\tmMinVelocity = vc.getScaledMinimumFlingVelocity();\n\t\tmScroller = ScrollerCompat.create(context, sInterpolator);\n\t}\n\n\t/**\n\t * Set the minimum velocity that will be detected as having a magnitude\n\t * greater than zero in pixels per second. Callback methods accepting a\n\t * velocity will be clamped appropriately.\n\t * \n\t * @param minVel\n\t *            Minimum velocity to detect\n\t */\n\tpublic void setMinVelocity(float minVel) {\n\t\tmMinVelocity = minVel;\n\t}\n\n\t/**\n\t * Return the currently configured minimum velocity. Any flings with a\n\t * magnitude less than this value in pixels per second. Callback methods\n\t * accepting a velocity will receive zero as a velocity value if the real\n\t * detected velocity was below this threshold.\n\t * \n\t * @return the minimum velocity that will be detected\n\t */\n\tpublic float getMinVelocity() {\n\t\treturn mMinVelocity;\n\t}\n\n\t/**\n\t * Retrieve the current drag state of this helper. This will return one of\n\t * {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or {@link #STATE_SETTLING}.\n\t * \n\t * @return The current drag state\n\t */\n\tpublic int getViewDragState() {\n\t\treturn mDragState;\n\t}\n\n\t/**\n\t * Enable edge tracking for the selected edges of the parent view. The\n\t * callback's\n\t * {@link com.way.ui.swipeback.ViewDragHelper.Callback#onEdgeTouched(int, int)}\n\t * and\n\t * {@link com.way.ui.swipeback.ViewDragHelper.Callback#onEdgeDragStarted(int, int)}\n\t * methods will only be invoked for edges for which edge tracking has been\n\t * enabled.\n\t * \n\t * @param edgeFlags\n\t *            Combination of edge flags describing the edges to watch\n\t * @see #EDGE_LEFT\n\t * @see #EDGE_TOP\n\t * @see #EDGE_RIGHT\n\t * @see #EDGE_BOTTOM\n\t */\n\tpublic void setEdgeTrackingEnabled(int edgeFlags) {\n\t\tmTrackingEdges = edgeFlags;\n\t}\n\n\t/**\n\t * Return the size of an edge. This is the range in pixels along the edges\n\t * of this view that will actively detect edge touches or drags if edge\n\t * tracking is enabled.\n\t * \n\t * @return The size of an edge in pixels\n\t * @see #setEdgeTrackingEnabled(int)\n\t */\n\tpublic int getEdgeSize() {\n\t\treturn mEdgeSize;\n\t}\n\n\t/**\n\t * Set the size of an edge. This is the range in pixels along the edges of\n\t * this view that will actively detect edge touches or drags if edge\n\t * tracking is enabled.\n\t * \n\t * @param size\n\t *            The size of an edge in pixels\n\t */\n\tpublic void setEdgeSize(int size) {\n\t\tmEdgeSize = size;\n\t}\n\n\t/**\n\t * Capture a specific child view for dragging within the parent. The\n\t * callback will be notified but\n\t * {@link com.way.ui.swipeback.ViewDragHelper.Callback#tryCaptureView(android.view.View, int)}\n\t * will not be asked permission to capture this view.\n\t * \n\t * @param childView\n\t *            Child view to capture\n\t * @param activePointerId\n\t *            ID of the pointer that is dragging the captured child view\n\t */\n\tpublic void captureChildView(View childView, int activePointerId) {\n\t\tif (childView.getParent() != mParentView) {\n\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\"captureChildView: parameter must be a descendant \"\n\t\t\t\t\t\t\t+ \"of the ViewDragHelper's tracked parent view (\"\n\t\t\t\t\t\t\t+ mParentView + \")\");\n\t\t}\n\n\t\tmCapturedView = childView;\n\t\tmActivePointerId = activePointerId;\n\t\tmCallback.onViewCaptured(childView, activePointerId);\n\t\tsetDragState(STATE_DRAGGING);\n\t}\n\n\t/**\n\t * @return The currently captured view, or null if no view has been\n\t *         captured.\n\t */\n\tpublic View getCapturedView() {\n\t\treturn mCapturedView;\n\t}\n\n\t/**\n\t * @return The ID of the pointer currently dragging the captured view, or\n\t *         {@link #INVALID_POINTER}.\n\t */\n\tpublic int getActivePointerId() {\n\t\treturn mActivePointerId;\n\t}\n\n\t/**\n\t * @return The minimum distance in pixels that the user must travel to\n\t *         initiate a drag\n\t */\n\tpublic int getTouchSlop() {\n\t\treturn mTouchSlop;\n\t}\n\n\t/**\n\t * The result of a call to this method is equivalent to\n\t * {@link #processTouchEvent(android.view.MotionEvent)} receiving an\n\t * ACTION_CANCEL event.\n\t */\n\tpublic void cancel() {\n\t\tmActivePointerId = INVALID_POINTER;\n\t\tclearMotionHistory();\n\n\t\tif (mVelocityTracker != null) {\n\t\t\tmVelocityTracker.recycle();\n\t\t\tmVelocityTracker = null;\n\t\t}\n\t}\n\n\t/**\n\t * {@link #cancel()}, but also abort all motion in progress and snap to the\n\t * end of any animation.\n\t */\n\tpublic void abort() {\n\t\tcancel();\n\t\tif (mDragState == STATE_SETTLING) {\n\t\t\tfinal int oldX = mScroller.getCurrX();\n\t\t\tfinal int oldY = mScroller.getCurrY();\n\t\t\tmScroller.abortAnimation();\n\t\t\tfinal int newX = mScroller.getCurrX();\n\t\t\tfinal int newY = mScroller.getCurrY();\n\t\t\tmCallback.onViewPositionChanged(mCapturedView, newX, newY, newX\n\t\t\t\t\t- oldX, newY - oldY);\n\t\t}\n\t\tsetDragState(STATE_IDLE);\n\t}\n\n\t/**\n\t * Animate the view <code>child</code> to the given (left, top) position. If\n\t * this method returns true, the caller should invoke\n\t * {@link #continueSettling(boolean)} on each subsequent frame to continue\n\t * the motion until it returns false. If this method returns false there is\n\t * no further work to do to complete the movement.\n\t * <p>\n\t * This operation does not count as a capture event, though\n\t * {@link #getCapturedView()} will still report the sliding view while the\n\t * slide is in progress.\n\t * </p>\n\t * \n\t * @param child\n\t *            Child view to capture and animate\n\t * @param finalLeft\n\t *            Final left position of child\n\t * @param finalTop\n\t *            Final top position of child\n\t * @return true if animation should continue through\n\t *         {@link #continueSettling(boolean)} calls\n\t */\n\tpublic boolean smoothSlideViewTo(View child, int finalLeft, int finalTop) {\n\t\tmCapturedView = child;\n\t\tmActivePointerId = INVALID_POINTER;\n\n\t\treturn forceSettleCapturedViewAt(finalLeft, finalTop, 0, 0);\n\t}\n\n\t/**\n\t * Settle the captured view at the given (left, top) position. The\n\t * appropriate velocity from prior motion will be taken into account. If\n\t * this method returns true, the caller should invoke\n\t * {@link #continueSettling(boolean)} on each subsequent frame to continue\n\t * the motion until it returns false. If this method returns false there is\n\t * no further work to do to complete the movement.\n\t * \n\t * @param finalLeft\n\t *            Settled left edge position for the captured view\n\t * @param finalTop\n\t *            Settled top edge position for the captured view\n\t * @return true if animation should continue through\n\t *         {@link #continueSettling(boolean)} calls\n\t */\n\tpublic boolean settleCapturedViewAt(int finalLeft, int finalTop) {\n\t\tif (!mReleaseInProgress) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\"Cannot settleCapturedViewAt outside of a call to \"\n\t\t\t\t\t\t\t+ \"Callback#onViewReleased\");\n\t\t}\n\n\t\treturn forceSettleCapturedViewAt(finalLeft, finalTop,\n\t\t\t\t(int) VelocityTrackerCompat.getXVelocity(mVelocityTracker,\n\t\t\t\t\t\tmActivePointerId),\n\t\t\t\t(int) VelocityTrackerCompat.getYVelocity(mVelocityTracker,\n\t\t\t\t\t\tmActivePointerId));\n\t}\n\n\t/**\n\t * Settle the captured view at the given (left, top) position.\n\t * \n\t * @param finalLeft\n\t *            Target left position for the captured view\n\t * @param finalTop\n\t *            Target top position for the captured view\n\t * @param xvel\n\t *            Horizontal velocity\n\t * @param yvel\n\t *            Vertical velocity\n\t * @return true if animation should continue through\n\t *         {@link #continueSettling(boolean)} calls\n\t */\n\tprivate boolean forceSettleCapturedViewAt(int finalLeft, int finalTop,\n\t\t\tint xvel, int yvel) {\n\t\tfinal int startLeft = mCapturedView.getLeft();\n\t\tfinal int startTop = mCapturedView.getTop();\n\t\tfinal int dx = finalLeft - startLeft;\n\t\tfinal int dy = finalTop - startTop;\n\n\t\tif (dx == 0 && dy == 0) {\n\t\t\t// Nothing to do. Send callbacks, be done.\n\t\t\tmScroller.abortAnimation();\n\t\t\tsetDragState(STATE_IDLE);\n\t\t\treturn false;\n\t\t}\n\n\t\tfinal int duration = computeSettleDuration(mCapturedView, dx, dy, xvel,\n\t\t\t\tyvel);\n\t\tmScroller.startScroll(startLeft, startTop, dx, dy, duration);\n\n\t\tsetDragState(STATE_SETTLING);\n\t\treturn true;\n\t}\n\n\tprivate int computeSettleDuration(View child, int dx, int dy, int xvel,\n\t\t\tint yvel) {\n\t\txvel = clampMag(xvel, (int) mMinVelocity, (int) mMaxVelocity);\n\t\tyvel = clampMag(yvel, (int) mMinVelocity, (int) mMaxVelocity);\n\t\tfinal int absDx = Math.abs(dx);\n\t\tfinal int absDy = Math.abs(dy);\n\t\tfinal int absXVel = Math.abs(xvel);\n\t\tfinal int absYVel = Math.abs(yvel);\n\t\tfinal int addedVel = absXVel + absYVel;\n\t\tfinal int addedDistance = absDx + absDy;\n\n\t\tfinal float xweight = xvel != 0 ? (float) absXVel / addedVel\n\t\t\t\t: (float) absDx / addedDistance;\n\t\tfinal float yweight = yvel != 0 ? (float) absYVel / addedVel\n\t\t\t\t: (float) absDy / addedDistance;\n\n\t\tint xduration = computeAxisDuration(dx, xvel,\n\t\t\t\tmCallback.getViewHorizontalDragRange(child));\n\t\tint yduration = computeAxisDuration(dy, yvel,\n\t\t\t\tmCallback.getViewVerticalDragRange(child));\n\n\t\treturn (int) (xduration * xweight + yduration * yweight);\n\t}\n\n\tprivate int computeAxisDuration(int delta, int velocity, int motionRange) {\n\t\tif (delta == 0) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tfinal int width = mParentView.getWidth();\n\t\tfinal int halfWidth = width / 2;\n\t\tfinal float distanceRatio = Math.min(1f, (float) Math.abs(delta)\n\t\t\t\t/ width);\n\t\tfinal float distance = halfWidth + halfWidth\n\t\t\t\t* distanceInfluenceForSnapDuration(distanceRatio);\n\n\t\tint duration;\n\t\tvelocity = Math.abs(velocity);\n\t\tif (velocity > 0) {\n\t\t\tduration = 4 * Math.round(1000 * Math.abs(distance / velocity));\n\t\t} else {\n\t\t\tfinal float range = (float) Math.abs(delta) / motionRange;\n\t\t\tduration = (int) ((range + 1) * BASE_SETTLE_DURATION);\n\t\t}\n\t\treturn Math.min(duration, MAX_SETTLE_DURATION);\n\t}\n\n\t/**\n\t * Clamp the magnitude of value for absMin and absMax. If the value is below\n\t * the minimum, it will be clamped to zero. If the value is above the\n\t * maximum, it will be clamped to the maximum.\n\t * \n\t * @param value\n\t *            Value to clamp\n\t * @param absMin\n\t *            Absolute value of the minimum significant value to return\n\t * @param absMax\n\t *            Absolute value of the maximum value to return\n\t * @return The clamped value with the same sign as <code>value</code>\n\t */\n\tprivate int clampMag(int value, int absMin, int absMax) {\n\t\tfinal int absValue = Math.abs(value);\n\t\tif (absValue < absMin)\n\t\t\treturn 0;\n\t\tif (absValue > absMax)\n\t\t\treturn value > 0 ? absMax : -absMax;\n\t\treturn value;\n\t}\n\n\t/**\n\t * Clamp the magnitude of value for absMin and absMax. If the value is below\n\t * the minimum, it will be clamped to zero. If the value is above the\n\t * maximum, it will be clamped to the maximum.\n\t * \n\t * @param value\n\t *            Value to clamp\n\t * @param absMin\n\t *            Absolute value of the minimum significant value to return\n\t * @param absMax\n\t *            Absolute value of the maximum value to return\n\t * @return The clamped value with the same sign as <code>value</code>\n\t */\n\tprivate float clampMag(float value, float absMin, float absMax) {\n\t\tfinal float absValue = Math.abs(value);\n\t\tif (absValue < absMin)\n\t\t\treturn 0;\n\t\tif (absValue > absMax)\n\t\t\treturn value > 0 ? absMax : -absMax;\n\t\treturn value;\n\t}\n\n\tprivate float distanceInfluenceForSnapDuration(float f) {\n\t\tf -= 0.5f; // center the values about 0.\n\t\tf *= 0.3f * Math.PI / 2.0f;\n\t\treturn (float) Math.sin(f);\n\t}\n\n\t/**\n\t * Settle the captured view based on standard free-moving fling behavior.\n\t * The caller should invoke {@link #continueSettling(boolean)} on each\n\t * subsequent frame to continue the motion until it returns false.\n\t * \n\t * @param minLeft\n\t *            Minimum X position for the view's left edge\n\t * @param minTop\n\t *            Minimum Y position for the view's top edge\n\t * @param maxLeft\n\t *            Maximum X position for the view's left edge\n\t * @param maxTop\n\t *            Maximum Y position for the view's top edge\n\t */\n\tpublic void flingCapturedView(int minLeft, int minTop, int maxLeft,\n\t\t\tint maxTop) {\n\t\tif (!mReleaseInProgress) {\n\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\"Cannot flingCapturedView outside of a call to \"\n\t\t\t\t\t\t\t+ \"Callback#onViewReleased\");\n\t\t}\n\n\t\tmScroller.fling(mCapturedView.getLeft(), mCapturedView.getTop(),\n\t\t\t\t(int) VelocityTrackerCompat.getXVelocity(mVelocityTracker,\n\t\t\t\t\t\tmActivePointerId), (int) VelocityTrackerCompat\n\t\t\t\t\t\t.getYVelocity(mVelocityTracker, mActivePointerId),\n\t\t\t\tminLeft, maxLeft, minTop, maxTop);\n\n\t\tsetDragState(STATE_SETTLING);\n\t}\n\n\t/**\n\t * Move the captured settling view by the appropriate amount for the current\n\t * time. If <code>continueSettling</code> returns true, the caller should\n\t * call it again on the next frame to continue.\n\t * \n\t * @param deferCallbacks\n\t *            true if state callbacks should be deferred via posted message.\n\t *            Set this to true if you are calling this method from\n\t *            {@link android.view.View#computeScroll()} or similar methods\n\t *            invoked as part of layout or drawing.\n\t * @return true if settle is still in progress\n\t */\n\tpublic boolean continueSettling(boolean deferCallbacks) {\n\t\tif (mDragState == STATE_SETTLING) {\n\t\t\tboolean keepGoing = mScroller.computeScrollOffset();\n\t\t\tfinal int x = mScroller.getCurrX();\n\t\t\tfinal int y = mScroller.getCurrY();\n\t\t\tfinal int dx = x - mCapturedView.getLeft();\n\t\t\tfinal int dy = y - mCapturedView.getTop();\n\n\t\t\tif (dx != 0) {\n\t\t\t\tmCapturedView.offsetLeftAndRight(dx);\n\t\t\t}\n\t\t\tif (dy != 0) {\n\t\t\t\tmCapturedView.offsetTopAndBottom(dy);\n\t\t\t}\n\n\t\t\tif (dx != 0 || dy != 0) {\n\t\t\t\tmCallback.onViewPositionChanged(mCapturedView, x, y, dx, dy);\n\t\t\t}\n\n\t\t\tif (keepGoing && x == mScroller.getFinalX()\n\t\t\t\t\t&& y == mScroller.getFinalY()) {\n\t\t\t\t// Close enough. The interpolator/scroller might think we're\n\t\t\t\t// still moving\n\t\t\t\t// but the user sure doesn't.\n\t\t\t\tmScroller.abortAnimation();\n\t\t\t\tkeepGoing = mScroller.isFinished();\n\t\t\t}\n\n\t\t\tif (!keepGoing) {\n\t\t\t\tif (deferCallbacks) {\n\t\t\t\t\tmParentView.post(mSetIdleRunnable);\n\t\t\t\t} else {\n\t\t\t\t\tsetDragState(STATE_IDLE);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn mDragState == STATE_SETTLING;\n\t}\n\n\t/**\n\t * Like all callback events this must happen on the UI thread, but release\n\t * involves some extra semantics. During a release (mReleaseInProgress) is\n\t * the only time it is valid to call {@link #settleCapturedViewAt(int, int)}\n\t * or {@link #flingCapturedView(int, int, int, int)}.\n\t */\n\tprivate void dispatchViewReleased(float xvel, float yvel) {\n\t\tmReleaseInProgress = true;\n\t\tmCallback.onViewReleased(mCapturedView, xvel, yvel);\n\t\tmReleaseInProgress = false;\n\n\t\tif (mDragState == STATE_DRAGGING) {\n\t\t\t// onViewReleased didn't call a method that would have changed this.\n\t\t\t// Go idle.\n\t\t\tsetDragState(STATE_IDLE);\n\t\t}\n\t}\n\n\tprivate void clearMotionHistory() {\n\t\tif (mInitialMotionX == null) {\n\t\t\treturn;\n\t\t}\n\t\tArrays.fill(mInitialMotionX, 0);\n\t\tArrays.fill(mInitialMotionY, 0);\n\t\tArrays.fill(mLastMotionX, 0);\n\t\tArrays.fill(mLastMotionY, 0);\n\t\tArrays.fill(mInitialEdgesTouched, 0);\n\t\tArrays.fill(mEdgeDragsInProgress, 0);\n\t\tArrays.fill(mEdgeDragsLocked, 0);\n\t\tmPointersDown = 0;\n\t}\n\n\tprivate void clearMotionHistory(int pointerId) {\n\t\tif (mInitialMotionX == null) {\n\t\t\treturn;\n\t\t}\n\t\tmInitialMotionX[pointerId] = 0;\n\t\tmInitialMotionY[pointerId] = 0;\n\t\tmLastMotionX[pointerId] = 0;\n\t\tmLastMotionY[pointerId] = 0;\n\t\tmInitialEdgesTouched[pointerId] = 0;\n\t\tmEdgeDragsInProgress[pointerId] = 0;\n\t\tmEdgeDragsLocked[pointerId] = 0;\n\t\tmPointersDown &= ~(1 << pointerId);\n\t}\n\n\tprivate void ensureMotionHistorySizeForId(int pointerId) {\n\t\tif (mInitialMotionX == null || mInitialMotionX.length <= pointerId) {\n\t\t\tfloat[] imx = new float[pointerId + 1];\n\t\t\tfloat[] imy = new float[pointerId + 1];\n\t\t\tfloat[] lmx = new float[pointerId + 1];\n\t\t\tfloat[] lmy = new float[pointerId + 1];\n\t\t\tint[] iit = new int[pointerId + 1];\n\t\t\tint[] edip = new int[pointerId + 1];\n\t\t\tint[] edl = new int[pointerId + 1];\n\n\t\t\tif (mInitialMotionX != null) {\n\t\t\t\tSystem.arraycopy(mInitialMotionX, 0, imx, 0,\n\t\t\t\t\t\tmInitialMotionX.length);\n\t\t\t\tSystem.arraycopy(mInitialMotionY, 0, imy, 0,\n\t\t\t\t\t\tmInitialMotionY.length);\n\t\t\t\tSystem.arraycopy(mLastMotionX, 0, lmx, 0, mLastMotionX.length);\n\t\t\t\tSystem.arraycopy(mLastMotionY, 0, lmy, 0, mLastMotionY.length);\n\t\t\t\tSystem.arraycopy(mInitialEdgesTouched, 0, iit, 0,\n\t\t\t\t\t\tmInitialEdgesTouched.length);\n\t\t\t\tSystem.arraycopy(mEdgeDragsInProgress, 0, edip, 0,\n\t\t\t\t\t\tmEdgeDragsInProgress.length);\n\t\t\t\tSystem.arraycopy(mEdgeDragsLocked, 0, edl, 0,\n\t\t\t\t\t\tmEdgeDragsLocked.length);\n\t\t\t}\n\n\t\t\tmInitialMotionX = imx;\n\t\t\tmInitialMotionY = imy;\n\t\t\tmLastMotionX = lmx;\n\t\t\tmLastMotionY = lmy;\n\t\t\tmInitialEdgesTouched = iit;\n\t\t\tmEdgeDragsInProgress = edip;\n\t\t\tmEdgeDragsLocked = edl;\n\t\t}\n\t}\n\n\tprivate void saveInitialMotion(float x, float y, int pointerId) {\n\t\tensureMotionHistorySizeForId(pointerId);\n\t\tmInitialMotionX[pointerId] = mLastMotionX[pointerId] = x;\n\t\tmInitialMotionY[pointerId] = mLastMotionY[pointerId] = y;\n\t\tmInitialEdgesTouched[pointerId] = getEdgesTouched((int) x, (int) y);\n\t\tmPointersDown |= 1 << pointerId;\n\t}\n\n\tprivate void saveLastMotion(MotionEvent ev) {\n\t\tfinal int pointerCount = MotionEventCompat.getPointerCount(ev);\n\t\tfor (int i = 0; i < pointerCount; i++) {\n\t\t\tfinal int pointerId = MotionEventCompat.getPointerId(ev, i);\n\t\t\tfinal float x = MotionEventCompat.getX(ev, i);\n\t\t\tfinal float y = MotionEventCompat.getY(ev, i);\n\t\t\tmLastMotionX[pointerId] = x;\n\t\t\tmLastMotionY[pointerId] = y;\n\t\t}\n\t}\n\n\t/**\n\t * Check if the given pointer ID represents a pointer that is currently down\n\t * (to the best of the ViewDragHelper's knowledge).\n\t * <p>\n\t * The state used to report this information is populated by the methods\n\t * {@link #shouldInterceptTouchEvent(android.view.MotionEvent)} or\n\t * {@link #processTouchEvent(android.view.MotionEvent)}. If one of these\n\t * methods has not been called for all relevant MotionEvents to track, the\n\t * information reported by this method may be stale or incorrect.\n\t * </p>\n\t * \n\t * @param pointerId\n\t *            pointer ID to check; corresponds to IDs provided by\n\t *            MotionEvent\n\t * @return true if the pointer with the given ID is still down\n\t */\n\tpublic boolean isPointerDown(int pointerId) {\n\t\treturn (mPointersDown & 1 << pointerId) != 0;\n\t}\n\n\tvoid setDragState(int state) {\n\t\tif (mDragState != state) {\n\t\t\tmDragState = state;\n\t\t\tmCallback.onViewDragStateChanged(state);\n\t\t\tif (state == STATE_IDLE) {\n\t\t\t\tmCapturedView = null;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Attempt to capture the view with the given pointer ID. The callback will\n\t * be involved. This will put us into the \"dragging\" state. If we've already\n\t * captured this view with this pointer this method will immediately return\n\t * true without consulting the callback.\n\t * \n\t * @param toCapture\n\t *            View to capture\n\t * @param pointerId\n\t *            Pointer to capture with\n\t * @return true if capture was successful\n\t */\n\tboolean tryCaptureViewForDrag(View toCapture, int pointerId) {\n\t\tif (toCapture == mCapturedView && mActivePointerId == pointerId) {\n\t\t\t// Already done!\n\t\t\treturn true;\n\t\t}\n\t\tif (toCapture != null && mCallback.tryCaptureView(toCapture, pointerId)) {\n\t\t\tmActivePointerId = pointerId;\n\t\t\tcaptureChildView(toCapture, pointerId);\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Tests scrollability within child views of v given a delta of dx.\n\t * \n\t * @param v\n\t *            View to test for horizontal scrollability\n\t * @param checkV\n\t *            Whether the view v passed should itself be checked for\n\t *            scrollability (true), or just its children (false).\n\t * @param dx\n\t *            Delta scrolled in pixels along the X axis\n\t * @param dy\n\t *            Delta scrolled in pixels along the Y axis\n\t * @param x\n\t *            X coordinate of the active touch point\n\t * @param y\n\t *            Y coordinate of the active touch point\n\t * @return true if child views of v can be scrolled by delta of dx.\n\t */\n\tprotected boolean canScroll(View v, boolean checkV, int dx, int dy, int x,\n\t\t\tint y) {\n\t\tif (v instanceof ViewGroup) {\n\t\t\tfinal ViewGroup group = (ViewGroup) v;\n\t\t\tfinal int scrollX = v.getScrollX();\n\t\t\tfinal int scrollY = v.getScrollY();\n\t\t\tfinal int count = group.getChildCount();\n\t\t\t// Count backwards - let topmost views consume scroll distance\n\t\t\t// first.\n\t\t\tfor (int i = count - 1; i >= 0; i--) {\n\t\t\t\t// TODO: Add versioned support here for transformed views.\n\t\t\t\t// This will not work for transformed views in Honeycomb+\n\t\t\t\tfinal View child = group.getChildAt(i);\n\t\t\t\tif (x + scrollX >= child.getLeft()\n\t\t\t\t\t\t&& x + scrollX < child.getRight()\n\t\t\t\t\t\t&& y + scrollY >= child.getTop()\n\t\t\t\t\t\t&& y + scrollY < child.getBottom()\n\t\t\t\t\t\t&& canScroll(child, true, dx, dy,\n\t\t\t\t\t\t\t\tx + scrollX - child.getLeft(), y + scrollY\n\t\t\t\t\t\t\t\t\t\t- child.getTop())) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn checkV\n\t\t\t\t&& (ViewCompat.canScrollHorizontally(v, -dx) || ViewCompat\n\t\t\t\t\t\t.canScrollVertically(v, -dy));\n\t}\n\n\t/**\n\t * Check if this event as provided to the parent view's\n\t * onInterceptTouchEvent should cause the parent to intercept the touch\n\t * event stream.\n\t * \n\t * @param ev\n\t *            MotionEvent provided to onInterceptTouchEvent\n\t * @return true if the parent view should return true from\n\t *         onInterceptTouchEvent\n\t */\n\tpublic boolean shouldInterceptTouchEvent(MotionEvent ev) {\n\t\tfinal int action = MotionEventCompat.getActionMasked(ev);\n\t\tfinal int actionIndex = MotionEventCompat.getActionIndex(ev);\n\n\t\tif (action == MotionEvent.ACTION_DOWN) {\n\t\t\t// Reset things for a new event stream, just in case we didn't get\n\t\t\t// the whole previous stream.\n\t\t\tcancel();\n\t\t}\n\n\t\tif (mVelocityTracker == null) {\n\t\t\tmVelocityTracker = VelocityTracker.obtain();\n\t\t}\n\t\tmVelocityTracker.addMovement(ev);\n\n\t\tswitch (action) {\n\t\tcase MotionEvent.ACTION_DOWN: {\n\t\t\tfinal float x = ev.getX();\n\t\t\tfinal float y = ev.getY();\n\t\t\tfinal int pointerId = MotionEventCompat.getPointerId(ev, 0);\n\t\t\tsaveInitialMotion(x, y, pointerId);\n\n\t\t\tfinal View toCapture = findTopChildUnder((int) x, (int) y);\n\n\t\t\t// Catch a settling view if possible.\n\t\t\tif (toCapture == mCapturedView && mDragState == STATE_SETTLING) {\n\t\t\t\ttryCaptureViewForDrag(toCapture, pointerId);\n\t\t\t}\n\n\t\t\tfinal int edgesTouched = mInitialEdgesTouched[pointerId];\n\t\t\tif ((edgesTouched & mTrackingEdges) != 0) {\n\t\t\t\tmCallback.onEdgeTouched(edgesTouched & mTrackingEdges,\n\t\t\t\t\t\tpointerId);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase MotionEventCompat.ACTION_POINTER_DOWN: {\n\t\t\tfinal int pointerId = MotionEventCompat.getPointerId(ev,\n\t\t\t\t\tactionIndex);\n\t\t\tfinal float x = MotionEventCompat.getX(ev, actionIndex);\n\t\t\tfinal float y = MotionEventCompat.getY(ev, actionIndex);\n\n\t\t\tsaveInitialMotion(x, y, pointerId);\n\n\t\t\t// A ViewDragHelper can only manipulate one view at a time.\n\t\t\tif (mDragState == STATE_IDLE) {\n\t\t\t\tfinal int edgesTouched = mInitialEdgesTouched[pointerId];\n\t\t\t\tif ((edgesTouched & mTrackingEdges) != 0) {\n\t\t\t\t\tmCallback.onEdgeTouched(edgesTouched & mTrackingEdges,\n\t\t\t\t\t\t\tpointerId);\n\t\t\t\t}\n\t\t\t} else if (mDragState == STATE_SETTLING) {\n\t\t\t\t// Catch a settling view if possible.\n\t\t\t\tfinal View toCapture = findTopChildUnder((int) x, (int) y);\n\t\t\t\tif (toCapture == mCapturedView) {\n\t\t\t\t\ttryCaptureViewForDrag(toCapture, pointerId);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase MotionEvent.ACTION_MOVE: {\n\t\t\t// First to cross a touch slop over a draggable view wins. Also\n\t\t\t// report edge drags.\n\t\t\tfinal int pointerCount = MotionEventCompat.getPointerCount(ev);\n\t\t\tfor (int i = 0; i < pointerCount; i++) {\n\t\t\t\tfinal int pointerId = MotionEventCompat.getPointerId(ev, i);\n\t\t\t\tfinal float x = MotionEventCompat.getX(ev, i);\n\t\t\t\tfinal float y = MotionEventCompat.getY(ev, i);\n\t\t\t\tfinal float dx = x - mInitialMotionX[pointerId];\n\t\t\t\tfinal float dy = y - mInitialMotionY[pointerId];\n\n\t\t\t\treportNewEdgeDrags(dx, dy, pointerId);\n\t\t\t\tif (mDragState == STATE_DRAGGING) {\n\t\t\t\t\t// Callback might have started an edge drag\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tfinal View toCapture = findTopChildUnder((int) x, (int) y);\n\t\t\t\tif (toCapture != null && checkTouchSlop(toCapture, dx, dy)\n\t\t\t\t\t\t&& tryCaptureViewForDrag(toCapture, pointerId)) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tsaveLastMotion(ev);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase MotionEventCompat.ACTION_POINTER_UP: {\n\t\t\tfinal int pointerId = MotionEventCompat.getPointerId(ev,\n\t\t\t\t\tactionIndex);\n\t\t\tclearMotionHistory(pointerId);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase MotionEvent.ACTION_UP:\n\t\tcase MotionEvent.ACTION_CANCEL: {\n\t\t\tcancel();\n\t\t\tbreak;\n\t\t}\n\t\t}\n\n\t\treturn mDragState == STATE_DRAGGING;\n\t}\n\n\t/**\n\t * Process a touch event received by the parent view. This method will\n\t * dispatch callback events as needed before returning. The parent view's\n\t * onTouchEvent implementation should call this.\n\t * \n\t * @param ev\n\t *            The touch event received by the parent view\n\t */\n\tpublic void processTouchEvent(MotionEvent ev) {\n\t\tfinal int action = MotionEventCompat.getActionMasked(ev);\n\t\tfinal int actionIndex = MotionEventCompat.getActionIndex(ev);\n\n\t\tif (action == MotionEvent.ACTION_DOWN) {\n\t\t\t// Reset things for a new event stream, just in case we didn't get\n\t\t\t// the whole previous stream.\n\t\t\tcancel();\n\t\t}\n\n\t\tif (mVelocityTracker == null) {\n\t\t\tmVelocityTracker = VelocityTracker.obtain();\n\t\t}\n\t\tmVelocityTracker.addMovement(ev);\n\n\t\tswitch (action) {\n\t\tcase MotionEvent.ACTION_DOWN: {\n\t\t\tfinal float x = ev.getX();\n\t\t\tfinal float y = ev.getY();\n\t\t\tfinal int pointerId = MotionEventCompat.getPointerId(ev, 0);\n\t\t\tfinal View toCapture = findTopChildUnder((int) x, (int) y);\n\n\t\t\tsaveInitialMotion(x, y, pointerId);\n\n\t\t\t// Since the parent is already directly processing this touch\n\t\t\t// event,\n\t\t\t// there is no reason to delay for a slop before dragging.\n\t\t\t// Start immediately if possible.\n\t\t\ttryCaptureViewForDrag(toCapture, pointerId);\n\n\t\t\tfinal int edgesTouched = mInitialEdgesTouched[pointerId];\n\t\t\tif ((edgesTouched & mTrackingEdges) != 0) {\n\t\t\t\tmCallback.onEdgeTouched(edgesTouched & mTrackingEdges,\n\t\t\t\t\t\tpointerId);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase MotionEventCompat.ACTION_POINTER_DOWN: {\n\t\t\tfinal int pointerId = MotionEventCompat.getPointerId(ev,\n\t\t\t\t\tactionIndex);\n\t\t\tfinal float x = MotionEventCompat.getX(ev, actionIndex);\n\t\t\tfinal float y = MotionEventCompat.getY(ev, actionIndex);\n\n\t\t\tsaveInitialMotion(x, y, pointerId);\n\n\t\t\t// A ViewDragHelper can only manipulate one view at a time.\n\t\t\tif (mDragState == STATE_IDLE) {\n\t\t\t\t// If we're idle we can do anything! Treat it like a normal\n\t\t\t\t// down event.\n\n\t\t\t\tfinal View toCapture = findTopChildUnder((int) x, (int) y);\n\t\t\t\ttryCaptureViewForDrag(toCapture, pointerId);\n\n\t\t\t\tfinal int edgesTouched = mInitialEdgesTouched[pointerId];\n\t\t\t\tif ((edgesTouched & mTrackingEdges) != 0) {\n\t\t\t\t\tmCallback.onEdgeTouched(edgesTouched & mTrackingEdges,\n\t\t\t\t\t\t\tpointerId);\n\t\t\t\t}\n\t\t\t} else if (isCapturedViewUnder((int) x, (int) y)) {\n\t\t\t\t// We're still tracking a captured view. If the same view is\n\t\t\t\t// under this\n\t\t\t\t// point, we'll swap to controlling it with this pointer\n\t\t\t\t// instead.\n\t\t\t\t// (This will still work if we're \"catching\" a settling\n\t\t\t\t// view.)\n\n\t\t\t\ttryCaptureViewForDrag(mCapturedView, pointerId);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase MotionEvent.ACTION_MOVE: {\n\t\t\tif (mDragState == STATE_DRAGGING) {\n\t\t\t\tfinal int index = MotionEventCompat.findPointerIndex(ev,\n\t\t\t\t\t\tmActivePointerId);\n\t\t\t\tfinal float x = MotionEventCompat.getX(ev, index);\n\t\t\t\tfinal float y = MotionEventCompat.getY(ev, index);\n\t\t\t\tfinal int idx = (int) (x - mLastMotionX[mActivePointerId]);\n\t\t\t\tfinal int idy = (int) (y - mLastMotionY[mActivePointerId]);\n\n\t\t\t\tdragTo(mCapturedView.getLeft() + idx, mCapturedView.getTop()\n\t\t\t\t\t\t+ idy, idx, idy);\n\n\t\t\t\tsaveLastMotion(ev);\n\t\t\t} else {\n\t\t\t\t// Check to see if any pointer is now over a draggable view.\n\t\t\t\tfinal int pointerCount = MotionEventCompat.getPointerCount(ev);\n\t\t\t\tfor (int i = 0; i < pointerCount; i++) {\n\t\t\t\t\tfinal int pointerId = MotionEventCompat.getPointerId(ev, i);\n\t\t\t\t\tfinal float x = MotionEventCompat.getX(ev, i);\n\t\t\t\t\tfinal float y = MotionEventCompat.getY(ev, i);\n\t\t\t\t\tfinal float dx = x - mInitialMotionX[pointerId];\n\t\t\t\t\tfinal float dy = y - mInitialMotionY[pointerId];\n\n\t\t\t\t\treportNewEdgeDrags(dx, dy, pointerId);\n\t\t\t\t\tif (mDragState == STATE_DRAGGING) {\n\t\t\t\t\t\t// Callback might have started an edge drag.\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tfinal View toCapture = findTopChildUnder((int) x, (int) y);\n\t\t\t\t\tif (checkTouchSlop(toCapture, dx, dy)\n\t\t\t\t\t\t\t&& tryCaptureViewForDrag(toCapture, pointerId)) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tsaveLastMotion(ev);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase MotionEventCompat.ACTION_POINTER_UP: {\n\t\t\tfinal int pointerId = MotionEventCompat.getPointerId(ev,\n\t\t\t\t\tactionIndex);\n\t\t\tif (mDragState == STATE_DRAGGING && pointerId == mActivePointerId) {\n\t\t\t\t// Try to find another pointer that's still holding on to\n\t\t\t\t// the captured view.\n\t\t\t\tint newActivePointer = INVALID_POINTER;\n\t\t\t\tfinal int pointerCount = MotionEventCompat.getPointerCount(ev);\n\t\t\t\tfor (int i = 0; i < pointerCount; i++) {\n\t\t\t\t\tfinal int id = MotionEventCompat.getPointerId(ev, i);\n\t\t\t\t\tif (id == mActivePointerId) {\n\t\t\t\t\t\t// This one's going away, skip.\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tfinal float x = MotionEventCompat.getX(ev, i);\n\t\t\t\t\tfinal float y = MotionEventCompat.getY(ev, i);\n\t\t\t\t\tif (findTopChildUnder((int) x, (int) y) == mCapturedView\n\t\t\t\t\t\t\t&& tryCaptureViewForDrag(mCapturedView, id)) {\n\t\t\t\t\t\tnewActivePointer = mActivePointerId;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (newActivePointer == INVALID_POINTER) {\n\t\t\t\t\t// We didn't find another pointer still touching the\n\t\t\t\t\t// view, release it.\n\t\t\t\t\treleaseViewForPointerUp();\n\t\t\t\t}\n\t\t\t}\n\t\t\tclearMotionHistory(pointerId);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase MotionEvent.ACTION_UP: {\n\t\t\tif (mDragState == STATE_DRAGGING) {\n\t\t\t\treleaseViewForPointerUp();\n\t\t\t}\n\t\t\tcancel();\n\t\t\tbreak;\n\t\t}\n\n\t\tcase MotionEvent.ACTION_CANCEL: {\n\t\t\tif (mDragState == STATE_DRAGGING) {\n\t\t\t\tdispatchViewReleased(0, 0);\n\t\t\t}\n\t\t\tcancel();\n\t\t\tbreak;\n\t\t}\n\t\t}\n\t}\n\n\tprivate void reportNewEdgeDrags(float dx, float dy, int pointerId) {\n\t\tint dragsStarted = 0;\n\t\tif (checkNewEdgeDrag(dx, dy, pointerId, EDGE_LEFT)) {\n\t\t\tdragsStarted |= EDGE_LEFT;\n\t\t}\n\t\tif (checkNewEdgeDrag(dy, dx, pointerId, EDGE_TOP)) {\n\t\t\tdragsStarted |= EDGE_TOP;\n\t\t}\n\t\tif (checkNewEdgeDrag(dx, dy, pointerId, EDGE_RIGHT)) {\n\t\t\tdragsStarted |= EDGE_RIGHT;\n\t\t}\n\t\tif (checkNewEdgeDrag(dy, dx, pointerId, EDGE_BOTTOM)) {\n\t\t\tdragsStarted |= EDGE_BOTTOM;\n\t\t}\n\n\t\tif (dragsStarted != 0) {\n\t\t\tmEdgeDragsInProgress[pointerId] |= dragsStarted;\n\t\t\tmCallback.onEdgeDragStarted(dragsStarted, pointerId);\n\t\t}\n\t}\n\n\tprivate boolean checkNewEdgeDrag(float delta, float odelta, int pointerId,\n\t\t\tint edge) {\n\t\tfinal float absDelta = Math.abs(delta);\n\t\tfinal float absODelta = Math.abs(odelta);\n\n\t\tif ((mInitialEdgesTouched[pointerId] & edge) != edge\n\t\t\t\t|| (mTrackingEdges & edge) == 0\n\t\t\t\t|| (mEdgeDragsLocked[pointerId] & edge) == edge\n\t\t\t\t|| (mEdgeDragsInProgress[pointerId] & edge) == edge\n\t\t\t\t|| (absDelta <= mTouchSlop && absODelta <= mTouchSlop)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (absDelta < absODelta * 0.5f && mCallback.onEdgeLock(edge)) {\n\t\t\tmEdgeDragsLocked[pointerId] |= edge;\n\t\t\treturn false;\n\t\t}\n\t\treturn (mEdgeDragsInProgress[pointerId] & edge) == 0\n\t\t\t\t&& absDelta > mTouchSlop;\n\t}\n\n\t/**\n\t * Check if we've crossed a reasonable touch slop for the given child view.\n\t * If the child cannot be dragged along the horizontal or vertical axis,\n\t * motion along that axis will not count toward the slop check.\n\t * \n\t * @param child\n\t *            Child to check\n\t * @param dx\n\t *            Motion since initial position along X axis\n\t * @param dy\n\t *            Motion since initial position along Y axis\n\t * @return true if the touch slop has been crossed\n\t */\n\tprivate boolean checkTouchSlop(View child, float dx, float dy) {\n\t\tif (child == null) {\n\t\t\treturn false;\n\t\t}\n\t\tfinal boolean checkHorizontal = mCallback\n\t\t\t\t.getViewHorizontalDragRange(child) > 0;\n\t\tfinal boolean checkVertical = mCallback.getViewVerticalDragRange(child) > 0;\n\n\t\tif (checkHorizontal && checkVertical) {\n\t\t\treturn dx * dx + dy * dy > mTouchSlop * mTouchSlop;\n\t\t} else if (checkHorizontal) {\n\t\t\treturn Math.abs(dx) > mTouchSlop;\n\t\t} else if (checkVertical) {\n\t\t\treturn Math.abs(dy) > mTouchSlop;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Check if any pointer tracked in the current gesture has crossed the\n\t * required slop threshold.\n\t * <p>\n\t * This depends on internal state populated by\n\t * {@link #shouldInterceptTouchEvent(android.view.MotionEvent)} or\n\t * {@link #processTouchEvent(android.view.MotionEvent)}. You should only\n\t * rely on the results of this method after all currently available touch\n\t * data has been provided to one of these two methods.\n\t * </p>\n\t * \n\t * @param directions\n\t *            Combination of direction flags, see\n\t *            {@link #DIRECTION_HORIZONTAL}, {@link #DIRECTION_VERTICAL},\n\t *            {@link #DIRECTION_ALL}\n\t * @return true if the slop threshold has been crossed, false otherwise\n\t */\n\tpublic boolean checkTouchSlop(int directions) {\n\t\tfinal int count = mInitialMotionX.length;\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tif (checkTouchSlop(directions, i)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Check if the specified pointer tracked in the current gesture has crossed\n\t * the required slop threshold.\n\t * <p>\n\t * This depends on internal state populated by\n\t * {@link #shouldInterceptTouchEvent(android.view.MotionEvent)} or\n\t * {@link #processTouchEvent(android.view.MotionEvent)}. You should only\n\t * rely on the results of this method after all currently available touch\n\t * data has been provided to one of these two methods.\n\t * </p>\n\t * \n\t * @param directions\n\t *            Combination of direction flags, see\n\t *            {@link #DIRECTION_HORIZONTAL}, {@link #DIRECTION_VERTICAL},\n\t *            {@link #DIRECTION_ALL}\n\t * @param pointerId\n\t *            ID of the pointer to slop check as specified by MotionEvent\n\t * @return true if the slop threshold has been crossed, false otherwise\n\t */\n\tpublic boolean checkTouchSlop(int directions, int pointerId) {\n\t\tif (!isPointerDown(pointerId)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfinal boolean checkHorizontal = (directions & DIRECTION_HORIZONTAL) == DIRECTION_HORIZONTAL;\n\t\tfinal boolean checkVertical = (directions & DIRECTION_VERTICAL) == DIRECTION_VERTICAL;\n\n\t\tfinal float dx = mLastMotionX[pointerId] - mInitialMotionX[pointerId];\n\t\tfinal float dy = mLastMotionY[pointerId] - mInitialMotionY[pointerId];\n\n\t\tif (checkHorizontal && checkVertical) {\n\t\t\treturn dx * dx + dy * dy > mTouchSlop * mTouchSlop;\n\t\t} else if (checkHorizontal) {\n\t\t\treturn Math.abs(dx) > mTouchSlop;\n\t\t} else if (checkVertical) {\n\t\t\treturn Math.abs(dy) > mTouchSlop;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Check if any of the edges specified were initially touched in the\n\t * currently active gesture. If there is no currently active gesture this\n\t * method will return false.\n\t * \n\t * @param edges\n\t *            Edges to check for an initial edge touch. See\n\t *            {@link #EDGE_LEFT}, {@link #EDGE_TOP}, {@link #EDGE_RIGHT},\n\t *            {@link #EDGE_BOTTOM} and {@link #EDGE_ALL}\n\t * @return true if any of the edges specified were initially touched in the\n\t *         current gesture\n\t */\n\tpublic boolean isEdgeTouched(int edges) {\n\t\tfinal int count = mInitialEdgesTouched.length;\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tif (isEdgeTouched(edges, i)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Check if any of the edges specified were initially touched by the pointer\n\t * with the specified ID. If there is no currently active gesture or if\n\t * there is no pointer with the given ID currently down this method will\n\t * return false.\n\t * \n\t * @param edges\n\t *            Edges to check for an initial edge touch. See\n\t *            {@link #EDGE_LEFT}, {@link #EDGE_TOP}, {@link #EDGE_RIGHT},\n\t *            {@link #EDGE_BOTTOM} and {@link #EDGE_ALL}\n\t * @return true if any of the edges specified were initially touched in the\n\t *         current gesture\n\t */\n\tpublic boolean isEdgeTouched(int edges, int pointerId) {\n\t\treturn isPointerDown(pointerId)\n\t\t\t\t&& (mInitialEdgesTouched[pointerId] & edges) != 0;\n\t}\n\n\tprivate void releaseViewForPointerUp() {\n\t\tmVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);\n\t\tfinal float xvel = clampMag(VelocityTrackerCompat.getXVelocity(\n\t\t\t\tmVelocityTracker, mActivePointerId), mMinVelocity, mMaxVelocity);\n\t\tfinal float yvel = clampMag(VelocityTrackerCompat.getYVelocity(\n\t\t\t\tmVelocityTracker, mActivePointerId), mMinVelocity, mMaxVelocity);\n\t\tdispatchViewReleased(xvel, yvel);\n\t}\n\n\tprivate void dragTo(int left, int top, int dx, int dy) {\n\t\tint clampedX = left;\n\t\tint clampedY = top;\n\t\tfinal int oldLeft = mCapturedView.getLeft();\n\t\tfinal int oldTop = mCapturedView.getTop();\n\t\tif (dx != 0) {\n\t\t\tclampedX = mCallback.clampViewPositionHorizontal(mCapturedView,\n\t\t\t\t\tleft, dx);\n\t\t\tmCapturedView.offsetLeftAndRight(clampedX - oldLeft);\n\t\t}\n\t\tif (dy != 0) {\n\t\t\tclampedY = mCallback.clampViewPositionVertical(mCapturedView, top,\n\t\t\t\t\tdy);\n\t\t\tmCapturedView.offsetTopAndBottom(clampedY - oldTop);\n\t\t}\n\n\t\tif (dx != 0 || dy != 0) {\n\t\t\tfinal int clampedDx = clampedX - oldLeft;\n\t\t\tfinal int clampedDy = clampedY - oldTop;\n\t\t\tmCallback.onViewPositionChanged(mCapturedView, clampedX, clampedY,\n\t\t\t\t\tclampedDx, clampedDy);\n\t\t}\n\t}\n\n\t/**\n\t * Determine if the currently captured view is under the given point in the\n\t * parent view's coordinate system. If there is no captured view this method\n\t * will return false.\n\t * \n\t * @param x\n\t *            X position to test in the parent's coordinate system\n\t * @param y\n\t *            Y position to test in the parent's coordinate system\n\t * @return true if the captured view is under the given point, false\n\t *         otherwise\n\t */\n\tpublic boolean isCapturedViewUnder(int x, int y) {\n\t\treturn isViewUnder(mCapturedView, x, y);\n\t}\n\n\t/**\n\t * Determine if the supplied view is under the given point in the parent\n\t * view's coordinate system.\n\t * \n\t * @param view\n\t *            Child view of the parent to hit test\n\t * @param x\n\t *            X position to test in the parent's coordinate system\n\t * @param y\n\t *            Y position to test in the parent's coordinate system\n\t * @return true if the supplied view is under the given point, false\n\t *         otherwise\n\t */\n\tpublic boolean isViewUnder(View view, int x, int y) {\n\t\tif (view == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn x >= view.getLeft() && x < view.getRight() && y >= view.getTop()\n\t\t\t\t&& y < view.getBottom();\n\t}\n\n\t/**\n\t * Find the topmost child under the given point within the parent view's\n\t * coordinate system. The child order is determined using\n\t * {@link com.way.ui.swipeback.ViewDragHelper.Callback#getOrderedChildIndex(int)}\n\t * .\n\t * \n\t * @param x\n\t *            X position to test in the parent's coordinate system\n\t * @param y\n\t *            Y position to test in the parent's coordinate system\n\t * @return The topmost child view under (x, y) or null if none found.\n\t */\n\tpublic View findTopChildUnder(int x, int y) {\n\t\tfinal int childCount = mParentView.getChildCount();\n\t\tfor (int i = childCount - 1; i >= 0; i--) {\n\t\t\tfinal View child = mParentView.getChildAt(mCallback\n\t\t\t\t\t.getOrderedChildIndex(i));\n\t\t\tif (x >= child.getLeft() && x < child.getRight()\n\t\t\t\t\t&& y >= child.getTop() && y < child.getBottom()) {\n\t\t\t\treturn child;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate int getEdgesTouched(int x, int y) {\n\t\tint result = 0;\n\n\t\tif (x < mParentView.getLeft() + mEdgeSize)\n\t\t\tresult |= EDGE_LEFT;\n\t\tif (y < mParentView.getTop() + mEdgeSize)\n\t\t\tresult |= EDGE_TOP;\n\t\tif (x > mParentView.getRight() - mEdgeSize)\n\t\t\tresult |= EDGE_RIGHT;\n\t\tif (y > mParentView.getBottom() - mEdgeSize)\n\t\t\tresult |= EDGE_BOTTOM;\n\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/ui/view/CountDownView.java",
    "content": "package com.way.ui.view;\n\nimport java.util.Locale;\n\nimport android.content.Context;\nimport android.os.Handler;\nimport android.os.Message;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.animation.Animation;\nimport android.view.animation.AnimationUtils;\nimport android.widget.FrameLayout;\nimport android.widget.TextView;\n\nimport com.way.common.util.L;\nimport com.way.yahoo.R;\n\npublic class CountDownView extends FrameLayout {\n\n\tprivate static final String TAG = \"CAM_CountDownView\";\n\tprivate static final int SET_TIMER_TEXT = 1;\n\tprivate TextView mRemainingSecondsView;\n\tprivate int mRemainingSecs = 0;\n\tprivate OnCountDownFinishedListener mListener;\n\tprivate Animation mCountDownAnim;\n//\tprivate SoundPool mSoundPool;\n//\tprivate int mBeepTwice;\n//\tprivate int mBeepOnce;\n//\tprivate boolean mPlaySound;\n\tprivate final Handler mHandler = new MainHandler();\n\n\tpublic CountDownView(Context context, AttributeSet attrs) {\n\t\tsuper(context, attrs);\n\t\tmCountDownAnim = AnimationUtils.loadAnimation(context,\n\t\t\t\tR.anim.count_down_exit);\n\t\t// Load the beeps\n//\t\tmSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);\n//\t\tmBeepOnce = mSoundPool.load(context, R.raw.beep_once, 1);\n//\t\tmBeepTwice = mSoundPool.load(context, R.raw.beep_twice, 1);\n\t}\n\n\tpublic boolean isCountingDown() {\n\t\treturn mRemainingSecs > 0;\n\t};\n\n\tpublic interface OnCountDownFinishedListener {\n\t\tpublic void onCountDownFinished();\n\t}\n\n\tprivate void remainingSecondsChanged(int newVal) {\n\t\tmRemainingSecs = newVal;\n\t\tif (newVal == 0) {\n\t\t\t// Countdown has finished\n\t\t\tsetVisibility(View.INVISIBLE);\n\t\t\tif(mListener != null)\n\t\t\tmListener.onCountDownFinished();\n\t\t} else {\n\t\t\tLocale locale = getResources().getConfiguration().locale;\n\t\t\tString localizedValue = String.format(locale, \"%d\", newVal);\n\t\t\tmRemainingSecondsView.setText(localizedValue);\n\t\t\t// Fade-out animation\n\t\t\tmCountDownAnim.reset();\n\t\t\tmRemainingSecondsView.clearAnimation();\n\t\t\tmRemainingSecondsView.startAnimation(mCountDownAnim);\n\n\t\t\t// Play sound effect for the last 3 seconds of the countdown\n//\t\t\tif (mPlaySound) {\n//\t\t\t\tif (newVal == 1) {\n//\t\t\t\t\tmSoundPool.play(mBeepTwice, 1.0f, 1.0f, 0, 0, 1.0f);\n//\t\t\t\t} else if (newVal <= 3) {\n//\t\t\t\t\tmSoundPool.play(mBeepOnce, 1.0f, 1.0f, 0, 0, 1.0f);\n//\t\t\t\t}\n//\t\t\t}\n\t\t\t// Schedule the next remainingSecondsChanged() call in 1 second\n\t\t\tmHandler.sendEmptyMessageDelayed(SET_TIMER_TEXT, 1000);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void onFinishInflate() {\n\t\tsuper.onFinishInflate();\n\t\tmRemainingSecondsView = (TextView) findViewById(R.id.remaining_seconds);\n\t}\n\n\tpublic void setCountDownFinishedListener(\n\t\t\tOnCountDownFinishedListener listener) {\n\t\tmListener = listener;\n\t}\n\n\tpublic void startCountDown(int sec, boolean playSound) {\n\t\tif (sec <= 0) {\n\t\t\tL.d(TAG, \"Invalid input for countdown timer: \" + sec + \" seconds\");\n\t\t\treturn;\n\t\t}\n\t\tsetVisibility(View.VISIBLE);\n//\t\tmPlaySound = playSound;\n\t\tremainingSecondsChanged(sec);\n\t}\n\tpublic void startCountDown(int sec) {\n\t\tif (sec <= 0) {\n\t\t\tL.i(TAG, \"Invalid input for countdown timer: \" + sec + \" seconds\");\n\t\t\treturn;\n\t\t}\n\t\tsetVisibility(View.VISIBLE);\n\t\tremainingSecondsChanged(sec);\n\t}\n\n\tpublic void cancelCountDown() {\n\t\tif (mRemainingSecs > 0) {\n\t\t\tmRemainingSecs = 0;\n\t\t\tmHandler.removeMessages(SET_TIMER_TEXT);\n\t\t\tsetVisibility(View.INVISIBLE);\n\t\t}\n\t}\n\n\tprivate class MainHandler extends Handler {\n\t\t@Override\n\t\tpublic void handleMessage(Message message) {\n\t\t\tif (message.what == SET_TIMER_TEXT) {\n\t\t\t\tremainingSecondsChanged(mRemainingSecs - 1);\n\t\t\t}\n\t\t}\n\t}\n}"
  },
  {
    "path": "WayHoo/src/com/way/ui/view/DragSortGridView.java",
    "content": "package com.way.ui.view;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.graphics.Rect;\nimport android.util.AttributeSet;\nimport android.util.SparseArray;\nimport android.view.DragEvent;\nimport android.view.View;\nimport android.view.animation.Animation;\nimport android.view.animation.AnimationUtils;\nimport android.view.animation.TranslateAnimation;\nimport android.widget.AdapterView;\nimport android.widget.GridView;\n\nimport com.way.common.util.L;\nimport com.way.yahoo.R;\n\n@SuppressLint(\"NewApi\")\npublic class DragSortGridView extends GridView {\n\n\tprivate static final String TAG = \"drag-sort-gridview\";\n\tprivate static final boolean DEBUG_LOG = false;\n\n\tpublic DragSortGridView(Context context) {\n\t\tthis(context, null);\n\t}\n\n\tpublic DragSortGridView(Context context, AttributeSet attrs) {\n\t\tthis(context, attrs, 0);\n\t}\n\n\tpublic DragSortGridView(Context context, AttributeSet attrs, int defStyle) {\n\t\tsuper(context, attrs, defStyle);\n\n\t\tsetOnItemLongClickListener(itemLongClickListener);\n\t\tsetOnDragListener(dragListener);\n\t}\n\n\t/**\n\t * Only user of this view know how to update content data to change their\n\t * order. So they need this callback.\n\t */\n\tpublic interface OnReorderingListener {\n\t\tpublic void beginRecordering(AdapterView<?> parent, View view,\n\t\t\t\tint position, long id);\n\n\t\tpublic void onReordering(int fromPosition, int toPosition);\n\t}\n\n\tpublic void setOnReorderingListener(OnReorderingListener listener) {\n\t\tthis.onReorderingListener = listener;\n\t}\n\n\tprotected OnReorderingListener onReorderingListener;\n\n\tprotected OnItemLongClickListener itemLongClickListener = new OnItemLongClickListener() {\n\n\t\t@Override\n\t\tpublic boolean onItemLongClick(AdapterView<?> parent, View view,\n\t\t\t\tint position, long id) {\n\t\t\tview.startDrag(null, new View.DragShadowBuilder(view), position, 0);\n\t\t\tif (onReorderingListener != null)\n\t\t\t\tonReorderingListener.beginRecordering(parent, view, position,\n\t\t\t\t\t\tid);\n\t\t\treturn true;\n\t\t}\n\n\t};\n\n\t/**\n\t * Between {@link DragEvent.ACTION_DRAG_STARTED} and\n\t * {@link DragEvent.ACTION_DRAG_ENDED}, grid view children are moving around\n\t * and lost their original positions. So we need a method to keep track of\n\t * the changing positions.\n\t */\n\tprivate class IntegerArray {\n\t\tprivate int[] data;\n\n\t\tpublic IntegerArray(int size) {\n\t\t\tint[] _data = new int[size];\n\t\t\tfor (int i = 0; i < size; ++i) {\n\t\t\t\t_data[i] = i;\n\t\t\t}\n\t\t\tthis.data = _data;\n\t\t}\n\n\t\tpublic int get(int position) {\n\t\t\treturn data[position];\n\t\t}\n\n\t\t/**\n\t\t * Move array value from one position to another.\n\t\t * \n\t\t * Example: [0, 1, 2, 3, 4], move from 1 to 3 results: [0, 2, 3, 1, 4].\n\t\t * \n\t\t * Another: [0, 1, 2, 3, 4], move from 3 to 1 results: [0, 3, 1, 2, 4].\n\t\t */\n\t\tpublic void reorder(int from, int to) {\n\t\t\tif (from == to) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfinal int[] array = data;\n\n\t\t\tif (from < to) {\n\t\t\t\tint fromValue = array[from];\n\t\t\t\tfor (int i = from; i < to; ++i) {\n\t\t\t\t\tarray[i] = array[i + 1];\n\t\t\t\t}\n\t\t\t\tarray[to] = fromValue;\n\t\t\t} else {\n\t\t\t\tint fromValue = array[from];\n\t\t\t\tfor (int i = from; i > to; --i) {\n\t\t\t\t\tarray[i] = array[i - 1];\n\t\t\t\t}\n\t\t\t\tarray[to] = fromValue;\n\t\t\t}\n\t\t}\n\n\t\tpublic int getValueIndex(int value) {\n\t\t\tfinal int[] array = data;\n\t\t\tfinal int size = array.length;\n\n\t\t\tfor (int i = 0; i < size; ++i) {\n\t\t\t\tif (value == array[i]) {\n\t\t\t\t\treturn i;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn -1;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tStringBuilder builder = new StringBuilder();\n\t\t\tbuilder.append(\"[\");\n\n\t\t\tfinal int[] array = data;\n\t\t\tfinal int size = array.length;\n\t\t\tfor (int i = 0; i < size; i++) {\n\t\t\t\tbuilder.append(Integer.toString(array[i])).append(\",\");\n\t\t\t}\n\t\t\tbuilder.append(\"]\");\n\t\t\treturn builder.toString();\n\t\t}\n\t}\n\n\tprotected IntegerArray reorderingPositions;\n\n\tprotected int lastDraggingPosition;\n\tprotected boolean movingChildViews = false;\n\tprivate Animation fadeOutAnimation;\n\n\tprivate View.OnDragListener dragListener = new View.OnDragListener() {\n\n\t\t@Override\n\t\tpublic boolean onDrag(View v, DragEvent event) {\n\t\t\tfinal int action = event.getAction();\n\t\t\tfinal int x = Math.round(event.getX());\n\t\t\tfinal int y = Math.round(event.getY());\n\t\t\tif (DEBUG_LOG) {\n\t\t\t\tL.d(TAG,\n\t\t\t\t\t\t\"onDrag event action:\" + DRAG_EVENT_ACTION.get(action)\n\t\t\t\t\t\t\t\t+ \",x:\" + x + \",y:\" + y);\n\t\t\t}\n\n\t\t\tswitch (action) {\n\t\t\tcase DragEvent.ACTION_DRAG_STARTED:\n\t\t\t\t// notice: init array with adapter item count, NOT child view\n\t\t\t\t// count.\n\t\t\t\treorderingPositions = new IntegerArray(getAdapter().getCount());\n\n\t\t\t\tlastDraggingPosition = (Integer) event.getLocalState();\n\n\t\t\t\tif (fadeOutAnimation == null) {\n\t\t\t\t\tfadeOutAnimation = AnimationUtils.loadAnimation(\n\t\t\t\t\t\t\tgetContext(), R.anim.fade_out);\n\t\t\t\t}\n\t\t\t\tgetView(lastDraggingPosition).startAnimation(fadeOutAnimation);\n\t\t\t\tbreak;\n\t\t\tcase DragEvent.ACTION_DRAG_ENTERED:\n\t\t\t\tbreak;\n\t\t\tcase DragEvent.ACTION_DRAG_LOCATION: {\n\t\t\t\tif (movingChildViews)\n\t\t\t\t\tbreak;\n\n\t\t\t\t// Watch out: pointToPosition ignores invisible views.\n\t\t\t\tfinal int pos = pointToPosition(x, y);\n\t\t\t\tfinal int newPosition = pos == -1 ? -1 : reorderingPositions\n\t\t\t\t\t\t.getValueIndex(pos);\n\n\t\t\t\tif (-1 != newPosition && lastDraggingPosition != newPosition) {\n\t\t\t\t\tif (DEBUG_LOG) {\n\t\t\t\t\t\tL.d(TAG, \"ACTION_DRAG_LOCATION lastPosition:\"\n\t\t\t\t\t\t\t\t+ lastDraggingPosition + \",newPosition:\"\n\t\t\t\t\t\t\t\t+ newPosition);\n\t\t\t\t\t}\n\n\t\t\t\t\treorderViews(lastDraggingPosition, newPosition);\n\t\t\t\t\treorderingPositions.reorder(lastDraggingPosition,\n\t\t\t\t\t\t\tnewPosition);\n\t\t\t\t\tlastDraggingPosition = newPosition;\n\n\t\t\t\t\tif (DEBUG_LOG) {\n\t\t\t\t\t\tL.d(TAG, \"reordering positions:\"\n\t\t\t\t\t\t\t\t+ reorderingPositions);\n\t\t\t\t\t}\n\n\t\t\t\t\tmovingChildViews = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase DragEvent.ACTION_DRAG_EXITED:\n\t\t\t\tbreak;\n\t\t\tcase DragEvent.ACTION_DROP: {\n\t\t\t\tfinal int pos = pointToPosition(x, y);\n\t\t\t\tfinal int newPosition = pos == -1 ? -1 : reorderingPositions\n\t\t\t\t\t\t.getValueIndex(pos);\n\n\t\t\t\tif (DEBUG_LOG) {\n\t\t\t\t\tL.d(TAG, \"ACTION_DROP pos:\" + pos + \",newPosition:\"\n\t\t\t\t\t\t\t+ newPosition);\n\t\t\t\t}\n\n\t\t\t\tgetView(lastDraggingPosition).clearAnimation();\n\n\t\t\t\tif (onReorderingListener != null) {\n\t\t\t\t\tfinal int oldPosition = (Integer) event.getLocalState();\n\t\t\t\t\tif (newPosition != -1 && newPosition != oldPosition) {\n\t\t\t\t\t\tonReorderingListener.onReordering(oldPosition,\n\t\t\t\t\t\t\t\tnewPosition);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase DragEvent.ACTION_DRAG_ENDED:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t};\n\n\t/**\n\t * Move child view from one position to another.\n\t * \n\t * Example: [0, 1, 2, 3, 4], move from 1 to 3 results: [0, 2, 3, 1, 4].\n\t * \n\t * Another: [0, 1, 2, 3, 4], move from 3 to 1 results: [0, 3, 1, 2, 4].\n\t */\n\tprotected void reorderViews(int fromPosition, int toPosition) {\n\t\tif (DEBUG_LOG) {\n\t\t\tL.d(TAG, \"reorderViews old:\" + fromPosition + \",new:\"\n\t\t\t\t\t+ toPosition);\n\t\t}\n\n\t\tif (fromPosition == toPosition) {\n\t\t\treturn;\n\t\t}\n\n\t\tfinal View fromChild = getView(fromPosition);\n\t\tfinal View toChild = getView(toPosition);\n\n\t\tRect toLayout = new Rect();\n\n\t\tif (toPosition < fromPosition) {\n\t\t\t// preserve view layout on new position, for it will be changed on\n\t\t\t// next step.\n\t\t\tgetLayout(toChild, toLayout);\n\n\t\t\tfor (int i = toPosition; i < fromPosition; ++i) {\n\t\t\t\tmoveView(i, i + 1);\n\t\t\t}\n\n\t\t\tfromChild.layout(toLayout.left, toLayout.top, toLayout.right,\n\t\t\t\t\ttoLayout.bottom);\n\t\t} else {\n\t\t\t// preserve view layout on new position, for it will be changed on\n\t\t\t// next step.\n\t\t\tgetLayout(toChild, toLayout);\n\n\t\t\tfor (int i = toPosition; i > fromPosition; --i) {\n\t\t\t\tmoveView(i, i - 1);\n\t\t\t}\n\n\t\t\tfromChild.layout(toLayout.left, toLayout.top, toLayout.right,\n\t\t\t\t\ttoLayout.bottom);\n\t\t}\n\t}\n\n\tprivate void moveView(int fromPosition, int toPosition) {\n\t\tif (DEBUG_LOG) {\n\t\t\tL.d(TAG, \"moveView from:\" + fromPosition + \",to:\" + toPosition);\n\t\t}\n\n\t\tfinal View from = getView(fromPosition);\n\t\tfinal View to = getView(toPosition);\n\n\t\tfinal Rect fromRect = new Rect();\n\t\tgetLayout(from, fromRect);\n\t\tfinal Rect toRect = new Rect();\n\t\tgetLayout(to, toRect);\n\n\t\tAnimation translate = new TranslateAnimation(0, toRect.left\n\t\t\t\t- fromRect.left, 0, toRect.top - fromRect.top);\n\t\ttranslate.setDuration(150);\n\t\ttranslate.setFillEnabled(true);\n\t\ttranslate.setFillBefore(true);\n\t\ttranslate.setFillAfter(true);\n\t\ttranslate.setAnimationListener(new MoveViewAnimationListener(from, to\n\t\t\t\t.getLeft(), to.getTop()));\n\n\t\tfrom.startAnimation(translate);\n\t}\n\n\tprivate class MoveViewAnimationListener implements\n\t\t\tAnimation.AnimationListener {\n\n\t\tprivate View target;\n\t\tprivate int newX, newY;\n\n\t\tpublic MoveViewAnimationListener(View target, int newX, int newY) {\n\t\t\tthis.target = target;\n\t\t\tthis.newX = newX;\n\t\t\tthis.newY = newY;\n\t\t}\n\n\t\t@Override\n\t\tpublic void onAnimationEnd(Animation animation) {\n\t\t\ttarget.layout(newX, newY, newX + target.getWidth(),\n\t\t\t\t\tnewY + target.getHeight());\n\t\t\ttarget.clearAnimation();\n\t\t\tmovingChildViews = false;\n\t\t}\n\n\t\t@Override\n\t\tpublic void onAnimationRepeat(Animation animation) {\n\t\t}\n\n\t\t@Override\n\t\tpublic void onAnimationStart(Animation animation) {\n\t\t}\n\n\t}\n\n\tprivate void getLayout(View view, Rect rect) {\n\t\trect.set(view.getLeft(), view.getTop(), view.getRight(),\n\t\t\t\tview.getBottom());\n\t}\n\n\tprivate View getView(int reorderingPosition) {\n\t\tint orgPosition = reorderingPositions.get(reorderingPosition);\n\t\treturn getChildAt(orgPosition - getFirstVisiblePosition());\n\t}\n\n\tprivate static final SparseArray<String> DRAG_EVENT_ACTION = DEBUG_LOG ? new SparseArray<String>()\n\t\t\t: null;\n\tstatic {\n\t\tif (DEBUG_LOG) {\n\t\t\tDRAG_EVENT_ACTION.put(DragEvent.ACTION_DRAG_STARTED,\n\t\t\t\t\t\"ACTION_DRAG_STARTED\");\n\t\t\tDRAG_EVENT_ACTION.put(DragEvent.ACTION_DRAG_ENTERED,\n\t\t\t\t\t\"ACTION_DRAG_ENTERED\");\n\t\t\tDRAG_EVENT_ACTION.put(DragEvent.ACTION_DRAG_LOCATION,\n\t\t\t\t\t\"ACTION_DRAG_LOCATION\");\n\t\t\tDRAG_EVENT_ACTION.put(DragEvent.ACTION_DRAG_EXITED,\n\t\t\t\t\t\"ACTION_DRAG_EXITED\");\n\t\t\tDRAG_EVENT_ACTION.put(DragEvent.ACTION_DROP, \"ACTION_DROP\");\n\t\t\tDRAG_EVENT_ACTION.put(DragEvent.ACTION_DRAG_ENDED,\n\t\t\t\t\t\"ACTION_DRAG_ENDED\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/ui/view/LoadingEmptyContainer.java",
    "content": "package com.way.ui.view;\n\nimport android.content.Context;\nimport android.os.Handler;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.widget.FrameLayout;\n\nimport com.way.yahoo.R;\n\n/**\n * This class is the default empty state view for most listviews/fragments\n * It allows the ability to set a main text, a main highlight text and a secondary text\n * By default this container has some strings loaded, but other classes can call the apis to change\n * the text\n */\npublic class LoadingEmptyContainer extends FrameLayout {\n    private static final int LOADING_DELAY = 300;\n\n    private Handler mHandler;\n    private Runnable mShowLoadingRunnable;\n\n    public LoadingEmptyContainer(Context context, AttributeSet attrs) {\n        super(context, attrs);\n\n        mHandler = new Handler();\n        mShowLoadingRunnable = new Runnable() {\n            @Override\n            public void run() {\n                findViewById(R.id.progressbar).setVisibility(View.VISIBLE);\n                getNoResultsContainer().setVisibility(View.INVISIBLE);\n            }\n        };\n    }\n\n    @Override\n    protected void onFinishInflate() {\n        super.onFinishInflate();\n\n        hideAll();\n    }\n\n    public void hideAll() {\n        findViewById(R.id.progressbar).setVisibility(View.INVISIBLE);\n        getNoResultsContainer().setVisibility(View.INVISIBLE);\n    }\n\n    public void showLoading() {\n        hideAll();\n\n        //if (!mHandler.hasCallbacks(mShowLoadingRunnable)) {\n        mHandler.removeCallbacks(mShowLoadingRunnable);\n            mHandler.postDelayed(mShowLoadingRunnable, LOADING_DELAY);\n        //}\n    }\n\n    public void showNoResults() {\n        mHandler.removeCallbacks(mShowLoadingRunnable);\n\n        findViewById(R.id.progressbar).setVisibility(View.INVISIBLE);\n        getNoResultsContainer().setVisibility(View.VISIBLE);\n    }\n\n    public NoResultsContainer getNoResultsContainer() {\n        return (NoResultsContainer)findViewById(R.id.no_results_container);\n    }\n}\n\n"
  },
  {
    "path": "WayHoo/src/com/way/ui/view/NoResultsContainer.java",
    "content": "package com.way.ui.view;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\n\nimport com.way.yahoo.R;\n\n/**\n * This class is the default empty state view for most listviews/fragments It\n * allows the ability to set a main text, a main highlight text and a secondary\n * text By default this container has some strings loaded, but other classes can\n * call the apis to change the text\n */\npublic class NoResultsContainer extends LinearLayout {\n\tpublic NoResultsContainer(Context context, AttributeSet attrs) {\n\t\tsuper(context, attrs);\n\t}\n\n\t/**\n\t * This changes the Main text (top-most text) of the empty container\n\t * \n\t * @param resId\n\t *            String resource id\n\t */\n\tpublic void setMainText(final int resId) {\n\t\t((TextView) findViewById(R.id.no_results_main_text)).setText(resId);\n\t}\n\n\tpublic void setTextColor(int color) {\n\t\t((TextView) findViewById(R.id.no_results_main_text))\n\t\t\t\t.setTextColor(color);\n\t}\n}"
  },
  {
    "path": "WayHoo/src/com/way/ui/view/TouchDispatchView.java",
    "content": "package com.way.ui.view;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.widget.RelativeLayout;\n\npublic class TouchDispatchView extends RelativeLayout {\n\tprivate boolean isInterceptTouches = true;\n\n\tpublic TouchDispatchView(Context context) {\n\t\tsuper(context);\n\t}\n\n\tpublic TouchDispatchView(Context context, AttributeSet attrs) {\n\t\tsuper(context, attrs);\n\t}\n\n\tpublic TouchDispatchView(Context context, AttributeSet attrs, int defStyle) {\n\t\tsuper(context, attrs, defStyle);\n\t}\n\n\tpublic boolean onInterceptTouchEvent(MotionEvent event) {\n\t\treturn isInterceptTouches;\n\t}\n\n\tpublic boolean onTouchEvent(MotionEvent event) {\n\t\tif (!isInterceptTouches)\n\t\t\treturn super.onTouchEvent(event);\n\t\tint count = getChildCount();\n\t\tif (count < 0)\n\t\t\treturn isInterceptTouches;\n\t\t\n\t\tfor (int i = 0; i < count; ++i) {\n\t\t\tView childView = getChildAt(i);\n\t\t\tfloat oldX = event.getX();\n\t\t\tfloat oldY = event.getY();\n\t\t\tfloat x = event.getX() - childView.getLeft();\n\t\t\tfloat y = event.getY() - childView.getTop();\n\t\t\tif (((y >= 0.0F) && (x >= 0.0F))\n\t\t\t\t\t|| ((MotionEvent.ACTION_MASK & event.getAction()) != MotionEvent.ACTION_DOWN)){\n\t\t\t\t//L.i(\"liweiping\", \"new touch --> x = \" + x +\", y = \" + y);\n\t\t\t\tevent.setLocation(x, y);\n\t\t\t}else{\n\t\t\t\t//L.i(\"liweiping\", \"old touch --> oldX = \" + oldX +\", oldY = \" + oldY);\n\t\t\t\tevent.setLocation(oldX, oldY);\n\t\t\t}\n\t\t\tchildView.dispatchTouchEvent(event);\n\t\t}\n\t\treturn isInterceptTouches;\n\t}\n\n\tpublic void setInterceptTouches(boolean isInterceptTouches) {\n\t\tthis.isInterceptTouches = isInterceptTouches;\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/ui/view/WeatherAqiView.java",
    "content": "package com.way.ui.view;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.ImageView;\nimport android.widget.RelativeLayout;\nimport android.widget.TextView;\n\nimport com.way.weather.plugin.bean.AQI;\nimport com.way.weather.plugin.bean.WeatherInfo;\nimport com.way.yahoo.R;\n\npublic class WeatherAqiView extends WeatherBaseView {\n\t// 空气质量\n\tImageView aqiIV;\n\tTextView aqiLevelTV;\n\tTextView aqiTV;\n\tTextView pm25TV;\n\tTextView aqiDescTV;\n\tTextView aqiFootTV;\n\n\tpublic WeatherAqiView(Context c) {\n\t\tthis(c, null);\n\t}\n\n\tpublic WeatherAqiView(Context context, AttributeSet attrs) {\n\t\tthis(context, attrs, 0);\n\t}\n\n\tpublic WeatherAqiView(Context context, AttributeSet attrs, int defStyleAttr) {\n\t\tsuper(context, attrs, defStyleAttr);\n\t}\n\n\t@Override\n\tprotected void onFinishInflate() {\n\t\tsuper.onFinishInflate();\n\t\t// 空气质量\n\t\taqiIV = (ImageView) findViewById(R.id.aqi_icon);\n\t\taqiLevelTV = (TextView) findViewById(R.id.aqi_level);\n\t\taqiTV = (TextView) findViewById(R.id.aqi);\n\t\tpm25TV = (TextView) findViewById(R.id.pm25);\n\t\taqiDescTV = (TextView) findViewById(R.id.aqi_desc);\n\t\taqiFootTV = (TextView) findViewById(R.id.weather_aqi_foot_tv);\n\t}\n\n\tpublic void setWeatherInfo(AQI aqi) {\n\t\tif (aqi == null || aqi.getAqi() < 0)\n\t\t\treturn;\n\t\taqiIV.setImageResource(getAqiIcon(aqi.getAqi()));\n\t\taqiLevelTV.setText(aqi.getAqi_level());\n\t\taqiTV.setText(aqi.getAqi() + \"μg/m³\");\n\t\tpm25TV.setText(aqi.getPm25() + \"μg/m³\");\n\t\taqiDescTV.setText(aqi.getAqi_desc());\n\t\taqiFootTV.setText(\"中国环境检测总站\");\n\t}\n\n\tprivate int getAqiIcon(int aqi) {\n\t\tint aqi_img = R.drawable.biz_plugin_weather_0_50;\n\t\tif (aqi > 300) {\n\t\t\taqi_img = R.drawable.biz_plugin_weather_greater_300;\n\t\t} else if (aqi > 200) {\n\t\t\taqi_img = R.drawable.biz_plugin_weather_201_300;\n\t\t} else if (aqi > 150) {\n\t\t\taqi_img = R.drawable.biz_plugin_weather_151_200;\n\t\t} else if (aqi > 100) {\n\t\t\taqi_img = R.drawable.biz_plugin_weather_101_150;\n\t\t} else if (aqi > 50) {\n\t\t\taqi_img = R.drawable.biz_plugin_weather_51_100;\n\t\t} else {\n\t\t\taqi_img = R.drawable.biz_plugin_weather_0_50;\n\t\t}\n\t\treturn aqi_img;\n\t}\n\n\t@Override\n\tpublic void setWeatherInfo(WeatherInfo weatherInfo) {\n\t\tsetWeatherInfo(weatherInfo.getAqi());\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/ui/view/WeatherBaseView.java",
    "content": "package com.way.ui.view;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.RelativeLayout;\n\nimport com.way.weather.plugin.bean.WeatherInfo;\n\npublic abstract class WeatherBaseView extends RelativeLayout {\n\tpublic WeatherBaseView(Context c) {\n\t\tthis(c, null);\n\t}\n\n\tpublic WeatherBaseView(Context context, AttributeSet attrs) {\n\t\tthis(context, attrs, 0);\n\t}\n\n\tpublic WeatherBaseView(Context context, AttributeSet attrs, int defStyleAttr) {\n\t\tsuper(context, attrs, defStyleAttr);\n\t}\n\tpublic abstract void setWeatherInfo(WeatherInfo weatherInfo);\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/ui/view/WeatherDetailsView.java",
    "content": "package com.way.ui.view;\n\nimport android.content.Context;\nimport android.text.format.Time;\nimport android.util.AttributeSet;\nimport android.widget.ImageView;\nimport android.widget.RelativeLayout;\nimport android.widget.TextView;\n\nimport com.way.common.util.LunarCalendar;\nimport com.way.common.util.LunarCalendarConvertUtil;\nimport com.way.common.util.WeatherIconUtils;\nimport com.way.weather.plugin.bean.RealTime;\nimport com.way.weather.plugin.bean.WeatherInfo;\nimport com.way.yahoo.R;\n\npublic class WeatherDetailsView extends WeatherBaseView {\n\t// 详细信息\n\tImageView detailsWeatherIV;\n\tTextView weatherNameTV;\n\tTextView feelsTempTV;\n\tTextView humidityTV;\n\tTextView windTV;\n\tTextView windDescTV;\n\tTextView detailsFootTV;\n\tprivate LunarCalendar mLunarCalendar;\n\n\tpublic WeatherDetailsView(Context c) {\n\t\tthis(c, null);\n\t}\n\n\tpublic WeatherDetailsView(Context context, AttributeSet attrs) {\n\t\tthis(context, attrs, 0);\n\t}\n\n\tpublic WeatherDetailsView(Context context, AttributeSet attrs,\n\t\t\tint defStyleAttr) {\n\t\tsuper(context, attrs, defStyleAttr);\n\t}\n\n\t@Override\n\tprotected void onFinishInflate() {\n\t\tsuper.onFinishInflate();\n\t\t// 详细信息\n\t\tdetailsWeatherIV = (ImageView) findViewById(R.id.details_icon);\n\t\tweatherNameTV = (TextView) findViewById(R.id.weather_name_tv);\n\t\tfeelsTempTV = (TextView) findViewById(R.id.feelsTemp_tv);\n\t\thumidityTV = (TextView) findViewById(R.id.humidity_tv);\n\t\twindTV = (TextView) findViewById(R.id.wind_tv);\n\t\twindDescTV = (TextView) findViewById(R.id.wind_desc);\n\t\tdetailsFootTV = (TextView) findViewById(R.id.weather_details_foot_tv);\n\t}\n\n\tpublic void setWeatherInfo(RealTime realTime) {\n\t\tif (realTime == null || realTime.getAnimation_type() < 0)\n\t\t\treturn;\n\t\t// 农历\n\t\tmLunarCalendar = new LunarCalendar(getContext());\n\t\tTime time = new Time();\n\t\ttime.set(System.currentTimeMillis());\n\t\tLunarCalendarConvertUtil.parseLunarCalendar(time.year, time.month,\n\t\t\t\ttime.monthDay, mLunarCalendar);\n\t\t// 绑定数据\n\t\tdetailsWeatherIV.setImageResource(WeatherIconUtils\n\t\t\t\t.getWeatherIcon(realTime.getAnimation_type()));\n\t\tweatherNameTV.setText(realTime.getWeather_name());\n\t\tfeelsTempTV.setText(realTime.getTemp() + \"°\");\n\t\thumidityTV.setText(realTime.getHumidity() + \"%\");\n\t\tString[] winds = realTime.getWind().split(\"，\");\n\t\tif (winds.length > 1) {\n\t\t\twindTV.setText(winds[1]);\n\t\t\twindDescTV.setText(winds[0]);\n\t\t} else {\n\t\t\twindTV.setText(realTime.getWind());\n\t\t}\n\t\t// detailsFootTV.setText(\"中国天气网\");\n\t\t// detailsFootTV.setText(mLunarCalendar.getLunarDayInfo());\n\t\tString str[] = mLunarCalendar.getLunarCalendarInfo(false);\n\t\tdetailsFootTV.setText(mLunarCalendar\n\t\t\t\t.getLunarYear(mLunarCalendar.lunarYear)\n\t\t\t\t+ \"(\"\n\t\t\t\t+ mLunarCalendar.animalsYear(mLunarCalendar.lunarYear)\n\t\t\t\t+ \")年\"\n\t\t\t\t+ str[1] + str[2]);\n\t}\n\n\t@Override\n\tpublic void setWeatherInfo(WeatherInfo weatherInfo) {\n\t\tsetWeatherInfo(weatherInfo.getRealTime());\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/ui/view/WeatherForecastView.java",
    "content": "package com.way.ui.view;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport com.way.common.util.TimeUtils;\nimport com.way.common.util.WeatherIconUtils;\nimport com.way.weather.plugin.bean.Forecast;\nimport com.way.weather.plugin.bean.WeatherInfo;\nimport com.way.yahoo.R;\n\npublic class WeatherForecastView extends WeatherBaseView {\n\t// 预报\n\tView forecastViewDay1;\n\tView forecastViewDay2;\n\tView forecastViewDay3;\n\tView forecastViewDay4;\n\tView forecastViewDay5;\n\tTextView forecastFootView;\n\t// 天气图标\n\tImageView iconDay1;\n\tImageView iconDay2;\n\tImageView iconDay3;\n\tImageView iconDay4;\n\tImageView iconDay5;\n\t// 星期\n\tTextView weekDay1;\n\tTextView weekDay2;\n\tTextView weekDay3;\n\tTextView weekDay4;\n\tTextView weekDay5;\n\t// 最高温\n\tTextView highTempDay1;\n\tTextView highTempDay2;\n\tTextView highTempDay3;\n\tTextView highTempDay4;\n\tTextView highTempDay5;\n\t// 最低温\n\tTextView lowTempDay1;\n\tTextView lowTempDay2;\n\tTextView lowTempDay3;\n\tTextView lowTempDay4;\n\tTextView lowTempDay5;\n\n\tpublic WeatherForecastView(Context c) {\n\t\tthis(c, null);\n\t}\n\n\tpublic WeatherForecastView(Context context, AttributeSet attrs) {\n\t\tthis(context, attrs, 0);\n\t}\n\n\tpublic WeatherForecastView(Context context, AttributeSet attrs,\n\t\t\tint defStyleAttr) {\n\t\tsuper(context, attrs, defStyleAttr);\n\t}\n\n\t@Override\n\tprotected void onFinishInflate() {\n\t\tsuper.onFinishInflate();\n\t\t// 预报\n\t\tforecastViewDay1 = findViewById(R.id.day1);\n\t\tforecastViewDay2 = findViewById(R.id.day2);\n\t\tforecastViewDay3 = findViewById(R.id.day3);\n\t\tforecastViewDay4 = findViewById(R.id.day4);\n\t\tforecastViewDay5 = findViewById(R.id.day5);\n\t\tforecastFootView = (TextView) findViewById(R.id.forecast_foot);\n\t\t// 天气图标\n\t\ticonDay1 = (ImageView) forecastViewDay1\n\t\t\t\t.findViewById(R.id.forecast_icon);\n\t\ticonDay2 = (ImageView) forecastViewDay2\n\t\t\t\t.findViewById(R.id.forecast_icon);\n\t\ticonDay3 = (ImageView) forecastViewDay3\n\t\t\t\t.findViewById(R.id.forecast_icon);\n\t\ticonDay4 = (ImageView) forecastViewDay4\n\t\t\t\t.findViewById(R.id.forecast_icon);\n\t\ticonDay5 = (ImageView) forecastViewDay5\n\t\t\t\t.findViewById(R.id.forecast_icon);\n\t\t// 星期\n\t\tweekDay1 = (TextView) forecastViewDay1\n\t\t\t\t.findViewById(R.id.forecast_week_tv);\n\t\tweekDay2 = (TextView) forecastViewDay2\n\t\t\t\t.findViewById(R.id.forecast_week_tv);\n\t\tweekDay3 = (TextView) forecastViewDay3\n\t\t\t\t.findViewById(R.id.forecast_week_tv);\n\t\tweekDay4 = (TextView) forecastViewDay4\n\t\t\t\t.findViewById(R.id.forecast_week_tv);\n\t\tweekDay5 = (TextView) forecastViewDay5\n\t\t\t\t.findViewById(R.id.forecast_week_tv);\n\t\t// 最高温\n\t\thighTempDay1 = (TextView) forecastViewDay1\n\t\t\t\t.findViewById(R.id.forecast_high_temp_tv);\n\t\thighTempDay2 = (TextView) forecastViewDay2\n\t\t\t\t.findViewById(R.id.forecast_high_temp_tv);\n\t\thighTempDay3 = (TextView) forecastViewDay3\n\t\t\t\t.findViewById(R.id.forecast_high_temp_tv);\n\t\thighTempDay4 = (TextView) forecastViewDay4\n\t\t\t\t.findViewById(R.id.forecast_high_temp_tv);\n\t\thighTempDay5 = (TextView) forecastViewDay5\n\t\t\t\t.findViewById(R.id.forecast_high_temp_tv);\n\t\t// 最低温\n\t\tlowTempDay1 = (TextView) forecastViewDay1\n\t\t\t\t.findViewById(R.id.forecast_low_temp_tv);\n\t\tlowTempDay2 = (TextView) forecastViewDay2\n\t\t\t\t.findViewById(R.id.forecast_low_temp_tv);\n\t\tlowTempDay3 = (TextView) forecastViewDay3\n\t\t\t\t.findViewById(R.id.forecast_low_temp_tv);\n\t\tlowTempDay4 = (TextView) forecastViewDay4\n\t\t\t\t.findViewById(R.id.forecast_low_temp_tv);\n\t\tlowTempDay5 = (TextView) forecastViewDay5\n\t\t\t\t.findViewById(R.id.forecast_low_temp_tv);\n\t}\n\n\tpublic void setWeatherInfo(Forecast forecast) {\n\t\tif (forecast == null || forecast.getType(1) < 0)\n\t\t\treturn;\n\t\t// 天气图标\n\t\ticonDay1.setImageResource(WeatherIconUtils.getWeatherIcon(forecast\n\t\t\t\t.getType(1)));\n\t\ticonDay2.setImageResource(WeatherIconUtils.getWeatherIcon(forecast\n\t\t\t\t.getType(2)));\n\t\ticonDay3.setImageResource(WeatherIconUtils.getWeatherIcon(forecast\n\t\t\t\t.getType(3)));\n\t\ticonDay4.setImageResource(WeatherIconUtils.getWeatherIcon(forecast\n\t\t\t\t.getType(4)));\n\t\ticonDay5.setImageResource(WeatherIconUtils.getWeatherIcon(forecast\n\t\t\t\t.getType(5)));\n\n\t\t// 星期\n\t\tweekDay1.setText(\"今天\");// 从今天开始\n\t\tweekDay2.setText(TimeUtils.getWeek(1, TimeUtils.XING_QI));\n\t\tweekDay3.setText(TimeUtils.getWeek(2, TimeUtils.XING_QI));\n\t\tweekDay4.setText(TimeUtils.getWeek(3, TimeUtils.XING_QI));\n\t\tweekDay5.setText(TimeUtils.getWeek(4, TimeUtils.XING_QI));\n\t\t// 最高温\n\t\thighTempDay1.setText(forecast.getTmpHigh(1) + \"°\");\n\t\thighTempDay2.setText(forecast.getTmpHigh(2) + \"°\");\n\t\thighTempDay3.setText(forecast.getTmpHigh(3) + \"°\");\n\t\thighTempDay4.setText(forecast.getTmpHigh(4) + \"°\");\n\t\thighTempDay5.setText(forecast.getTmpHigh(5) + \"°\");\n\t\t// 最低温\n\t\tlowTempDay1.setText(forecast.getTmpLow(1) + \"°\");\n\t\tlowTempDay2.setText(forecast.getTmpLow(2) + \"°\");\n\t\tlowTempDay3.setText(forecast.getTmpLow(3) + \"°\");\n\t\tlowTempDay4.setText(forecast.getTmpLow(4) + \"°\");\n\t\tlowTempDay5.setText(forecast.getTmpLow(5) + \"°\");\n\n\t\tforecastFootView.setText(\"\");\n\t}\n\n\t@Override\n\tpublic void setWeatherInfo(WeatherInfo weatherInfo) {\n\t\tsetWeatherInfo(weatherInfo.getForecast());\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/ui/view/WeatherIndexView.java",
    "content": "package com.way.ui.view;\n\nimport java.util.List;\n\nimport android.content.Context;\nimport android.text.TextUtils;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.widget.ImageView;\nimport android.widget.RelativeLayout;\nimport android.widget.TextView;\n\nimport com.way.weather.plugin.bean.Index;\nimport com.way.weather.plugin.bean.IndexDetail;\nimport com.way.weather.plugin.bean.WeatherInfo;\nimport com.way.yahoo.R;\n\npublic class WeatherIndexView extends WeatherBaseView {\n\t// 指数\n\tView windIndexView;\n\tView uaIndexView;// 紫外线指数\n\tView clotheIndexView;\n\tView comfortIndexView;\n\tView carIndexView;\n\tView insolationIndexView;// 晾晒\n\tView sportIndexView;\n\tView travelIndexDivider;\n\tView travelIndexView;\n\t// 图标\n\tImageView windIndexIcon;\n\tImageView uaIndexIcon;\n\tImageView clotheIndexIcon;\n\tImageView comfortIndexIcon;\n\tImageView carIndexIcon;\n\tImageView insolationIndexIcon;\n\tImageView sportIndexIcon;\n\tImageView travelIndexIcon;\n\t// 标题\n\tTextView windIndexTitle;\n\tTextView uaIndexTitle;\n\tTextView clotheIndexTitle;\n\tTextView comfortIndexTitle;\n\tTextView carIndexTitle;\n\tTextView insolationIndexTitle;\n\tTextView sportIndexTitle;\n\tTextView travelIndexTitle;\n\tTextView windIndexDesc;\n\tTextView uandexDesc;\n\tTextView clotheIndexDesc;\n\tTextView comfortIndexDesc;\n\tTextView carIndexDesc;\n\tTextView insolationIndexDesc;\n\tTextView sportIndexDesc;\n\tTextView travelIndexDesc;\n\t// 详细\n\tTextView windIndexDetail;\n\tTextView uandexDetail;\n\tTextView clotheIndexDetail;\n\tTextView comfortIndexDetail;\n\tTextView carIndexDetail;\n\tTextView insolationIndexDetail;\n\tTextView sportIndexDetail;\n\tTextView travelIndexDetail;\n\n\tpublic WeatherIndexView(Context c) {\n\t\tthis(c, null);\n\t}\n\n\tpublic WeatherIndexView(Context context, AttributeSet attrs) {\n\t\tthis(context, attrs, 0);\n\t}\n\n\tpublic WeatherIndexView(Context context, AttributeSet attrs,\n\t\t\tint defStyleAttr) {\n\t\tsuper(context, attrs, defStyleAttr);\n\t}\n\n\t@Override\n\tprotected void onFinishInflate() {\n\t\tsuper.onFinishInflate();\n\t\t// 指数\n\t\twindIndexView = findViewById(R.id.wind_index);\n\t\tuaIndexView = findViewById(R.id.ua_index);\n\t\tclotheIndexView = findViewById(R.id.clothe_index);\n\t\tcomfortIndexView = findViewById(R.id.comfort_index);\n\t\tcarIndexView = findViewById(R.id.car_index);\n\t\tinsolationIndexView = findViewById(R.id.insolation_index);\n\t\tsportIndexView = findViewById(R.id.sport_index);\n\t\ttravelIndexDivider = findViewById(R.id.travel_divider);\n\t\ttravelIndexView = findViewById(R.id.travel_index);\n\t\t// 图标\n\t\twindIndexIcon = (ImageView) windIndexView\n\t\t\t\t.findViewById(R.id.index_icon_iv);\n\t\tuaIndexIcon = (ImageView) uaIndexView.findViewById(R.id.index_icon_iv);\n\t\tclotheIndexIcon = (ImageView) clotheIndexView\n\t\t\t\t.findViewById(R.id.index_icon_iv);\n\t\tcomfortIndexIcon = (ImageView) comfortIndexView\n\t\t\t\t.findViewById(R.id.index_icon_iv);\n\t\tcarIndexIcon = (ImageView) carIndexView\n\t\t\t\t.findViewById(R.id.index_icon_iv);\n\t\tinsolationIndexIcon = (ImageView) insolationIndexView\n\t\t\t\t.findViewById(R.id.index_icon_iv);\n\t\tsportIndexIcon = (ImageView) sportIndexView\n\t\t\t\t.findViewById(R.id.index_icon_iv);\n\t\ttravelIndexIcon = (ImageView) travelIndexView\n\t\t\t\t.findViewById(R.id.index_icon_iv);\n\t\t// 标题\n\t\twindIndexTitle = (TextView) windIndexView\n\t\t\t\t.findViewById(R.id.index_title_tv);\n\t\tuaIndexTitle = (TextView) uaIndexView.findViewById(R.id.index_title_tv);\n\t\tclotheIndexTitle = (TextView) clotheIndexView\n\t\t\t\t.findViewById(R.id.index_title_tv);\n\t\tcomfortIndexTitle = (TextView) comfortIndexView\n\t\t\t\t.findViewById(R.id.index_title_tv);\n\t\tcarIndexTitle = (TextView) carIndexView\n\t\t\t\t.findViewById(R.id.index_title_tv);\n\t\tinsolationIndexTitle = (TextView) insolationIndexView\n\t\t\t\t.findViewById(R.id.index_title_tv);\n\t\tsportIndexTitle = (TextView) sportIndexView\n\t\t\t\t.findViewById(R.id.index_title_tv);\n\t\ttravelIndexTitle = (TextView) travelIndexView\n\t\t\t\t.findViewById(R.id.index_title_tv);\n\n\t\t// 描述\n\t\twindIndexDesc = (TextView) windIndexView\n\t\t\t\t.findViewById(R.id.index_desc_tv);\n\t\tuandexDesc = (TextView) uaIndexView.findViewById(R.id.index_desc_tv);\n\t\tclotheIndexDesc = (TextView) clotheIndexView\n\t\t\t\t.findViewById(R.id.index_desc_tv);\n\t\tcomfortIndexDesc = (TextView) comfortIndexView\n\t\t\t\t.findViewById(R.id.index_desc_tv);\n\t\tcarIndexDesc = (TextView) carIndexView.findViewById(R.id.index_desc_tv);\n\t\tinsolationIndexDesc = (TextView) insolationIndexView\n\t\t\t\t.findViewById(R.id.index_desc_tv);\n\t\tsportIndexDesc = (TextView) sportIndexView\n\t\t\t\t.findViewById(R.id.index_desc_tv);\n\t\ttravelIndexDesc = (TextView) travelIndexView\n\t\t\t\t.findViewById(R.id.index_desc_tv);\n\t\t// 详细\n\t\twindIndexDetail = (TextView) windIndexView\n\t\t\t\t.findViewById(R.id.index_detail_tv);\n\t\tuandexDetail = (TextView) uaIndexView\n\t\t\t\t.findViewById(R.id.index_detail_tv);\n\t\tclotheIndexDetail = (TextView) clotheIndexView\n\t\t\t\t.findViewById(R.id.index_detail_tv);\n\t\tcomfortIndexDetail = (TextView) comfortIndexView\n\t\t\t\t.findViewById(R.id.index_detail_tv);\n\t\tcarIndexDetail = (TextView) carIndexView\n\t\t\t\t.findViewById(R.id.index_detail_tv);\n\t\tinsolationIndexDetail = (TextView) insolationIndexView\n\t\t\t\t.findViewById(R.id.index_detail_tv);\n\t\tsportIndexDetail = (TextView) sportIndexView\n\t\t\t\t.findViewById(R.id.index_detail_tv);\n\t\ttravelIndexDetail = (TextView) travelIndexView\n\t\t\t\t.findViewById(R.id.index_detail_tv);\n\t}\n\n\tpublic void setWeatherInfo(Index index) {\n\t\tif (index == null || index.getIndex().get(0) == null)\n\t\t\treturn;\n\t\tList<IndexDetail> lists = index.getIndex();\n\t\tif (TextUtils.isEmpty(lists.get(7).getDesc())) {\n\t\t\ttravelIndexDivider.setVisibility(View.GONE);\n\t\t\ttravelIndexView.setVisibility(View.GONE);\n\t\t} else {\n\t\t\ttravelIndexDivider.setVisibility(View.VISIBLE);\n\t\t\ttravelIndexView.setVisibility(View.VISIBLE);\n\t\t}\n\t\t// 图标\n\t\twindIndexIcon.setImageResource(R.drawable.ic_lifeindex_wind);\n\t\tuaIndexIcon.setImageResource(R.drawable.ic_lifeindex_ultravioletrays);\n\t\tclotheIndexIcon.setImageResource(R.drawable.ic_lifeindex_clothes);\n\t\tcomfortIndexIcon.setImageResource(R.drawable.ic_lifeindex_cold);\n\t\tcarIndexIcon.setImageResource(R.drawable.ic_lifeindex_carwash);\n\t\tinsolationIndexIcon.setImageResource(R.drawable.ic_lifeindex_makeup);\n\t\tsportIndexIcon.setImageResource(R.drawable.ic_lifeindex_sport);\n\t\ttravelIndexIcon.setImageResource(R.drawable.ic_lifeindex_tour);\n\n\t\t// 标题\n\t\twindIndexTitle.setText(lists.get(0).getTitle());\n\t\tuaIndexTitle.setText(lists.get(1).getTitle());\n\t\tclotheIndexTitle.setText(lists.get(2).getTitle());\n\t\tcomfortIndexTitle.setText(lists.get(3).getTitle());\n\t\tcarIndexTitle.setText(lists.get(4).getTitle());\n\t\tinsolationIndexTitle.setText(lists.get(5).getTitle());\n\t\tsportIndexTitle.setText(lists.get(6).getTitle());\n\t\ttravelIndexTitle.setText(lists.get(7).getTitle());\n\t\t// 描述\n\t\twindIndexDesc.setText(lists.get(0).getDesc());\n\t\tuandexDesc.setText(lists.get(1).getDesc());\n\t\tclotheIndexDesc.setText(lists.get(2).getDesc());\n\t\tcomfortIndexDesc.setText(lists.get(3).getDesc());\n\t\tcarIndexDesc.setText(lists.get(4).getDesc());\n\t\tinsolationIndexDesc.setText(lists.get(5).getDesc());\n\t\tsportIndexDesc.setText(lists.get(6).getDesc());\n\t\ttravelIndexDesc.setText(lists.get(7).getDesc());\n\n\t\t// 详细\n\t\twindIndexDetail.setText(lists.get(0).getDetail());\n\t\tuandexDetail.setText(lists.get(1).getDetail());\n\t\tclotheIndexDetail.setText(lists.get(2).getDetail());\n\t\tcomfortIndexDetail.setText(lists.get(3).getDetail());\n\t\tcarIndexDetail.setText(lists.get(4).getDetail());\n\t\tinsolationIndexDetail.setText(lists.get(5).getDetail());\n\t\tsportIndexDetail.setText(lists.get(6).getDetail());\n\t\ttravelIndexDetail.setText(lists.get(7).getDetail());\n\t}\n\n\t@Override\n\tpublic void setWeatherInfo(WeatherInfo weatherInfo) {\n\t\tsetWeatherInfo(weatherInfo.getIndex());\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/ui/view/WeatherTypefacedTextView.java",
    "content": "package com.way.ui.view;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Typeface;\nimport android.text.TextUtils;\nimport android.util.AttributeSet;\nimport android.widget.TextView;\n\nimport com.way.yahoo.R;\n\npublic class WeatherTypefacedTextView extends TextView {\n\tpublic WeatherTypefacedTextView(Context context) {\n\t\tthis(context, null);\n\t}\n\n\tpublic WeatherTypefacedTextView(Context context, AttributeSet attrs) {\n\t\tthis(context, attrs, 0);\n\t}\n\n\tpublic WeatherTypefacedTextView(Context context, AttributeSet attrs,\n\t\t\tint defStyle) {\n\t\tsuper(context, attrs, defStyle);\n\t\tif (isInEditMode())\n\t\t\treturn;\n\t\tTypedArray a = context.obtainStyledAttributes(attrs,\n\t\t\t\tR.styleable.WeatherTypefacedTextView, defStyle, 0);\n\t\tString typeface = a\n\t\t\t\t.getString(R.styleable.WeatherTypefacedTextView_typeface);\n\t\tif (!TextUtils.isEmpty(typeface)) {\n\t\t\tTypeface face = Typeface.createFromAsset(getContext().getAssets(),\n\t\t\t\t\ttypeface);\n\t\t\tif (face != null)\n\t\t\t\tsetTypeface(face);\n\t\t}\n\t\ta.recycle();\n\t}\n}"
  },
  {
    "path": "WayHoo/src/com/way/util/blur/jni/BitmapUtils.java",
    "content": "package com.way.util.blur.jni;\n\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.view.View;\n\n/**\n * Created by way on 14-6-17.\n */\npublic class BitmapUtils {\n\tpublic static Bitmap drawViewToBitmap(View view, int width, int height,\n\t\t\tint scaleRatio) {\n\t\treturn drawViewToBitmap(view, width, height, 0f, 0f, scaleRatio);\n\t}\n\n\tpublic static Bitmap drawViewToBitmap(View view, int width, int height,\n\t\t\tfloat translateX, float translateY, int scaleRatio) {\n\t\tfloat scale = 1f / scaleRatio;\n\t\tint bmpWidth = (int) (width * scale - translateX / scaleRatio);\n\t\tint bmpHeight = (int) (height * scale - translateY / scaleRatio);\n\t\tBitmap dest = Bitmap.createBitmap(bmpWidth, bmpHeight,\n\t\t\t\tBitmap.Config.ARGB_8888);\n\t\tCanvas c = new Canvas(dest);\n\t\tc.translate(-translateX / scaleRatio, -translateY / scaleRatio);\n\t\tif (scaleRatio > 1) {\n\t\t\tc.scale(scale, scale);\n\t\t}\n\t\tview.draw(c);\n\t\treturn dest;\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/util/blur/jni/FrostedGlassUtil.java",
    "content": "package com.way.util.blur.jni;\n\nimport android.graphics.Bitmap;\n\npublic class FrostedGlassUtil {\n\tprivate static final String TAG = \"FrostedGlass\";\n\tprivate volatile static FrostedGlassUtil mFrostedGlassUtil;\n\n\tpublic static FrostedGlassUtil getInstance() {\n\t\tif (mFrostedGlassUtil == null) {\n\t\t\tsynchronized (FrostedGlassUtil.class) {\n\t\t\t\tif (mFrostedGlassUtil == null) {\n\t\t\t\t\tmFrostedGlassUtil = new FrostedGlassUtil();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn mFrostedGlassUtil;\n\t}\n\n\tpublic native void boxBlur(Bitmap srcBitmap, int radius);\n\n\tpublic native void stackBlur(Bitmap srcBitmap, int radius);\n\n\tpublic native void oilPaint(Bitmap srcBitmap, int radius);\n\n\tpublic native void colorWaterPaint(Bitmap srcBitmap, int radius);\n\n\tpublic synchronized Bitmap convertToBlur(Bitmap bmp, int radius) {\n\t\tstackBlur(bmp, radius);\n\t\treturn bmp;\n\t}\n\n\tstatic {\n\t\t// load frosted glass lib\n\t\tSystem.loadLibrary(\"frostedGlass\");\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/weather/plugin/bean/AQI.java",
    "content": "package com.way.weather.plugin.bean;\n\n\npublic class AQI {\n\tprivate int aqi;\n\tprivate String aqi_desc;\n\tprivate String aqi_level;\n\tprivate String city_code;\n\tprivate int co;\n\tprivate int no2;\n\tprivate int o3;\n\tprivate int pm10;\n\tprivate int pm25;\n\tprivate long pub_time;\n\tprivate int so2;\n\tprivate String source;\n\tprivate String spot;\n\n\tpublic int getAqi() {\n\t\treturn this.aqi;\n\t}\n\n\tpublic String getAqi_desc() {\n\t\treturn this.aqi_desc;\n\t}\n\n\tpublic String getAqi_level() {\n\t\treturn this.aqi_level;\n\t}\n\n\tpublic String getCity_code() {\n\t\treturn this.city_code;\n\t}\n\n\tpublic int getCo() {\n\t\treturn this.co;\n\t}\n\n\tpublic int getNo2() {\n\t\treturn this.no2;\n\t}\n\n\tpublic int getO3() {\n\t\treturn this.o3;\n\t}\n\n\tpublic int getPm10() {\n\t\treturn this.pm10;\n\t}\n\n\tpublic int getPm25() {\n\t\treturn this.pm25;\n\t}\n\n\tpublic long getPub_time() {\n\t\treturn this.pub_time;\n\t}\n\n\tpublic int getSo2() {\n\t\treturn this.so2;\n\t}\n\n\tpublic String getSource() {\n\t\treturn this.source;\n\t}\n\n\tpublic String getSpot() {\n\t\treturn this.spot;\n\t}\n\n\tpublic void setAqi(int paramInt) {\n\t\tthis.aqi = paramInt;\n\t}\n\n\tpublic void setAqi_desc(String paramString) {\n\t\tthis.aqi_desc = paramString;\n\t}\n\n\tpublic void setAqi_level(String paramString) {\n\t\tthis.aqi_level = paramString;\n\t}\n\n\tpublic void setCity_code(String paramString) {\n\t\tthis.city_code = paramString;\n\t}\n\n\tpublic void setCo(int paramInt) {\n\t\tthis.co = paramInt;\n\t}\n\n\tpublic void setNo2(int paramInt) {\n\t\tthis.no2 = paramInt;\n\t}\n\n\tpublic void setO3(int paramInt) {\n\t\tthis.o3 = paramInt;\n\t}\n\n\tpublic void setPm10(int paramInt) {\n\t\tthis.pm10 = paramInt;\n\t}\n\n\tpublic void setPm25(int paramInt) {\n\t\tthis.pm25 = paramInt;\n\t}\n\n\tpublic void setPub_time(long paramLong) {\n\t\tthis.pub_time = paramLong;\n\t}\n\n\tpublic void setSo2(int paramInt) {\n\t\tthis.so2 = paramInt;\n\t}\n\n\tpublic void setSource(String paramString) {\n\t\tthis.source = paramString;\n\t}\n\n\tpublic void setSpot(String paramString) {\n\t\tthis.spot = paramString;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"AQI [aqi=\" + aqi + \", aqi_desc=\" + aqi_desc + \", aqi_level=\"\n\t\t\t\t+ aqi_level + \", city_code=\" + city_code + \", co=\" + co\n\t\t\t\t+ \", no2=\" + no2 + \", o3=\" + o3 + \", pm10=\" + pm10 + \", pm25=\"\n\t\t\t\t+ pm25 + \", pub_time=\" + pub_time + \", so2=\" + so2\n\t\t\t\t+ \", source=\" + source + \", spot=\" + spot + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/weather/plugin/bean/Alerts.java",
    "content": "package com.way.weather.plugin.bean;\n\nimport java.util.ArrayList;\n\npublic class Alerts {\n\tprivate ArrayList<Alert> arryAlert;\n\tprivate String pid;\n\n\tpublic ArrayList<Alert> getArryAlert() {\n\t\treturn this.arryAlert;\n\t}\n\n\tpublic String getPid() {\n\t\treturn this.pid;\n\t}\n\n\tpublic void setArryAlert(ArrayList<Alert> paramArrayList) {\n\t\tthis.arryAlert = paramArrayList;\n\t}\n\n\tpublic void setPid(String paramString) {\n\t\tthis.pid = paramString;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Alerts [arryAlert=\" + arryAlert + \", pid=\" + pid + \"]\";\n\t}\n\n\tpublic static class Alert {\n\t\tprivate String abnormal;\n\t\tprivate String detail;\n\t\tprivate String holiday;\n\t\tprivate String level;\n\t\tprivate Long pubTime;\n\t\tprivate String title;\n\n\t\tpublic String getAbnormal() {\n\t\t\treturn this.abnormal;\n\t\t}\n\n\t\tpublic String getDetail() {\n\t\t\treturn this.detail;\n\t\t}\n\n\t\tpublic String getHoliday() {\n\t\t\treturn this.holiday;\n\t\t}\n\n\t\tpublic String getLevel() {\n\t\t\treturn this.level;\n\t\t}\n\n\t\tpublic Long getPubTime() {\n\t\t\treturn this.pubTime;\n\t\t}\n\n\t\tpublic String getTitle() {\n\t\t\treturn this.title;\n\t\t}\n\n\t\tpublic void setAbnormal(String paramString) {\n\t\t\tthis.abnormal = paramString;\n\t\t}\n\n\t\tpublic void setDetail(String paramString) {\n\t\t\tthis.detail = paramString;\n\t\t}\n\n\t\tpublic void setHoliday(String paramString) {\n\t\t\tthis.holiday = paramString;\n\t\t}\n\n\t\tpublic void setLevel(String paramString) {\n\t\t\tthis.level = paramString;\n\t\t}\n\n\t\tpublic void setPubTime(long paramLong) {\n\t\t\tthis.pubTime = paramLong;\n\t\t}\n\n\t\tpublic void setTitle(String paramString) {\n\t\t\tthis.title = paramString;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"Alert [abnormal=\" + abnormal + \", detail=\" + detail\n\t\t\t\t\t+ \", holiday=\" + holiday + \", level=\" + level\n\t\t\t\t\t+ \", pubTime=\" + pubTime + \", title=\" + title + \"]\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/weather/plugin/bean/Forecast.java",
    "content": "package com.way.weather.plugin.bean;\n\nimport java.util.Arrays;\n\nimport com.way.weather.plugin.util.Constants;\n\npublic class Forecast {\n\tpublic static final int DAY_NUM = 6;\n\tprivate String pid;\n\tprivate Long pub_time;\n\tprivate Integer[] humiditys = new Integer[DAY_NUM];\n\tprivate Integer[] pressures = new Integer[DAY_NUM];\n\tprivate Long[] sunrise = new Long[DAY_NUM];\n\tprivate Long[] sunset = new Long[DAY_NUM];\n\tprivate Integer[] tmpHighs = new Integer[DAY_NUM];\n\tprivate Integer[] tmpLows = new Integer[DAY_NUM];\n\tprivate String[] weatherNames = new String[DAY_NUM];\n\tprivate String[] weatherNamesFrom = new String[DAY_NUM];\n\tprivate String[] weatherNamesTo = new String[DAY_NUM];\n\tprivate String[] winds = new String[DAY_NUM];\n\n\tprivate int[] types = new int[DAY_NUM];\n\n\tpublic int getHumidity(int position) {\n\t\tif (position >= DAY_NUM || position < 0\n\t\t\t\t|| this.humiditys[position] == null)\n\t\t\treturn Constants.NO_VALUE_FLAG;\n\t\treturn humiditys[position];\n\t}\n\n\tpublic String getPid() {\n\t\treturn pid;\n\t}\n\n\tpublic int getPressure(int position) {\n\t\tif (position >= DAY_NUM || position < 0 || pressures[position] == null)\n\t\t\treturn Constants.NO_VALUE_FLAG;\n\t\treturn pressures[position];\n\t}\n\n\tpublic long getPubtime() {\n\t\treturn pub_time;\n\t}\n\n\tpublic long getSunrise(int position) {\n\t\tif (position >= DAY_NUM || position < 0)\n\t\t\treturn Constants.NO_VALUE_FLAG;\n\t\treturn sunrise[position];\n\t}\n\n\tpublic long getSunset(int position) {\n\t\tif (position >= DAY_NUM || position < 0)\n\t\t\treturn Constants.NO_VALUE_FLAG;\n\t\treturn sunset[position];\n\t}\n\n\tpublic int getTmpHigh(int position) {\n\t\tif (position >= DAY_NUM || position < 0 || tmpHighs[position] == null)\n\t\t\treturn Constants.NO_VALUE_FLAG;\n\t\treturn tmpHighs[position];\n\t}\n\n\tpublic int getTmpLow(int position) {\n\t\tif (position >= DAY_NUM || position < 0 || tmpLows[position] == null)\n\t\t\treturn Constants.NO_VALUE_FLAG;\n\t\treturn tmpLows[position];\n\t}\n\n\tpublic String getWeatherNames(int position) {\n\t\tif (position >= DAY_NUM || position < 0)\n\t\t\treturn \"\";\n\t\treturn weatherNames[position];\n\t}\n\n\tpublic String getWeatherNamesFrom(int position) {\n\t\tif (position >= DAY_NUM || position < 0)\n\t\t\treturn \"\";\n\t\treturn weatherNamesFrom[position];\n\t}\n\n\tpublic String getWeatherNamesTo(int position) {\n\t\tif (position >= DAY_NUM || position < 0)\n\t\t\treturn \"\";\n\t\treturn weatherNamesTo[position];\n\t}\n\n\tpublic String getWinds(int position) {\n\t\tif (position >= DAY_NUM || position < 0)\n\t\t\treturn \"\";\n\t\treturn winds[position];\n\t}\n\n\tpublic void setHumiditys(int position, int humidity) {\n\t\tif (position >= DAY_NUM || position < 0)\n\t\t\treturn;\n\t\tthis.humiditys[position] = humidity;\n\t}\n\n\tpublic void setPid(String pid) {\n\t\tthis.pid = pid;\n\t}\n\n\tpublic void setPressures(int position, int pressure) {\n\t\tif (position >= DAY_NUM || position < 0)\n\t\t\treturn;\n\t\tthis.pressures[position] = pressure;\n\t}\n\n\tpublic void setPubtime(Long pubtime) {\n\t\tthis.pub_time = pubtime;\n\t}\n\n\tpublic void setSunrise(int position, long sunrise) {\n\t\tif (position >= DAY_NUM || position < 0)\n\t\t\treturn;\n\t\tthis.sunrise[position] = sunrise;\n\t}\n\n\tpublic void setSunset(int position, long sunset) {\n\t\tif (position >= DAY_NUM || position < 0)\n\t\t\treturn;\n\t\tthis.sunset[position] = sunset;\n\t}\n\n\tpublic void setTmpHighs(int position, int tmpHigh) {\n\t\tif (position >= DAY_NUM || position < 0)\n\t\t\treturn;\n\t\tthis.tmpHighs[position] = tmpHigh;\n\t}\n\n\tpublic void setTmpLows(int position, int tmpLow) {\n\t\tif (position >= DAY_NUM || position < 0)\n\t\t\treturn;\n\t\tthis.tmpLows[position] = tmpLow;\n\t}\n\n\tpublic void setWeatherNames(int position, String weatherName) {\n\t\tif (position >= DAY_NUM || position < 0)\n\t\t\treturn;\n\t\tthis.weatherNames[position] = weatherName;\n\t}\n\n\tpublic void setWeatherNamesFrom(int position, String weatherNameFrom) {\n\t\tif (position >= DAY_NUM || position < 0)\n\t\t\treturn;\n\t\tthis.weatherNamesFrom[position] = weatherNameFrom;\n\t}\n\n\tpublic void setWeatherNamesTo(int position, String weatherNameTo) {\n\t\tif (position >= DAY_NUM || position < 0)\n\t\t\treturn;\n\t\tthis.weatherNamesTo[position] = weatherNameTo;\n\t}\n\n\tpublic void setWinds(int position, String wind) {\n\t\tif (position >= DAY_NUM || position < 0)\n\t\t\treturn;\n\t\tthis.winds[position] = wind;\n\t}\n\n\tpublic int getType(int position) {\n\t\tif (position >= DAY_NUM || position < 0)\n\t\t\treturn Constants.NO_VALUE_FLAG;\n\t\treturn types[position];\n\t}\n\n\tpublic void setType(int position, int type) {\n\t\tif (position >= DAY_NUM || position < 0)\n\t\t\treturn;\n\t\tthis.types[position] = type;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Forecast [humiditys=\" + Arrays.toString(humiditys) + \", pid=\"\n\t\t\t\t+ pid + \", pressures=\" + Arrays.toString(pressures)\n\t\t\t\t+ \", pub_time=\" + pub_time + \", sunrise=\"\n\t\t\t\t+ Arrays.toString(sunrise) + \", sunset=\"\n\t\t\t\t+ Arrays.toString(sunset) + \", tmpHighs=\"\n\t\t\t\t+ Arrays.toString(tmpHighs) + \", tmpLows=\"\n\t\t\t\t+ Arrays.toString(tmpLows) + \", weatherNames=\"\n\t\t\t\t+ Arrays.toString(weatherNames) + \", weatherNamesFrom=\"\n\t\t\t\t+ Arrays.toString(weatherNamesFrom) + \", weatherNamesTo=\"\n\t\t\t\t+ Arrays.toString(weatherNamesTo) + \", winds=\"\n\t\t\t\t+ Arrays.toString(winds) + \", types=\" + Arrays.toString(types)\n\t\t\t\t+ \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/weather/plugin/bean/Index.java",
    "content": "package com.way.weather.plugin.bean;\n\nimport java.util.List;\n\npublic class Index {\n\tprivate String city_code;\n\tprivate List<IndexDetail> index;\n\n\tpublic String getCity_code() {\n\t\treturn this.city_code;\n\t}\n\n\tpublic List<IndexDetail> getIndex() {\n\t\treturn this.index;\n\t}\n\n\tpublic void setCity_code(String paramString) {\n\t\tthis.city_code = paramString;\n\t}\n\n\tpublic void setIndex(List<IndexDetail> paramList) {\n\t\tthis.index = paramList;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Index [city_code=\" + city_code + \", index=\" + index + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/weather/plugin/bean/IndexDetail.java",
    "content": "package com.way.weather.plugin.bean;\n\n\npublic class IndexDetail {\n\tprivate String desc;\n\tprivate String detail;\n\tprivate String title;\n\tprivate int type;\n\n\tpublic String getDesc() {\n\t\treturn this.desc;\n\t}\n\n\tpublic String getDetail() {\n\t\treturn this.detail;\n\t}\n\n\tpublic String getTitle() {\n\t\treturn this.title;\n\t}\n\n\tpublic int getType() {\n\t\treturn this.type;\n\t}\n\n\tpublic void setDesc(String desc) {\n\t\tthis.desc = desc;\n\t}\n\n\tpublic void setDetail(String detail) {\n\t\tthis.detail = detail;\n\t}\n\n\tpublic void setTitle(String title) {\n\t\tthis.title = title;\n\t}\n\n\tpublic void setType(int type) {\n\t\tthis.type = type;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"IndexDetail [desc=\" + desc + \", detail=\" + detail + \", title=\"\n\t\t\t\t+ title + \", type=\" + type + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/weather/plugin/bean/RealTime.java",
    "content": "package com.way.weather.plugin.bean;\n\n\npublic class RealTime {\n\tprivate int animation_type;\n\tprivate String city_code;\n\tprivate long falling_tide;\n\tprivate int humidity;\n\tprivate double pressure;\n\tprivate long pub_time;\n\tprivate long rising_tide;\n\tprivate int temp;\n\tprivate int water;\n\tprivate String weather_name;\n\tprivate String wind;\n\n\tpublic int getAnimation_type() {\n\t\treturn this.animation_type;\n\t}\n\n\tpublic String getCity_code() {\n\t\treturn this.city_code;\n\t}\n\n\tpublic long getFalling_tide() {\n\t\treturn this.falling_tide;\n\t}\n\n\tpublic int getHumidity() {\n\t\treturn this.humidity;\n\t}\n\n\tpublic double getPressure() {\n\t\treturn this.pressure;\n\t}\n\n\tpublic long getPub_time() {\n\t\treturn this.pub_time;\n\t}\n\n\tpublic long getRising_tide() {\n\t\treturn this.rising_tide;\n\t}\n\n\tpublic int getTemp() {\n\t\treturn this.temp;\n\t}\n\n\tpublic int getWater() {\n\t\treturn this.water;\n\t}\n\n\tpublic String getWeather_name() {\n\t\treturn this.weather_name;\n\t}\n\n\tpublic String getWind() {\n\t\treturn this.wind;\n\t}\n\n\tpublic void setAnimation_type(int type) {\n\t\tthis.animation_type = type;\n\t}\n\n\tpublic void setCity_code(String cityCode) {\n\t\tthis.city_code = cityCode;\n\t}\n\n\tpublic void setFalling_tide(long fallingTide) {\n\t\tthis.falling_tide = fallingTide;\n\t}\n\n\tpublic void setHumidity(int humidity) {\n\t\tthis.humidity = humidity;\n\t}\n\n\tpublic void setPressure(double pressure) {\n\t\tthis.pressure = pressure;\n\t}\n\n\tpublic void setPub_time(long pubTime) {\n\t\tthis.pub_time = pubTime;\n\t}\n\n\tpublic void setRising_tide(long risingTide) {\n\t\tthis.rising_tide = risingTide;\n\t}\n\n\tpublic void setTemp(int temp) {\n\t\tthis.temp = temp;\n\t}\n\n\tpublic void setWater(int water) {\n\t\tthis.water = water;\n\t}\n\n\tpublic void setWeather_name(String weatherName) {\n\t\tthis.weather_name = weatherName;\n\t}\n\n\tpublic void setWind(String wind) {\n\t\tthis.wind = wind;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"RealTime [animation_type=\" + animation_type + \", city_code=\"\n\t\t\t\t+ city_code + \", falling_tide=\" + falling_tide + \", humidity=\"\n\t\t\t\t+ humidity + \", pressure=\" + pressure + \", pub_time=\"\n\t\t\t\t+ pub_time + \", rising_tide=\" + rising_tide + \", temp=\" + temp\n\t\t\t\t+ \", water=\" + water + \", weather_name=\" + weather_name\n\t\t\t\t+ \", wind=\" + wind + \"]\";\n\t}\n\n}"
  },
  {
    "path": "WayHoo/src/com/way/weather/plugin/bean/WeatherInfo.java",
    "content": "package com.way.weather.plugin.bean;\n\npublic class WeatherInfo {\n\tprivate boolean isNewData;\n\tprivate RealTime realTime;\n\tprivate Forecast forecast;\n\tprivate AQI aqi;\n\tprivate Index index;\n\tprivate Alerts alerts;\n\n\tpublic WeatherInfo() {\n\t\tsuper();\n\t}\n\n\tpublic WeatherInfo(RealTime realTime, Forecast forecast, AQI aqi,\n\t\t\tIndex index, Alerts alerts) {\n\t\tsuper();\n\t\tthis.realTime = realTime;\n\t\tthis.forecast = forecast;\n\t\tthis.aqi = aqi;\n\t\tthis.index = index;\n\t\tthis.alerts = alerts;\n\t}\n\n\tpublic boolean isNewData() {\n\t\treturn isNewData;\n\t}\n\n\tpublic void setNewData(boolean isNewData) {\n\t\tthis.isNewData = isNewData;\n\t}\n\n\tpublic RealTime getRealTime() {\n\t\treturn realTime;\n\t}\n\n\tpublic void setRealTime(RealTime realTime) {\n\t\tthis.realTime = realTime;\n\t}\n\n\tpublic Forecast getForecast() {\n\t\treturn forecast;\n\t}\n\n\tpublic void setForecast(Forecast forecast) {\n\t\tthis.forecast = forecast;\n\t}\n\n\tpublic AQI getAqi() {\n\t\treturn aqi;\n\t}\n\n\tpublic void setAqi(AQI aqi) {\n\t\tthis.aqi = aqi;\n\t}\n\n\tpublic Index getIndex() {\n\t\treturn index;\n\t}\n\n\tpublic void setIndex(Index index) {\n\t\tthis.index = index;\n\t}\n\n\tpublic Alerts getAlerts() {\n\t\treturn alerts;\n\t}\n\n\tpublic void setAlerts(Alerts alerts) {\n\t\tthis.alerts = alerts;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"WeatherInfo [isNewData=\" + isNewData + \", realTime=\" + realTime\n\t\t\t\t+ \", forecast=\" + forecast + \", aqi=\" + aqi + \", index=\"\n\t\t\t\t+ index + \", alerts=\" + alerts + \"]\";\n\t}\n\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/weather/plugin/spider/WeatherConstants.java",
    "content": "package com.way.weather.plugin.spider;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\n\npublic class WeatherConstants {\n\tpublic static final String AIRCURE_INDEX = \"晾晒指数\";\n\tpublic static final Map<String, String> AIRCURE_INDEX_DESC_CN_MAP;\n\tpublic static final Map<String, String> AIRCURE_INDEX_DESC_EN_MAP;\n\tpublic static final Map<String, Map<String, String>> AIRCURE_INDEX_DESC_LANGUAGE_MAP;\n\tpublic static final Map<String, String> AIRCURE_INDEX_DESC_TW_MAP;\n\tpublic static final Map<String, String> AIRCURE_INDEX_DETAIL_CN_MAP;\n\tpublic static final Map<String, String> AIRCURE_INDEX_DETAIL_EN_MAP;\n\tpublic static final Map<String, Map<String, String>> AIRCURE_INDEX_DETAIL_LANGUAGE_MAP;\n\tpublic static final Map<String, String> AIRCURE_INDEX_DETAIL_TW_MAP;\n\tpublic static final Map<String, String> ALERM_LEVEL_CN_MAP;\n\tpublic static final Map<String, String> ALERM_LEVEL_EN_MAP;\n\tpublic static final int AQI_DANGEROUS = 300;\n\tpublic static final Map<Integer, String> AQI_DESC_CN_MAP;\n\tpublic static final Map<Integer, String> AQI_DESC_EN_MAP;\n\tpublic static final Map<String, Map<Integer, String>> AQI_DESC_LANGUAGE_MAP;\n\tpublic static final Map<Integer, String> AQI_DESC_TW_MAP;\n\tpublic static final int AQI_FINE = 100;\n\tpublic static final int AQI_GOOD = 50;\n\tpublic static final int AQI_HAZARDOUS = 500;\n\tpublic static final Map<Integer, String> AQI_LEVEL_CN_MAP;\n\tpublic static final Map<Integer, String> AQI_LEVEL_EN_MAP;\n\tpublic static final Map<String, Map<Integer, String>> AQI_LEVEL_LANGUAGE_MAP;\n\tpublic static final Map<Integer, String> AQI_LEVEL_TW_MAP;\n\tpublic static final int AQI_LIGHT_POLLUTION = 150;\n\tpublic static final int AQI_PURE = 0;\n\tpublic static final Map<String, String> AQI_SOURCE_LANGUAGE_MAP;\n\tpublic static final int AQI_UNHEALTHY = 200;\n\tpublic static final String CARWASH_INDEX = \"洗车指数\";\n\tpublic static final Map<String, String> CARWASH_INDEX_DESC_CN_MAP;\n\tpublic static final Map<String, String> CARWASH_INDEX_DESC_EN_MAP;\n\tpublic static final Map<String, Map<String, String>> CARWASH_INDEX_DESC_LANGUAGE_MAP;\n\tpublic static final Map<String, String> CARWASH_INDEX_DESC_TW_MAP;\n\tpublic static final Map<String, String> CARWASH_INDEX_DETAIL_CN_MAP;\n\tpublic static final Map<String, String> CARWASH_INDEX_DETAIL_EN_MAP;\n\tpublic static final Map<String, Map<String, String>> CARWASH_INDEX_DETAIL_LANGUAGE_MAP;\n\tpublic static final Map<String, String> CARWASH_INDEX_DETAIL_TW_MAP;\n\tpublic static final Map<String, String> CHINA_LANGUAGE_MAP;\n\tpublic static final Map<String, String> CN_ALERM_LEVEL_MAP;\n\tpublic static final Map<String, String> CN_WEATHER_TYPE_MAP;\n\tpublic static final Map<String, String> CN_WIND_TYPE_MAP;\n\tpublic static final String COMFORT_INDEX = \"舒适度指数\";\n\tpublic static final Map<String, String> COMFORT_INDEX_DESC_CN_MAP;\n\tpublic static final Map<String, String> COMFORT_INDEX_DESC_EN_MAP;\n\tpublic static final Map<String, Map<String, String>> COMFORT_INDEX_DESC_LANGUAGE_MAP;\n\tpublic static final Map<String, String> COMFORT_INDEX_DESC_TW_MAP;\n\tpublic static final Map<String, String> COMFORT_INDEX_DETAIL_CN_MAP;\n\tpublic static final Map<String, String> COMFORT_INDEX_DETAIL_EN_MAP;\n\tpublic static final Map<String, Map<String, String>> COMFORT_INDEX_DETAIL_LANGUAGE_MAP;\n\tpublic static final Map<String, String> COMFORT_INDEX_DETAIL_TW_MAP;\n\tpublic static final String DRESS_48H_INDEX = \"48小时穿衣指数\";\n\tpublic static final String DRESS_INDEX = \"穿衣指数\";\n\tpublic static final Map<String, String> DRESS_INDEX_DESC_CN_MAP;\n\tpublic static final Map<String, String> DRESS_INDEX_DESC_EN_MAP;\n\tpublic static final Map<String, Map<String, String>> DRESS_INDEX_DESC_LANGUAGE_MAP;\n\tpublic static final Map<String, String> DRESS_INDEX_DESC_TW_MAP;\n\tpublic static final Map<String, String> DRESS_INDEX_DETAIL_CN_MAP;\n\tpublic static final Map<String, String> DRESS_INDEX_DETAIL_EN_MAP;\n\tpublic static final Map<String, Map<String, String>> DRESS_INDEX_DETAIL_LANGUAGE_MAP;\n\tpublic static final Map<String, String> DRESS_INDEX_DETAIL_TW_MAP;\n\tpublic static final Map<String, String> EN_ALERM_LEVEL_MAP;\n\tpublic static final Map<String, String> EN_WEATHER_TYPE_MAP;\n\tpublic static final Map<String, String> EN_WIND_TYPE_MAP;\n\tpublic static final String EXERCISE_INDEX = \"晨练指数\";\n\tpublic static final Map<String, String> EXERCISE_INDEX_DESC_CN_MAP;\n\tpublic static final Map<String, String> EXERCISE_INDEX_DESC_EN_MAP;\n\tpublic static final Map<String, Map<String, String>> EXERCISE_INDEX_DESC_LANGUAGE_MAP;\n\tpublic static final Map<String, String> EXERCISE_INDEX_DESC_TW_MAP;\n\tpublic static final Map<String, String> EXERCISE_INDEX_DETAIL_CN_MAP;\n\tpublic static final Map<String, String> EXERCISE_INDEX_DETAIL_EN_MAP;\n\tpublic static final Map<String, Map<String, String>> EXERCISE_INDEX_DETAIL_LANGUAGE_MAP;\n\tpublic static final Map<String, String> EXERCISE_INDEX_DETAIL_TW_MAP;\n\tpublic static final Map<String, String> INDEX_CN_MAP;\n\tpublic static final Map<String, Map<String, String>> INDEX_DESC_CN_MAP;\n\tpublic static final Map<String, Map<String, String>> INDEX_DESC_EN_MAP;\n\tpublic static final Map<String, Map<String, Map<String, String>>> INDEX_DESC_LANGUAGE_MAP;\n\tpublic static final Map<String, Map<String, String>> INDEX_DESC_TW_MAP;\n\tpublic static final Map<String, Map<String, String>> INDEX_DETAIL_CN_MAP;\n\tpublic static final Map<String, Map<String, String>> INDEX_DETAIL_EN_MAP;\n\tpublic static final Map<String, Map<String, Map<String, String>>> INDEX_DETAIL_LANGUAGE_MAP;\n\tpublic static final Map<String, Map<String, String>> INDEX_DETAIL_TW_MAP;\n\tpublic static final Map<String, String> INDEX_EN_MAP;\n\tpublic static final Map<String, Map<String, String>> INDEX_LANGUAGE_MAP;\n\tpublic static final Map<String, String> INDEX_OLD;\n\tpublic static final Map<String, String> INDEX_TW_MAP;\n\tpublic static final Map<String, Integer> INDEX_TYPE;\n\tpublic static final Integer NO_VALUE_FLAG = Integer.valueOf(-999);\n\tpublic static final Map<Integer, String> OLD_ALERM_LEVEL_EN_MAP;\n\tpublic static final Map<String, String> PROVINCE_FAKE_CITY_MAP;\n\tpublic static final List<String> SURPORTTED_LANGUAGE_LIST;\n\tpublic static final Map<String, String> TRANSFER_LANGUAGE_MAP = new HashMap<String, String>();\n\tpublic static final String TRAVEL_INDEX = \"旅游指数\";\n\tpublic static final Map<String, String> TRAVEL_INDEX_DESC_CN_MAP;\n\tpublic static final Map<String, String> TRAVEL_INDEX_DESC_EN_MAP;\n\tpublic static final Map<String, Map<String, String>> TRAVEL_INDEX_DESC_LANGUAGE_MAP;\n\tpublic static final Map<String, String> TRAVEL_INDEX_DESC_TW_MAP;\n\tpublic static final Map<String, String> TRAVEL_INDEX_DETAIL_CN_MAP;\n\tpublic static final Map<String, String> TRAVEL_INDEX_DETAIL_EN_MAP;\n\tpublic static final Map<String, Map<String, String>> TRAVEL_INDEX_DETAIL_LANGUAGE_MAP;\n\tpublic static final Map<String, String> TRAVEL_INDEX_DETAIL_TW_MAP;\n\tpublic static final String ULTRAVIOLET_48H_INDEX = \"48小时紫外线指数\";\n\tpublic static final String ULTRAVIOLET_INDEX = \"紫外线指数\";\n\tpublic static final Map<String, String> ULTRAVIOLET_INDEX_DESC_CN_MAP;\n\tpublic static final Map<String, String> ULTRAVIOLET_INDEX_DESC_EN_MAP;\n\tpublic static final Map<String, Map<String, String>> ULTRAVIOLET_INDEX_DESC_LANGUAGE_MAP;\n\tpublic static final Map<String, String> ULTRAVIOLET_INDEX_DESC_TW_MAP;\n\tpublic static final Map<String, String> ULTRAVIOLET_INDEX_DETAIL_CN_MAP;\n\tpublic static final Map<String, String> ULTRAVIOLET_INDEX_DETAIL_EN_MAP;\n\tpublic static final Map<String, Map<String, String>> ULTRAVIOLET_INDEX_DETAIL_LANGUAGE_MAP;\n\tpublic static final Map<String, String> ULTRAVIOLET_INDEX_DETAIL_TW_MAP;\n\tpublic static final Map<String, Integer> WEATHER_ANIMATION_MAP;\n\tpublic static final Map<String, String> WEATHER_TYPE_CN_MAP;\n\tpublic static final Map<String, String> WEATHER_TYPE_EN_MAP;\n\tpublic static final Map<String, Map<String, String>> WEATHER_TYPE_LANGUAGE_MAP;\n\tpublic static final Map<String, String> WEATHER_TYPE_TW_MAP;\n\tpublic static final Map<String, String> WIND_LEVEL_CN_MAP;\n\tpublic static final Map<String, String> WIND_LEVEL_DETAIL_CN_MAP;\n\tpublic static final Map<String, String> WIND_LEVEL_DETAIL_EN_MAP;\n\tpublic static final Map<String, Map<String, String>> WIND_LEVEL_DETAIL_LANGUAGE_MAP;\n\tpublic static final Map<String, String> WIND_LEVEL_DETAIL_TW_MAP;\n\tpublic static final Map<String, String> WIND_LEVEL_EN_CN_MAP;\n\tpublic static final Map<String, String> WIND_LEVEL_EN_MAP;\n\tpublic static final String WIND_LEVEL_INDEX = \"风力指数\";\n\tpublic static final Map<String, String> WIND_LEVEL_KEY_CN_MAP;\n\tpublic static final Map<String, Map<String, String>> WIND_LEVEL_LANGUAGE_MAP;\n\tpublic static final Map<String, String> WIND_LEVEL_TW_MAP;\n\tpublic static final Map<String, String> WIND_TYPE_CN_MAP;\n\tpublic static final Map<String, String> WIND_TYPE_CONNECTTOR_LANGUAGE_MAP;\n\tpublic static final Map<String, String> WIND_TYPE_EN_MAP;\n\tpublic static final Map<String, Map<String, String>> WIND_TYPE_LANGUAGE_MAP;\n\tpublic static final String WIND_TYPE_NULL = \"0\";\n\tpublic static final Map<String, String> WIND_TYPE_TW_MAP;\n\n\tstatic {\n\t\tTRANSFER_LANGUAGE_MAP\n\t\t\t\t.put(Locale.US.toString().toLowerCase(), \" later \");\n\t\tTRANSFER_LANGUAGE_MAP.put(Locale.CHINA.toString().toLowerCase(), \"转\");\n\t\tTRANSFER_LANGUAGE_MAP.put(Locale.TAIWAN.toString().toLowerCase(), \"轉\");\n\t\tCHINA_LANGUAGE_MAP = new HashMap();\n\t\tCHINA_LANGUAGE_MAP.put(Locale.US.toString().toLowerCase(), \"China\");\n\t\tCHINA_LANGUAGE_MAP.put(Locale.CHINA.toString().toLowerCase(), \"中国\");\n\t\tCHINA_LANGUAGE_MAP.put(Locale.TAIWAN.toString().toLowerCase(), \"中國\");\n\t\tWEATHER_TYPE_CN_MAP = new HashMap();\n\t\tWEATHER_TYPE_EN_MAP = new HashMap();\n\t\tWEATHER_TYPE_TW_MAP = new HashMap();\n\t\tWEATHER_TYPE_LANGUAGE_MAP = new HashMap();\n\t\tWEATHER_TYPE_LANGUAGE_MAP.put(Locale.US.toString().toLowerCase(),\n\t\t\t\tWEATHER_TYPE_EN_MAP);\n\t\tWEATHER_TYPE_LANGUAGE_MAP.put(Locale.CHINA.toString().toLowerCase(),\n\t\t\t\tWEATHER_TYPE_CN_MAP);\n\t\tWEATHER_TYPE_LANGUAGE_MAP.put(Locale.TAIWAN.toString().toLowerCase(),\n\t\t\t\tWEATHER_TYPE_TW_MAP);\n\t\tCN_WEATHER_TYPE_MAP = new HashMap();\n\t\tEN_WEATHER_TYPE_MAP = new HashMap();\n\t\tWEATHER_TYPE_CN_MAP.put(\"00\", \"晴\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"01\", \"多云\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"02\", \"阴\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"03\", \"阵雨\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"04\", \"雷阵雨\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"05\", \"雷阵雨伴有冰雹\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"06\", \"雨夹雪\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"07\", \"小雨\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"08\", \"中雨\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"09\", \"大雨\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"10\", \"暴雨\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"11\", \"大暴雨\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"12\", \"特大暴雨\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"13\", \"阵雪\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"14\", \"小雪\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"15\", \"中雪\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"16\", \"大雪\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"17\", \"暴雪\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"18\", \"雾\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"19\", \"冻雨\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"20\", \"沙尘暴\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"21\", \"小到中雨\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"22\", \"中到大雨\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"23\", \"大到暴雨\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"24\", \"暴雨到大暴雨\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"25\", \"大暴雨到特大暴雨\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"26\", \"小到中雪\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"27\", \"中到大雪\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"28\", \"大到暴雪\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"29\", \"浮尘\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"30\", \"扬沙\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"31\", \"强沙尘暴\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"53\", \"霾\");\n\t\tWEATHER_TYPE_CN_MAP.put(\"99\", \"无\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"00\", \"Sunny\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"01\", \"Cloudy\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"02\", \"Overcast\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"03\", \"Shower\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"04\", \"Thunderstorm\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"05\", \"Thunderstorm with hail\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"06\", \"Sleet\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"07\", \"Light rain\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"08\", \"Moderate rain\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"09\", \"Heavy rain\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"10\", \"Storm\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"11\", \"Heavy storm\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"12\", \"Severe storm\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"13\", \"Snow flurries\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"14\", \"Light snow\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"15\", \"Moderate snow\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"16\", \"Heavy snow\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"17\", \"Blizzard\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"18\", \"Foggy\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"19\", \"Freezing rain\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"20\", \"Duststorm\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"21\", \"Light to moderate rain\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"22\", \"Moderate to heavy rain\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"23\", \"Heavy rain to storm\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"24\", \"Storm to heavy storm\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"25\", \"Heavy to severe storm\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"26\", \"Light to moderate snow\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"27\", \"Moderate to heavy snow\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"28\", \"Heavy snow to blizzard\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"29\", \"Dust\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"30\", \"Sand\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"31\", \"Sandstorm\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"53\", \"Haze\");\n\t\tWEATHER_TYPE_EN_MAP.put(\"99\", \"Unknown\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"00\", \"晴\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"01\", \"多雲\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"02\", \"陰\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"03\", \"陣雨\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"04\", \"雷陣雨\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"05\", \"雷陣雨伴有冰雹\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"06\", \"雨夾雪\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"07\", \"小雨\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"08\", \"中雨\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"09\", \"大雨\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"10\", \"暴雨\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"11\", \"大暴雨\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"12\", \"特大暴雨\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"13\", \"陣雪\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"14\", \"小雪\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"15\", \"中雪\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"16\", \"大雪\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"17\", \"暴雪\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"18\", \"霧\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"19\", \"冻雨\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"20\", \"沙塵暴\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"21\", \"小到中雨\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"22\", \"中到大雨\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"23\", \"大到暴雨\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"24\", \"暴雨到大暴雨\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"25\", \"大暴雨到特大暴雨\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"26\", \"小到中雪\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"27\", \"中到大雪\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"28\", \"大到暴雪\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"29\", \"浮塵\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"30\", \"揚沙\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"31\", \"強沙塵暴\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"53\", \"霾\");\n\t\tWEATHER_TYPE_TW_MAP.put(\"99\", \"无\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"晴\", \"00\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"多云\", \"01\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"阴\", \"02\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"阵雨\", \"03\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"雷阵雨\", \"04\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"雷阵雨伴有冰雹\", \"05\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"雨夹雪\", \"06\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"小雨\", \"07\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"中雨\", \"08\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"大雨\", \"09\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"暴雨\", \"10\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"大暴雨\", \"11\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"特大暴雨\", \"12\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"阵雪\", \"13\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"小雪\", \"14\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"中雪\", \"15\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"大雪\", \"16\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"暴雪\", \"17\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"雾\", \"18\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"冻雨\", \"19\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"沙尘暴\", \"20\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"小到中雨\", \"21\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"中到大雨\", \"22\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"大到暴雨\", \"23\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"暴雨到大暴雨\", \"24\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"大暴雨到特大暴雨\", \"25\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"小到中雪\", \"26\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"中到大雪\", \"27\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"大到暴雪\", \"28\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"浮尘\", \"29\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"扬沙\", \"30\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"强沙尘暴\", \"31\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"霾\", \"53\");\n\t\tCN_WEATHER_TYPE_MAP.put(\"无\", \"99\");\n\t\tALERM_LEVEL_CN_MAP = new HashMap();\n\t\tALERM_LEVEL_EN_MAP = new HashMap();\n\t\tCN_ALERM_LEVEL_MAP = new HashMap();\n\t\tEN_ALERM_LEVEL_MAP = new HashMap();\n\t\tOLD_ALERM_LEVEL_EN_MAP = new HashMap();\n\t\tCN_ALERM_LEVEL_MAP.put(\"蓝色\", \"01\");\n\t\tCN_ALERM_LEVEL_MAP.put(\"黄色\", \"02\");\n\t\tCN_ALERM_LEVEL_MAP.put(\"橙色\", \"03\");\n\t\tCN_ALERM_LEVEL_MAP.put(\"红色\", \"04\");\n\t\tEN_ALERM_LEVEL_MAP.put(\"blue\", \"01\");\n\t\tEN_ALERM_LEVEL_MAP.put(\"yellow\", \"02\");\n\t\tEN_ALERM_LEVEL_MAP.put(\"orange\", \"03\");\n\t\tEN_ALERM_LEVEL_MAP.put(\"re\", \"04\");\n\t\tALERM_LEVEL_CN_MAP.put(\"01\", \"蓝色\");\n\t\tALERM_LEVEL_CN_MAP.put(\"02\", \"黄色\");\n\t\tALERM_LEVEL_CN_MAP.put(\"03\", \"橙色\");\n\t\tALERM_LEVEL_CN_MAP.put(\"04\", \"红色\");\n\t\tALERM_LEVEL_EN_MAP.put(\"01\", \"blue\");\n\t\tALERM_LEVEL_EN_MAP.put(\"02\", \"yellow\");\n\t\tALERM_LEVEL_EN_MAP.put(\"03\", \"orange\");\n\t\tALERM_LEVEL_EN_MAP.put(\"04\", \"red\");\n\t\tOLD_ALERM_LEVEL_EN_MAP.put(Integer.valueOf(1), \"blue\");\n\t\tOLD_ALERM_LEVEL_EN_MAP.put(Integer.valueOf(2), \"yellow\");\n\t\tOLD_ALERM_LEVEL_EN_MAP.put(Integer.valueOf(3), \"orange\");\n\t\tOLD_ALERM_LEVEL_EN_MAP.put(Integer.valueOf(4), \"red\");\n\t\tWIND_TYPE_CN_MAP = new HashMap<String, String>();\n\t\tWIND_TYPE_EN_MAP = new HashMap<String, String>();\n\t\tWIND_TYPE_TW_MAP = new HashMap<String, String>();\n\t\tCN_WIND_TYPE_MAP = new HashMap<String, String>();\n\t\tEN_WIND_TYPE_MAP = new HashMap<String, String>();\n\t\tWIND_TYPE_CONNECTTOR_LANGUAGE_MAP = new HashMap<String, String>();\n\t\tWIND_TYPE_CONNECTTOR_LANGUAGE_MAP.put(Locale.US.toString()\n\t\t\t\t.toLowerCase(), \", speed \");\n\t\tWIND_TYPE_CONNECTTOR_LANGUAGE_MAP.put(Locale.CHINA.toString()\n\t\t\t\t.toLowerCase(), \"，风力\");\n\t\tWIND_TYPE_CONNECTTOR_LANGUAGE_MAP.put(Locale.TAIWAN.toString()\n\t\t\t\t.toLowerCase(), \"，風力\");\n\t\tWIND_TYPE_LANGUAGE_MAP = new HashMap<String, Map<String, String>>();\n\t\tWIND_TYPE_LANGUAGE_MAP.put(Locale.US.toString().toLowerCase(),\n\t\t\t\tWIND_TYPE_EN_MAP);\n\t\tWIND_TYPE_LANGUAGE_MAP.put(Locale.CHINA.toString().toLowerCase(),\n\t\t\t\tWIND_TYPE_CN_MAP);\n\t\tWIND_TYPE_LANGUAGE_MAP.put(Locale.TAIWAN.toString().toLowerCase(),\n\t\t\t\tWIND_TYPE_TW_MAP);\n\t\tWIND_TYPE_CN_MAP.put(\"0\", \"微风\");\n\t\tWIND_TYPE_CN_MAP.put(\"1\", \"东北风\");\n\t\tWIND_TYPE_CN_MAP.put(\"2\", \"东风\");\n\t\tWIND_TYPE_CN_MAP.put(\"3\", \"东南风\");\n\t\tWIND_TYPE_CN_MAP.put(\"4\", \"南风\");\n\t\tWIND_TYPE_CN_MAP.put(\"5\", \"西南风\");\n\t\tWIND_TYPE_CN_MAP.put(\"6\", \"西风\");\n\t\tWIND_TYPE_CN_MAP.put(\"7\", \"西北风\");\n\t\tWIND_TYPE_CN_MAP.put(\"8\", \"北风\");\n\t\tWIND_TYPE_CN_MAP.put(\"9\", \"旋转风\");\n\t\tWIND_TYPE_EN_MAP.put(\"0\", \"No wind\");\n\t\tWIND_TYPE_EN_MAP.put(\"1\", \"Northeast\");\n\t\tWIND_TYPE_EN_MAP.put(\"2\", \"East\");\n\t\tWIND_TYPE_EN_MAP.put(\"3\", \"Southeast\");\n\t\tWIND_TYPE_EN_MAP.put(\"4\", \"South\");\n\t\tWIND_TYPE_EN_MAP.put(\"5\", \"Southwest\");\n\t\tWIND_TYPE_EN_MAP.put(\"6\", \"West\");\n\t\tWIND_TYPE_EN_MAP.put(\"7\", \"Northwest\");\n\t\tWIND_TYPE_EN_MAP.put(\"8\", \"North\");\n\t\tWIND_TYPE_EN_MAP.put(\"9\", \"Variable\");\n\t\tWIND_TYPE_TW_MAP.put(\"0\", \"微風\");\n\t\tWIND_TYPE_TW_MAP.put(\"1\", \"東北風\");\n\t\tWIND_TYPE_TW_MAP.put(\"2\", \"東風\");\n\t\tWIND_TYPE_TW_MAP.put(\"3\", \"東南風\");\n\t\tWIND_TYPE_TW_MAP.put(\"4\", \"南風\");\n\t\tWIND_TYPE_TW_MAP.put(\"5\", \"西南風\");\n\t\tWIND_TYPE_TW_MAP.put(\"6\", \"西風\");\n\t\tWIND_TYPE_TW_MAP.put(\"7\", \"西北風\");\n\t\tWIND_TYPE_TW_MAP.put(\"8\", \"北風\");\n\t\tWIND_TYPE_TW_MAP.put(\"9\", \"旋轉風\");\n\t\tCN_WIND_TYPE_MAP.put(\"微风\", \"0\");\n\t\tCN_WIND_TYPE_MAP.put(\"无持续风向\", \"0\");\n\t\tCN_WIND_TYPE_MAP.put(\"东北风\", \"1\");\n\t\tCN_WIND_TYPE_MAP.put(\"东风\", \"2\");\n\t\tCN_WIND_TYPE_MAP.put(\"东南风\", \"3\");\n\t\tCN_WIND_TYPE_MAP.put(\"南风\", \"4\");\n\t\tCN_WIND_TYPE_MAP.put(\"西南风\", \"5\");\n\t\tCN_WIND_TYPE_MAP.put(\"西风\", \"6\");\n\t\tCN_WIND_TYPE_MAP.put(\"西北风\", \"7\");\n\t\tCN_WIND_TYPE_MAP.put(\"北风\", \"8\");\n\t\tCN_WIND_TYPE_MAP.put(\"旋转风\", \"9\");\n\t\tEN_WIND_TYPE_MAP.put(\"No wind\", \"0\");\n\t\tEN_WIND_TYPE_MAP.put(\"Northeast\", \"1\");\n\t\tEN_WIND_TYPE_MAP.put(\"East\", \"2\");\n\t\tEN_WIND_TYPE_MAP.put(\"Southeast\", \"3\");\n\t\tEN_WIND_TYPE_MAP.put(\"South\", \"4\");\n\t\tEN_WIND_TYPE_MAP.put(\"Southwest\", \"5\");\n\t\tEN_WIND_TYPE_MAP.put(\"West\", \"6\");\n\t\tEN_WIND_TYPE_MAP.put(\"Northwest\", \"7\");\n\t\tEN_WIND_TYPE_MAP.put(\"North\", \"8\");\n\t\tEN_WIND_TYPE_MAP.put(\"Whirl wind\", \"9\");\n\t\tWIND_LEVEL_KEY_CN_MAP = new HashMap<String, String>();\n\t\tWIND_LEVEL_KEY_CN_MAP.put(\"0\", \"小于3级\");\n\t\tWIND_LEVEL_KEY_CN_MAP.put(\"1\", \"3-4级\");\n\t\tWIND_LEVEL_KEY_CN_MAP.put(\"2\", \"4-5级\");\n\t\tWIND_LEVEL_KEY_CN_MAP.put(\"3\", \"5-6级\");\n\t\tWIND_LEVEL_KEY_CN_MAP.put(\"4\", \"6-7级\");\n\t\tWIND_LEVEL_KEY_CN_MAP.put(\"5\", \"7-8级\");\n\t\tWIND_LEVEL_KEY_CN_MAP.put(\"6\", \"8-9级\");\n\t\tWIND_LEVEL_KEY_CN_MAP.put(\"7\", \"9-10级\");\n\t\tWIND_LEVEL_KEY_CN_MAP.put(\"8\", \"10-11级\");\n\t\tWIND_LEVEL_KEY_CN_MAP.put(\"9\", \"11-12级\");\n\t\tWIND_LEVEL_EN_MAP = new HashMap<String, String>();\n\t\tWIND_LEVEL_EN_CN_MAP = new HashMap<String, String>();\n\t\tWIND_LEVEL_TW_MAP = new HashMap<String, String>();\n\t\tWIND_LEVEL_CN_MAP = new HashMap<String, String>();\n\t\tWIND_LEVEL_LANGUAGE_MAP = new HashMap<String, Map<String, String>>();\n\t\tWIND_LEVEL_LANGUAGE_MAP.put(Locale.US.toString().toLowerCase(),\n\t\t\t\tWIND_LEVEL_EN_MAP);\n\t\tWIND_LEVEL_LANGUAGE_MAP.put(Locale.CHINA.toString().toLowerCase(),\n\t\t\t\tWIND_LEVEL_CN_MAP);\n\t\tWIND_LEVEL_LANGUAGE_MAP.put(Locale.TAIWAN.toString().toLowerCase(),\n\t\t\t\tWIND_LEVEL_TW_MAP);\n\t\tWIND_LEVEL_CN_MAP.put(\"0级\", \"小于3级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"1级\", \"1级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"2级\", \"2级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"3级\", \"3级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"4级\", \"4级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"5级\", \"5级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"6级\", \"6级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"7级\", \"7级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"8级\", \"8级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"9级\", \"9级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"10级\", \"10级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"11级\", \"11级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"12级\", \"12级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"小于3级\", \"小于3级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"3-4级\", \"3-4级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"4-5级\", \"4-5级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"5-6级\", \"5-6级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"6-7级\", \"6-7级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"7-8级\", \"7-8级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"8-9级\", \"8-9级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"9-10级\", \"9-10级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"10-11级\", \"10-11级\");\n\t\tWIND_LEVEL_CN_MAP.put(\"11-12级\", \"11-12级\");\n\t\tWIND_LEVEL_EN_MAP.put(\"0级\", \"< 16km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"1级\", \"< 16km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"2级\", \"< 16km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"3级\", \"16km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"4级\", \"27km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"5级\", \"40km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"6级\", \"55km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"7级\", \"69km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"8级\", \"87km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"9级\", \"105km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"10级\", \"124km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"11级\", \"143km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"12级\", \"164km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"小于3级\", \"< 16km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"3-4级\", \"16-27km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"4-5级\", \"27-40km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"5-6级\", \"40-55km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"6-7级\", \"55-69km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"7-8级\", \"69-87km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"8-9级\", \"87-105km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"9-10级\", \"105-124km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"10-11级\", \"124-143km/h\");\n\t\tWIND_LEVEL_EN_MAP.put(\"11-12级\", \"143-164km/h\");\n\t\tWIND_LEVEL_TW_MAP.put(\"0级\", \"小于3級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"1级\", \"1級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"2级\", \"2級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"3级\", \"3級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"4级\", \"4級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"5级\", \"5級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"6级\", \"6級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"7级\", \"7級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"8级\", \"8級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"9级\", \"9級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"10级\", \"10級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"11级\", \"11級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"12级\", \"12級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"小于3级\", \"小于3級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"3-4级\", \"3-4級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"4-5级\", \"4-5級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"5-6级\", \"5-6級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"6-7级\", \"6-7級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"7-8级\", \"7-8級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"8-9级\", \"8-9級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"9-10级\", \"9-10級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"10-11级\", \"10-11級\");\n\t\tWIND_LEVEL_TW_MAP.put(\"11-12级\", \"11-12級\");\n\t\tWIND_LEVEL_DETAIL_EN_MAP = new HashMap();\n\t\tWIND_LEVEL_DETAIL_TW_MAP = new HashMap();\n\t\tWIND_LEVEL_DETAIL_CN_MAP = new HashMap();\n\t\tWIND_LEVEL_DETAIL_LANGUAGE_MAP = new HashMap();\n\t\tWIND_LEVEL_DETAIL_LANGUAGE_MAP.put(Locale.US.toString().toLowerCase(),\n\t\t\t\tWIND_LEVEL_DETAIL_EN_MAP);\n\t\tWIND_LEVEL_DETAIL_LANGUAGE_MAP.put(Locale.CHINA.toString()\n\t\t\t\t.toLowerCase(), WIND_LEVEL_DETAIL_CN_MAP);\n\t\tWIND_LEVEL_DETAIL_LANGUAGE_MAP.put(Locale.TAIWAN.toString()\n\t\t\t\t.toLowerCase(), WIND_LEVEL_DETAIL_TW_MAP);\n\t\tWIND_LEVEL_DETAIL_CN_MAP.put(\"小于3级\", \"微风拂面，感觉不错，建议适当地去户外放松一下。\");\n\t\tWIND_LEVEL_DETAIL_CN_MAP.put(\"3-4级\", \"今天风有点大，外出的话，请做好防护措施。\");\n\t\tWIND_LEVEL_DETAIL_CN_MAP.put(\"4-5级\", \"今天风有点大，外出的话，请做好防护措施。\");\n\t\tWIND_LEVEL_DETAIL_CN_MAP.put(\"5-6级\", \"今天风有点大，外出的话，请做好防护措施。\");\n\t\tWIND_LEVEL_DETAIL_CN_MAP.put(\"6-7级\", \"今天风有点大，外出的话，请做好防护措施。\");\n\t\tWIND_LEVEL_DETAIL_CN_MAP.put(\"7-8级\", \"今天风有点大，外出的话，请做好防护措施。\");\n\t\tWIND_LEVEL_DETAIL_CN_MAP.put(\"8-9级\", \"今天风有点大，外出的话，请做好防护措施。\");\n\t\tWIND_LEVEL_DETAIL_CN_MAP.put(\"9-10级\", \"今天风有点大，外出的话，请做好防护措施。\");\n\t\tWIND_LEVEL_DETAIL_CN_MAP.put(\"10-11级\", \"今天风有点大，外出的话，请做好防护措施。\");\n\t\tWIND_LEVEL_DETAIL_CN_MAP.put(\"11-12级\", \"今天风有点大，外出的话，请做好防护措施。\");\n\t\tWIND_LEVEL_DETAIL_EN_MAP.put(\"小于3级\", \"Comfortable\");\n\t\tWIND_LEVEL_DETAIL_EN_MAP.put(\"3-4级\", \"Wear layers!\");\n\t\tWIND_LEVEL_DETAIL_EN_MAP.put(\"4-5级\", \"Wear layers!\");\n\t\tWIND_LEVEL_DETAIL_EN_MAP.put(\"5-6级\", \"Wear layers!\");\n\t\tWIND_LEVEL_DETAIL_EN_MAP.put(\"6-7级\", \"Wear layers!\");\n\t\tWIND_LEVEL_DETAIL_EN_MAP.put(\"7-8级\", \"Wear layers!\");\n\t\tWIND_LEVEL_DETAIL_EN_MAP.put(\"8-9级\", \"Wear layers!\");\n\t\tWIND_LEVEL_DETAIL_EN_MAP.put(\"9-10级\", \"Wear layers!\");\n\t\tWIND_LEVEL_DETAIL_EN_MAP.put(\"10-11级\", \"Wear layers!\");\n\t\tWIND_LEVEL_DETAIL_EN_MAP.put(\"11-12级\", \"Wear layers!\");\n\t\tWIND_LEVEL_DETAIL_TW_MAP.put(\"小于3级\", \"微風拂面，感覺不錯，建議適當地去戶外放鬆一下。\");\n\t\tWIND_LEVEL_DETAIL_TW_MAP.put(\"3-4级\", \"今天風有點大，外出的話，請做好防護措施。\");\n\t\tWIND_LEVEL_DETAIL_TW_MAP.put(\"4-5级\", \"今天風有點大，外出的話，請做好防護措施。\");\n\t\tWIND_LEVEL_DETAIL_TW_MAP.put(\"5-6级\", \"今天風有點大，外出的話，請做好防護措施。\");\n\t\tWIND_LEVEL_DETAIL_TW_MAP.put(\"6-7级\", \"今天風有點大，外出的話，請做好防護措施。\");\n\t\tWIND_LEVEL_DETAIL_TW_MAP.put(\"7-8级\", \"今天風有點大，外出的話，請做好防護措施。\");\n\t\tWIND_LEVEL_DETAIL_TW_MAP.put(\"8-9级\", \"今天風有點大，外出的話，請做好防護措施。\");\n\t\tWIND_LEVEL_DETAIL_TW_MAP.put(\"9-10级\", \"今天風有點大，外出的話，請做好防護措施。\");\n\t\tWIND_LEVEL_DETAIL_TW_MAP.put(\"10-11级\", \"今天風有點大，外出的話，請做好防護措施。\");\n\t\tWIND_LEVEL_DETAIL_TW_MAP.put(\"11-12级\", \"今天風有點大，外出的話，請做好防護措施。\");\n\t\tWEATHER_ANIMATION_MAP = new HashMap<String, Integer>();\n\t\tWEATHER_ANIMATION_MAP.put(\"晴\", Integer.valueOf(0));\n\t\tWEATHER_ANIMATION_MAP.put(\"多云\", Integer.valueOf(1));\n\t\tWEATHER_ANIMATION_MAP.put(\"阴\", Integer.valueOf(2));\n\t\tWEATHER_ANIMATION_MAP.put(\"雾\", Integer.valueOf(3));\n\t\tWEATHER_ANIMATION_MAP.put(\"特大暴雨\", Integer.valueOf(4));\n\t\tWEATHER_ANIMATION_MAP.put(\"大暴雨\", Integer.valueOf(5));\n\t\tWEATHER_ANIMATION_MAP.put(\"暴雨\", Integer.valueOf(6));\n\t\tWEATHER_ANIMATION_MAP.put(\"雷阵雨\", Integer.valueOf(7));\n\t\tWEATHER_ANIMATION_MAP.put(\"阵雨\", Integer.valueOf(8));\n\t\tWEATHER_ANIMATION_MAP.put(\"大雨\", Integer.valueOf(9));\n\t\tWEATHER_ANIMATION_MAP.put(\"中雨\", Integer.valueOf(10));\n\t\tWEATHER_ANIMATION_MAP.put(\"小雨\", Integer.valueOf(11));\n\t\tWEATHER_ANIMATION_MAP.put(\"雨夹雪\", Integer.valueOf(12));\n\t\tWEATHER_ANIMATION_MAP.put(\"暴雪\", Integer.valueOf(13));\n\t\tWEATHER_ANIMATION_MAP.put(\"阵雪\", Integer.valueOf(14));\n\t\tWEATHER_ANIMATION_MAP.put(\"大雪\", Integer.valueOf(15));\n\t\tWEATHER_ANIMATION_MAP.put(\"中雪\", Integer.valueOf(16));\n\t\tWEATHER_ANIMATION_MAP.put(\"小雪\", Integer.valueOf(17));\n\t\tWEATHER_ANIMATION_MAP.put(\"强沙尘暴\", Integer.valueOf(18));\n\t\tWEATHER_ANIMATION_MAP.put(\"沙尘暴\", Integer.valueOf(19));\n\t\tWEATHER_ANIMATION_MAP.put(\"沙尘\", Integer.valueOf(20));\n\t\tWEATHER_ANIMATION_MAP.put(\"扬沙\", Integer.valueOf(21));\n\t\tWEATHER_ANIMATION_MAP.put(\"冰雹\", Integer.valueOf(22));\n\t\tWEATHER_ANIMATION_MAP.put(\"浮尘\", Integer.valueOf(23));\n\t\tWEATHER_ANIMATION_MAP.put(\"霾\", Integer.valueOf(24));\n\t\tINDEX_TYPE = new HashMap<String, Integer>();\n\t\tINDEX_TYPE.put(\"风力指数\", Integer.valueOf(0));\n\t\tINDEX_TYPE.put(\"紫外线指数\", Integer.valueOf(1));\n\t\tINDEX_TYPE.put(\"穿衣指数\", Integer.valueOf(2));\n\t\tINDEX_TYPE.put(\"舒适度指数\", Integer.valueOf(3));\n\t\tINDEX_TYPE.put(\"洗车指数\", Integer.valueOf(4));\n\t\tINDEX_TYPE.put(\"晾晒指数\", Integer.valueOf(5));\n\t\tINDEX_TYPE.put(\"晨练指数\", Integer.valueOf(6));\n\t\tINDEX_TYPE.put(\"旅游指数\", Integer.valueOf(7));\n\t\tINDEX_TYPE.put(\"48小时穿衣指数\", Integer.valueOf(8));\n\t\tINDEX_TYPE.put(\"48小时紫外线指数\", Integer.valueOf(9));\n\t\tINDEX_OLD = new LinkedHashMap<String, String>();\n\t\tINDEX_OLD.put(\"紫外线指数\", \"index_uv\");\n\t\tINDEX_OLD.put(\"穿衣指数\", \"index\");\n\t\tINDEX_OLD.put(\"舒适度指数\", \"index_co\");\n\t\tINDEX_OLD.put(\"洗车指数\", \"index_xc\");\n\t\tINDEX_OLD.put(\"晾晒指数\", \"index_ls\");\n\t\tINDEX_OLD.put(\"晨练指数\", \"index_cl\");\n\t\tINDEX_OLD.put(\"旅游指数\", \"index_tr\");\n\t\tINDEX_OLD.put(\"48小时穿衣指数\", \"index48\");\n\t\tINDEX_OLD.put(\"48小时紫外线指数\", \"index48_uv\");\n\t\tEXERCISE_INDEX_DESC_EN_MAP = new HashMap<String, String>();\n\t\tEXERCISE_INDEX_DESC_EN_MAP.put(\"适宜\", \"OK\");\n\t\tEXERCISE_INDEX_DESC_EN_MAP.put(\"较适宜\", \"Good\");\n\t\tEXERCISE_INDEX_DESC_EN_MAP.put(\"较不宜\", \"Avoid if possible\");\n\t\tEXERCISE_INDEX_DESC_EN_MAP.put(\"不宜\", \"Not recommended\");\n\t\tEXERCISE_INDEX_DESC_EN_MAP.put(\"极适宜\", \"Ideal\");\n\t\tEXERCISE_INDEX_DESC_CN_MAP = new HashMap<String, String>();\n\t\tEXERCISE_INDEX_DESC_CN_MAP.put(\"适宜\", \"适宜\");\n\t\tEXERCISE_INDEX_DESC_CN_MAP.put(\"较适宜\", \"较适宜\");\n\t\tEXERCISE_INDEX_DESC_CN_MAP.put(\"较不宜\", \"较不宜\");\n\t\tEXERCISE_INDEX_DESC_CN_MAP.put(\"不宜\", \"不宜\");\n\t\tEXERCISE_INDEX_DESC_CN_MAP.put(\"极适宜\", \"极适宜\");\n\t\tEXERCISE_INDEX_DESC_TW_MAP = new HashMap<String, String>();\n\t\tEXERCISE_INDEX_DESC_TW_MAP.put(\"适宜\", \"適宜\");\n\t\tEXERCISE_INDEX_DESC_TW_MAP.put(\"较适宜\", \"較適宜\");\n\t\tEXERCISE_INDEX_DESC_TW_MAP.put(\"较不宜\", \"較不宜\");\n\t\tEXERCISE_INDEX_DESC_TW_MAP.put(\"不宜\", \"不宜\");\n\t\tEXERCISE_INDEX_DESC_TW_MAP.put(\"极适宜\", \"極適宜\");\n\t\tCARWASH_INDEX_DESC_EN_MAP = new HashMap<String, String>();\n\t\tCARWASH_INDEX_DESC_EN_MAP.put(\"适宜\", \"OK\");\n\t\tCARWASH_INDEX_DESC_EN_MAP.put(\"较适宜\", \"Good\");\n\t\tCARWASH_INDEX_DESC_EN_MAP.put(\"较不宜\", \"Avoid if possible\");\n\t\tCARWASH_INDEX_DESC_EN_MAP.put(\"不宜\", \"Not recommended\");\n\t\tCARWASH_INDEX_DESC_EN_MAP.put(\"极适宜\", \"Ideal\");\n\t\tCARWASH_INDEX_DESC_CN_MAP = new HashMap<String, String>();\n\t\tCARWASH_INDEX_DESC_CN_MAP.put(\"适宜\", \"适宜\");\n\t\tCARWASH_INDEX_DESC_CN_MAP.put(\"较适宜\", \"较适宜\");\n\t\tCARWASH_INDEX_DESC_CN_MAP.put(\"较不宜\", \"较不宜\");\n\t\tCARWASH_INDEX_DESC_CN_MAP.put(\"不宜\", \"不宜\");\n\t\tCARWASH_INDEX_DESC_CN_MAP.put(\"极适宜\", \"极适宜\");\n\t\tCARWASH_INDEX_DESC_TW_MAP = new HashMap<String, String>();\n\t\tCARWASH_INDEX_DESC_TW_MAP.put(\"适宜\", \"適宜\");\n\t\tCARWASH_INDEX_DESC_TW_MAP.put(\"较适宜\", \"較適宜\");\n\t\tCARWASH_INDEX_DESC_TW_MAP.put(\"较不宜\", \"較不宜\");\n\t\tCARWASH_INDEX_DESC_TW_MAP.put(\"不宜\", \"不宜\");\n\t\tCARWASH_INDEX_DESC_TW_MAP.put(\"极适宜\", \"極適宜\");\n\t\tAIRCURE_INDEX_DESC_EN_MAP = new HashMap<String, String>();\n\t\tAIRCURE_INDEX_DESC_EN_MAP.put(\"适宜\", \"OK\");\n\t\tAIRCURE_INDEX_DESC_EN_MAP.put(\"较适宜\", \"Good\");\n\t\tAIRCURE_INDEX_DESC_EN_MAP.put(\"不太适宜\", \"Not Good\");\n\t\tAIRCURE_INDEX_DESC_EN_MAP.put(\"较不宜\", \"Avoid if possible\");\n\t\tAIRCURE_INDEX_DESC_EN_MAP.put(\"不宜\", \"Not recommended\");\n\t\tAIRCURE_INDEX_DESC_EN_MAP.put(\"极适宜\", \"Ideal\");\n\t\tAIRCURE_INDEX_DESC_CN_MAP = new HashMap<String, String>();\n\t\tAIRCURE_INDEX_DESC_CN_MAP.put(\"适宜\", \"适宜\");\n\t\tAIRCURE_INDEX_DESC_CN_MAP.put(\"较适宜\", \"较适宜\");\n\t\tAIRCURE_INDEX_DESC_CN_MAP.put(\"不太适宜\", \"不太适宜\");\n\t\tAIRCURE_INDEX_DESC_CN_MAP.put(\"较不宜\", \"较不宜\");\n\t\tAIRCURE_INDEX_DESC_CN_MAP.put(\"不宜\", \"不宜\");\n\t\tAIRCURE_INDEX_DESC_CN_MAP.put(\"极适宜\", \"极适宜\");\n\t\tAIRCURE_INDEX_DESC_TW_MAP = new HashMap<String, String>();\n\t\tAIRCURE_INDEX_DESC_TW_MAP.put(\"适宜\", \"適宜\");\n\t\tAIRCURE_INDEX_DESC_TW_MAP.put(\"较适宜\", \"較適宜\");\n\t\tAIRCURE_INDEX_DESC_TW_MAP.put(\"不太适宜\", \"不太適宜\");\n\t\tAIRCURE_INDEX_DESC_TW_MAP.put(\"较不宜\", \"較不宜\");\n\t\tAIRCURE_INDEX_DESC_TW_MAP.put(\"不宜\", \"不宜\");\n\t\tAIRCURE_INDEX_DESC_TW_MAP.put(\"极适宜\", \"極適宜\");\n\t\tTRAVEL_INDEX_DESC_EN_MAP = new HashMap<String, String>();\n\t\tTRAVEL_INDEX_DESC_EN_MAP.put(\"适宜\", \"OK\");\n\t\tTRAVEL_INDEX_DESC_EN_MAP.put(\"较适宜\", \"Good\");\n\t\tTRAVEL_INDEX_DESC_EN_MAP.put(\"较不宜\", \"Avoid if possible\");\n\t\tTRAVEL_INDEX_DESC_EN_MAP.put(\"不宜\", \"Not recommended\");\n\t\tTRAVEL_INDEX_DESC_EN_MAP.put(\"极适宜\", \"Ideal\");\n\t\tTRAVEL_INDEX_DESC_CN_MAP = new HashMap<String, String>();\n\t\tTRAVEL_INDEX_DESC_CN_MAP.put(\"适宜\", \"适宜\");\n\t\tTRAVEL_INDEX_DESC_CN_MAP.put(\"较适宜\", \"较适宜\");\n\t\tTRAVEL_INDEX_DESC_CN_MAP.put(\"较不宜\", \"较不宜\");\n\t\tTRAVEL_INDEX_DESC_CN_MAP.put(\"不宜\", \"不宜\");\n\t\tTRAVEL_INDEX_DESC_CN_MAP.put(\"极适宜\", \"极适宜\");\n\t\tTRAVEL_INDEX_DESC_TW_MAP = new HashMap<String, String>();\n\t\tTRAVEL_INDEX_DESC_TW_MAP.put(\"适宜\", \"適宜\");\n\t\tTRAVEL_INDEX_DESC_TW_MAP.put(\"较适宜\", \"較適宜\");\n\t\tTRAVEL_INDEX_DESC_TW_MAP.put(\"较不宜\", \"較不宜\");\n\t\tTRAVEL_INDEX_DESC_TW_MAP.put(\"不宜\", \"不宜\");\n\t\tTRAVEL_INDEX_DESC_TW_MAP.put(\"极适宜\", \"極適宜\");\n\t\tULTRAVIOLET_INDEX_DESC_EN_MAP = new HashMap<String, String>();\n\t\tULTRAVIOLET_INDEX_DESC_EN_MAP.put(\"中等\", \"Moderate\");\n\t\tULTRAVIOLET_INDEX_DESC_EN_MAP.put(\"最弱\", \"Weak\");\n\t\tULTRAVIOLET_INDEX_DESC_EN_MAP.put(\"弱\", \"Medium\");\n\t\tULTRAVIOLET_INDEX_DESC_EN_MAP.put(\"强\", \"Strong\");\n\t\tULTRAVIOLET_INDEX_DESC_EN_MAP.put(\"很强\", \"Very strong\");\n\t\tULTRAVIOLET_INDEX_DESC_CN_MAP = new HashMap<String, String>();\n\t\tULTRAVIOLET_INDEX_DESC_CN_MAP.put(\"中等\", \"中等\");\n\t\tULTRAVIOLET_INDEX_DESC_CN_MAP.put(\"最弱\", \"最弱\");\n\t\tULTRAVIOLET_INDEX_DESC_CN_MAP.put(\"弱\", \"弱\");\n\t\tULTRAVIOLET_INDEX_DESC_CN_MAP.put(\"强\", \"强\");\n\t\tULTRAVIOLET_INDEX_DESC_CN_MAP.put(\"很强\", \"很强\");\n\t\tULTRAVIOLET_INDEX_DESC_TW_MAP = new HashMap<String, String>();\n\t\tULTRAVIOLET_INDEX_DESC_TW_MAP.put(\"中等\", \"最弱\");\n\t\tULTRAVIOLET_INDEX_DESC_TW_MAP.put(\"最弱\", \"最弱\");\n\t\tULTRAVIOLET_INDEX_DESC_TW_MAP.put(\"弱\", \"弱\");\n\t\tULTRAVIOLET_INDEX_DESC_TW_MAP.put(\"强\", \"強\");\n\t\tULTRAVIOLET_INDEX_DESC_TW_MAP.put(\"很强\", \"很強\");\n\t\tDRESS_INDEX_DESC_EN_MAP = new HashMap<String, String>();\n\t\tDRESS_INDEX_DESC_EN_MAP.put(\"凉\", \"Cool\");\n\t\tDRESS_INDEX_DESC_EN_MAP.put(\"舒适\", \"Comfortable\");\n\t\tDRESS_INDEX_DESC_EN_MAP.put(\"较舒适\", \"Good\");\n\t\tDRESS_INDEX_DESC_EN_MAP.put(\"炎热\", \"Very hot\");\n\t\tDRESS_INDEX_DESC_EN_MAP.put(\"热\", \"Hot\");\n\t\tDRESS_INDEX_DESC_EN_MAP.put(\"暖\", \"Warm\");\n\t\tDRESS_INDEX_DESC_EN_MAP.put(\"温凉\", \"Cool\");\n\t\tDRESS_INDEX_DESC_EN_MAP.put(\"气温较低\", \"Cooler\");\n\t\tDRESS_INDEX_DESC_EN_MAP.put(\"冷\", \"Cold\");\n\t\tDRESS_INDEX_DESC_EN_MAP.put(\"寒冷\", \"Freezing\");\n\t\tDRESS_INDEX_DESC_CN_MAP = new HashMap();\n\t\tDRESS_INDEX_DESC_CN_MAP.put(\"凉\", \"凉\");\n\t\tDRESS_INDEX_DESC_CN_MAP.put(\"舒适\", \"舒适\");\n\t\tDRESS_INDEX_DESC_CN_MAP.put(\"较舒适\", \"较舒适\");\n\t\tDRESS_INDEX_DESC_CN_MAP.put(\"炎热\", \"炎热\");\n\t\tDRESS_INDEX_DESC_CN_MAP.put(\"热\", \"热\");\n\t\tDRESS_INDEX_DESC_CN_MAP.put(\"暖\", \"暖\");\n\t\tDRESS_INDEX_DESC_CN_MAP.put(\"温凉\", \"温凉\");\n\t\tDRESS_INDEX_DESC_CN_MAP.put(\"气温较低\", \"气温较低\");\n\t\tDRESS_INDEX_DESC_CN_MAP.put(\"冷\", \"冷\");\n\t\tDRESS_INDEX_DESC_CN_MAP.put(\"寒冷\", \"寒冷\");\n\t\tDRESS_INDEX_DESC_TW_MAP = new HashMap();\n\t\tDRESS_INDEX_DESC_TW_MAP.put(\"凉\", \"涼\");\n\t\tDRESS_INDEX_DESC_TW_MAP.put(\"舒适\", \"舒適\");\n\t\tDRESS_INDEX_DESC_TW_MAP.put(\"较舒适\", \"較舒適\");\n\t\tDRESS_INDEX_DESC_TW_MAP.put(\"炎热\", \"炎熱\");\n\t\tDRESS_INDEX_DESC_TW_MAP.put(\"热\", \"熱\");\n\t\tDRESS_INDEX_DESC_TW_MAP.put(\"暖\", \"暖\");\n\t\tDRESS_INDEX_DESC_TW_MAP.put(\"温凉\", \"溫涼\");\n\t\tDRESS_INDEX_DESC_TW_MAP.put(\"气温较低\", \"氣溫較低\");\n\t\tDRESS_INDEX_DESC_TW_MAP.put(\"冷\", \"冷\");\n\t\tDRESS_INDEX_DESC_TW_MAP.put(\"寒冷\", \"寒冷\");\n\t\tCOMFORT_INDEX_DESC_EN_MAP = new HashMap();\n\t\tCOMFORT_INDEX_DESC_EN_MAP.put(\"较舒适\", \"High\");\n\t\tCOMFORT_INDEX_DESC_EN_MAP.put(\"舒适\", \"Middle\");\n\t\tCOMFORT_INDEX_DESC_EN_MAP.put(\"较不舒适\", \"Low\");\n\t\tCOMFORT_INDEX_DESC_EN_MAP.put(\"很不舒适\", \"Not comfortable\");\n\t\tCOMFORT_INDEX_DESC_EN_MAP.put(\"不宜\", \"Not recommended\");\n\t\tCOMFORT_INDEX_DESC_EN_MAP.put(\"基本适宜\", \"Suitable conditions\");\n\t\tCOMFORT_INDEX_DESC_EN_MAP.put(\"适宜\", \"Ideal conditions\");\n\t\tCOMFORT_INDEX_DESC_CN_MAP = new HashMap();\n\t\tCOMFORT_INDEX_DESC_CN_MAP.put(\"较舒适\", \"较舒适\");\n\t\tCOMFORT_INDEX_DESC_CN_MAP.put(\"舒适\", \"舒适\");\n\t\tCOMFORT_INDEX_DESC_CN_MAP.put(\"较不舒适\", \"较不舒适\");\n\t\tCOMFORT_INDEX_DESC_CN_MAP.put(\"很不舒适\", \"很不舒适\");\n\t\tCOMFORT_INDEX_DESC_CN_MAP.put(\"不宜\", \"不宜\");\n\t\tCOMFORT_INDEX_DESC_CN_MAP.put(\"基本适宜\", \"基本适宜\");\n\t\tCOMFORT_INDEX_DESC_CN_MAP.put(\"适宜\", \"适宜\");\n\t\tCOMFORT_INDEX_DESC_TW_MAP = new HashMap<String, String>();\n\t\tCOMFORT_INDEX_DESC_TW_MAP.put(\"较舒适\", \"較舒適\");\n\t\tCOMFORT_INDEX_DESC_TW_MAP.put(\"舒适\", \"舒適\");\n\t\tCOMFORT_INDEX_DESC_TW_MAP.put(\"较不舒适\", \"較不舒適\");\n\t\tCOMFORT_INDEX_DESC_TW_MAP.put(\"很不舒适\", \"很不舒適\");\n\t\tCOMFORT_INDEX_DESC_TW_MAP.put(\"不宜\", \"不宜\");\n\t\tCOMFORT_INDEX_DESC_TW_MAP.put(\"基本适宜\", \"基本適宜\");\n\t\tCOMFORT_INDEX_DESC_TW_MAP.put(\"适宜\", \"適宜\");\n\t\tEXERCISE_INDEX_DETAIL_CN_MAP = new HashMap<String, String>();\n\t\tCARWASH_INDEX_DETAIL_CN_MAP = new HashMap<String, String>();\n\t\tAIRCURE_INDEX_DETAIL_CN_MAP = new HashMap<String, String>();\n\t\tTRAVEL_INDEX_DETAIL_CN_MAP = new HashMap<String, String>();\n\t\tULTRAVIOLET_INDEX_DETAIL_CN_MAP = new HashMap<String, String>();\n\t\tDRESS_INDEX_DETAIL_CN_MAP = new HashMap<String, String>();\n\t\tCOMFORT_INDEX_DETAIL_CN_MAP = new HashMap<String, String>();\n\t\tEXERCISE_INDEX_DETAIL_CN_MAP.put(\"适宜\",\n\t\t\t\t\"天气晴朗，空气清新，是您晨练的大好时机，建议不同年龄段的人们积极参加户外健身活动。\");\n\t\tEXERCISE_INDEX_DETAIL_CN_MAP.put(\"较适宜\",\n\t\t\t\t\"早晨气象条件较适宜晨练，建议晨练着装不要过于单薄，有风时选择避风地点，以防感冒。\");\n\t\tEXERCISE_INDEX_DETAIL_CN_MAP.put(\"较不宜\",\n\t\t\t\t\"早晨天气变化较大，较不宜晨练，若坚持晨练，请适当减少晨练时间，注意保暖并携带雨具。\");\n\t\tEXERCISE_INDEX_DETAIL_CN_MAP.put(\"不宜\",\n\t\t\t\t\"早晨天气很差，请避免户外晨练，建议在室内做适当锻炼，保持身体健康。\");\n\t\tEXERCISE_INDEX_DETAIL_CN_MAP.put(\"极适宜\",\n\t\t\t\t\"天气晴朗，空气清新，是您晨练的大好时机，建议不同年龄段的人们积极参加户外健身活动。\");\n\t\tCARWASH_INDEX_DETAIL_CN_MAP.put(\"适宜\",\n\t\t\t\t\"适宜洗车，未来持续两天无雨天气较好，适合擦洗汽车，蓝天白云、风和日丽将伴您的车子连日洁净。\");\n\t\tCARWASH_INDEX_DETAIL_CN_MAP.put(\"较适宜\",\n\t\t\t\t\"较适宜洗车，未来一天无雨，风力较小，擦洗一新的汽车至少能保持一天。\");\n\t\tCARWASH_INDEX_DETAIL_CN_MAP.put(\"较不宜\", \"较不宜洗车，如果执意擦洗汽车，要做好溅上泥水的心理准备。\");\n\t\tCARWASH_INDEX_DETAIL_CN_MAP.put(\"不宜\",\n\t\t\t\t\"不宜洗车，未来24小时会有天气变化，如果在此期间洗车，可能会再次弄脏您的爱车。\");\n\t\tCARWASH_INDEX_DETAIL_CN_MAP.put(\"极适宜\",\n\t\t\t\t\"适宜洗车，未来持续两天无雨天气较好，适合擦洗汽车，蓝天白云、风和日丽将伴您的车子连日洁净。\");\n\t\tAIRCURE_INDEX_DETAIL_CN_MAP.put(\"适宜\",\n\t\t\t\t\"万里无云，光照充足，适宜晾晒。赶紧把久未见阳光的衣物搬出来吸收一下太阳的味道吧！\");\n\t\tAIRCURE_INDEX_DETAIL_CN_MAP.put(\"较适宜\",\n\t\t\t\t\"天气不错，适宜晾晒。赶紧把久未见阳光的衣物搬出来吸收一下太阳的味道吧。\");\n\t\tAIRCURE_INDEX_DETAIL_CN_MAP.put(\"不太适宜\", \"天气不好，不太适宜晾晒。请随时注意天气变化。\");\n\t\tAIRCURE_INDEX_DETAIL_CN_MAP.put(\"较不宜\",\n\t\t\t\t\"偶尔的降雨可能会淋湿晾晒的衣物，不太适宜晾晒。请随时注意天气变化。\");\n\t\tAIRCURE_INDEX_DETAIL_CN_MAP\n\t\t\t\t.put(\"不宜\", \"有降水，不适宜晾晒。如果非晾晒不可，请在室内准备出充足的空间。\");\n\t\tAIRCURE_INDEX_DETAIL_CN_MAP.put(\"极适宜\",\n\t\t\t\t\"万里无云，光照充足，适宜晾晒。赶紧把久未见阳光的衣物搬出来吸收一下太阳的味道吧！\");\n\t\tTRAVEL_INDEX_DETAIL_CN_MAP.put(\"适宜\",\n\t\t\t\t\"天气晴朗，风和日丽，温度适宜，是个好天气哦。这样的天气很适宜旅游，您可以尽情地享受大自然的风光。\");\n\t\tTRAVEL_INDEX_DETAIL_CN_MAP.put(\"较适宜\", \"今天气候温和，天气变化不大，较适宜旅游，是出行游玩的好时机。\");\n\t\tTRAVEL_INDEX_DETAIL_CN_MAP.put(\"较不宜\", \"天气变化较大，会给出行带来麻烦，建议还是多选择在室内活动！\");\n\t\tTRAVEL_INDEX_DETAIL_CN_MAP.put(\"不宜\", \"天气变化很大，会给出行带来很多麻烦，建议还是多选择在室内活动！\");\n\t\tTRAVEL_INDEX_DETAIL_CN_MAP.put(\"极适宜\",\n\t\t\t\t\"天气晴朗，风和日丽，温度适宜，是个好天气哦。这样的天气很适宜旅游，您可以尽情地享受大自然的风光。\");\n\t\tULTRAVIOLET_INDEX_DETAIL_CN_MAP.put(\"中等\",\n\t\t\t\t\"属中等强度紫外线辐射天气，外出时建议涂擦SPF高于15、PA+的防晒护肤品，戴帽子、太阳镜。\");\n\t\tULTRAVIOLET_INDEX_DETAIL_CN_MAP.put(\"最弱\",\n\t\t\t\t\"属弱紫外线辐射天气，无需特别防护。若长期在户外，建议涂擦SPF在8-12之间的防晒护肤品。\");\n\t\tULTRAVIOLET_INDEX_DETAIL_CN_MAP.put(\"弱\",\n\t\t\t\t\"紫外线强度较弱，建议出门前涂擦SPF在12-15之间、PA+的防晒护肤品。\");\n\t\tULTRAVIOLET_INDEX_DETAIL_CN_MAP.put(\"强\",\n\t\t\t\t\"紫外线辐射强，建议涂擦SPF20左右、PA++的防晒护肤品。避免在10点至14点暴露于日光下。\");\n\t\tULTRAVIOLET_INDEX_DETAIL_CN_MAP.put(\"很强\",\n\t\t\t\t\"紫外线辐射极强，建议涂擦SPF20以上、PA++的防晒护肤品，尽量避免暴露于日光下。\");\n\t\tDRESS_INDEX_DETAIL_CN_MAP.put(\"凉\", \"建议着薄型套装等春秋过渡装。年老体弱者宜着套装。\");\n\t\tDRESS_INDEX_DETAIL_CN_MAP.put(\"舒适\", \"建议着薄型套装等春秋过渡装。年老体弱者宜着套装。\");\n\t\tDRESS_INDEX_DETAIL_CN_MAP.put(\"较舒适\",\n\t\t\t\t\"建议着薄外套、开衫牛仔衫裤等服装。年老体弱者应适当添加衣物，宜着夹克衫、薄毛衣等。\");\n\t\tDRESS_INDEX_DETAIL_CN_MAP.put(\"炎热\",\n\t\t\t\t\"天气炎热，建议着短衫、短裙、短裤、薄型T恤衫、敞领短袖棉衫等清凉夏季服装。\");\n\t\tDRESS_INDEX_DETAIL_CN_MAP.put(\"热\",\n\t\t\t\t\"天气较热，建议着短裙、短裤、短套装、T恤等夏季服装。年老体弱者宜着长袖衬衫和单裤。\");\n\t\tDRESS_INDEX_DETAIL_CN_MAP.put(\"暖\", \"较凉爽，建议着长袖衬裤等春秋过渡装。体弱者宜着长袖衬衫和马甲。\");\n\t\tDRESS_INDEX_DETAIL_CN_MAP.put(\"温凉\", \"较凉爽，建议着夹衣加薄羊毛衫等春秋服装。体弱者宜着夹衣加羊毛衫。\");\n\t\tDRESS_INDEX_DETAIL_CN_MAP.put(\"气温较低\",\n\t\t\t\t\"天气凉，建议着厚外套加毛衣等春秋服装。年老体弱者宜着大衣、呢外套加羊毛衫。\");\n\t\tDRESS_INDEX_DETAIL_CN_MAP.put(\"冷\",\n\t\t\t\t\"天气冷，建议着棉衣、皮夹克加羊毛衫等冬季服装。年老体弱者宜着厚棉衣或冬大衣。\");\n\t\tDRESS_INDEX_DETAIL_CN_MAP.put(\"寒冷\",\n\t\t\t\t\"天气冷，建议着棉衣、皮夹克加羊毛衫等冬季服装。年老体弱者宜着厚棉衣或冬大衣。\");\n\t\tCOMFORT_INDEX_DETAIL_CN_MAP.put(\"较舒适\", \"今日温度较为稳定，天气变化较小，较为舒适\");\n\t\tCOMFORT_INDEX_DETAIL_CN_MAP.put(\"舒适\", \"今日温度较为稳定，天气变化较小，较为舒适\");\n\t\tCOMFORT_INDEX_DETAIL_CN_MAP.put(\"较不舒适\",\n\t\t\t\t\"白天虽然天气以阴为主，但由于天热，加上湿度较大，您会感到很闷热，很不舒适。\");\n\t\tCOMFORT_INDEX_DETAIL_CN_MAP.put(\"很不舒适\", \"白天天气晴好，但温度不够适宜，您可能会感到不舒适。\");\n\t\tCOMFORT_INDEX_DETAIL_CN_MAP.put(\"不宜\", \"今天天气变化较大，可能会令人有些不舒适。\");\n\t\tCOMFORT_INDEX_DETAIL_CN_MAP.put(\"基本适宜\",\n\t\t\t\t\"白天不太热也不太冷，风力不大，相信您在这样的天气条件下，应会感到比较清爽和舒适。\");\n\t\tCOMFORT_INDEX_DETAIL_CN_MAP.put(\"适宜\",\n\t\t\t\t\"白天不太热也不太冷，风力不大，相信您在这样的天气条件下，应会感到比较清爽和舒适。\");\n\t\tEXERCISE_INDEX_DETAIL_EN_MAP = new HashMap();\n\t\tCARWASH_INDEX_DETAIL_EN_MAP = new HashMap();\n\t\tAIRCURE_INDEX_DETAIL_EN_MAP = new HashMap();\n\t\tTRAVEL_INDEX_DETAIL_EN_MAP = new HashMap();\n\t\tULTRAVIOLET_INDEX_DETAIL_EN_MAP = new HashMap();\n\t\tDRESS_INDEX_DETAIL_EN_MAP = new HashMap();\n\t\tCOMFORT_INDEX_DETAIL_EN_MAP = new HashMap();\n\t\tEXERCISE_INDEX_DETAIL_EN_MAP.put(\"适宜\",\n\t\t\t\t\"It's a good day for morning exercises!\");\n\t\tEXERCISE_INDEX_DETAIL_EN_MAP.put(\"较适宜\", \"Bring additional layers!\");\n\t\tEXERCISE_INDEX_DETAIL_EN_MAP.put(\"较不宜\", \"Bring additional layers!\");\n\t\tEXERCISE_INDEX_DETAIL_EN_MAP.put(\"不宜\", \"Exercise indoors!\");\n\t\tEXERCISE_INDEX_DETAIL_EN_MAP.put(\"极适宜\",\n\t\t\t\t\"It's a good day for morning exercises!\");\n\t\tCARWASH_INDEX_DETAIL_EN_MAP.put(\"适宜\",\n\t\t\t\t\"It's a good day to wash your car!\");\n\t\tCARWASH_INDEX_DETAIL_EN_MAP.put(\"较适宜\",\n\t\t\t\t\"It's a good day to wash your car.\");\n\t\tCARWASH_INDEX_DETAIL_EN_MAP.put(\"较不宜\", \"Hold off on washing your car.\");\n\t\tCARWASH_INDEX_DETAIL_EN_MAP.put(\"不宜\", \"Hold off on washing your car.\");\n\t\tCARWASH_INDEX_DETAIL_EN_MAP.put(\"极适宜\",\n\t\t\t\t\"It's a good day to wash your car!\");\n\t\tAIRCURE_INDEX_DETAIL_EN_MAP.put(\"适宜\",\n\t\t\t\t\"The weather is suitable for drying clothes.\");\n\t\tAIRCURE_INDEX_DETAIL_EN_MAP.put(\"较适宜\", \"Good drying conditions.\");\n\t\tAIRCURE_INDEX_DETAIL_EN_MAP.put(\"不太适宜\", \"Dry your clothing indoors.\");\n\t\tAIRCURE_INDEX_DETAIL_EN_MAP.put(\"较不宜\", \"Dry your clothing indoors.\");\n\t\tAIRCURE_INDEX_DETAIL_EN_MAP.put(\"不宜\", \"Dry your clothing indoors.\");\n\t\tAIRCURE_INDEX_DETAIL_EN_MAP.put(\"极适宜\", \"Ideal drying conditions.\");\n\t\tTRAVEL_INDEX_DETAIL_EN_MAP.put(\"适宜\", \"Clear, sunny, and comfortable!\");\n\t\tTRAVEL_INDEX_DETAIL_EN_MAP.put(\"较适宜\", \"Mild and stable temperatures.\");\n\t\tTRAVEL_INDEX_DETAIL_EN_MAP.put(\"较不宜\",\n\t\t\t\t\"Today's weather will be volatile.\");\n\t\tTRAVEL_INDEX_DETAIL_EN_MAP.put(\"不宜\",\n\t\t\t\t\"Today's weather will be volatile.\");\n\t\tTRAVEL_INDEX_DETAIL_EN_MAP.put(\"极适宜\", \"Clear, sunny, and comfortable!\");\n\t\tULTRAVIOLET_INDEX_DETAIL_EN_MAP.put(\"中等\",\n\t\t\t\t\"Use sunscreen and wear hat and sunglasses when outdoors.\");\n\t\tULTRAVIOLET_INDEX_DETAIL_EN_MAP.put(\"最弱\",\n\t\t\t\t\"Use sunscreen and wear hat and sunglasses when outdoors.\");\n\t\tULTRAVIOLET_INDEX_DETAIL_EN_MAP.put(\"弱\",\n\t\t\t\t\"Use sunscreen and wear hat and sunglasses when outdoors.\");\n\t\tULTRAVIOLET_INDEX_DETAIL_EN_MAP.put(\"强\",\n\t\t\t\t\"Avoid sun if possible. Use sunscreen if you need to go out!\");\n\t\tULTRAVIOLET_INDEX_DETAIL_EN_MAP.put(\"很强\",\n\t\t\t\t\"Avoid sun if possible. Use sunscreen if you need to go out\");\n\t\tDRESS_INDEX_DETAIL_EN_MAP.put(\"凉\", \"Light jacket.\");\n\t\tDRESS_INDEX_DETAIL_EN_MAP.put(\"舒适\", \"Light jacket.\");\n\t\tDRESS_INDEX_DETAIL_EN_MAP.put(\"较舒适\", \"Light jacket.\");\n\t\tDRESS_INDEX_DETAIL_EN_MAP.put(\"炎热\", \"Summer clothing.\");\n\t\tDRESS_INDEX_DETAIL_EN_MAP.put(\"热\", \"Summer clothing.\");\n\t\tDRESS_INDEX_DETAIL_EN_MAP.put(\"暖\", \"Light jacket.\");\n\t\tDRESS_INDEX_DETAIL_EN_MAP.put(\"温凉\",\n\t\t\t\t\"Remember to bring additional layers!\");\n\t\tDRESS_INDEX_DETAIL_EN_MAP.put(\"气温较低\", \"Bring a jacket!\");\n\t\tDRESS_INDEX_DETAIL_EN_MAP.put(\"冷\", \"Bundle up!\");\n\t\tDRESS_INDEX_DETAIL_EN_MAP.put(\"寒冷\", \"Bundle up!\");\n\t\tCOMFORT_INDEX_DETAIL_EN_MAP.put(\"较舒适\", \"Temperature will be stable.\");\n\t\tCOMFORT_INDEX_DETAIL_EN_MAP.put(\"舒适\", \"Temperature will be stable.\");\n\t\tCOMFORT_INDEX_DETAIL_EN_MAP\n\t\t\t\t.put(\"较不舒适\", \"Temperature will be volatile!\");\n\t\tCOMFORT_INDEX_DETAIL_EN_MAP.put(\"很不舒适\", \"Hot and humid! :(\");\n\t\tCOMFORT_INDEX_DETAIL_EN_MAP.put(\"不宜\", \"Temperature will be volatile!\");\n\t\tCOMFORT_INDEX_DETAIL_EN_MAP.put(\"基本适宜\", \"Cool and comfortable.\");\n\t\tCOMFORT_INDEX_DETAIL_EN_MAP.put(\"适宜\", \"Cool and comfortable.\");\n\t\tEXERCISE_INDEX_DETAIL_TW_MAP = new HashMap();\n\t\tCARWASH_INDEX_DETAIL_TW_MAP = new HashMap();\n\t\tAIRCURE_INDEX_DETAIL_TW_MAP = new HashMap();\n\t\tTRAVEL_INDEX_DETAIL_TW_MAP = new HashMap();\n\t\tULTRAVIOLET_INDEX_DETAIL_TW_MAP = new HashMap();\n\t\tDRESS_INDEX_DETAIL_TW_MAP = new HashMap();\n\t\tCOMFORT_INDEX_DETAIL_TW_MAP = new HashMap();\n\t\tEXERCISE_INDEX_DETAIL_TW_MAP.put(\"适宜\",\n\t\t\t\t\"天氣晴朗，空氣清新，是您晨練的大好時機，建議不同年齡段的人們積極參加戶外健身活動。\");\n\t\tEXERCISE_INDEX_DETAIL_TW_MAP.put(\"较适宜\",\n\t\t\t\t\"早晨氣象條件較適宜晨練，建議晨練著裝不要過於單薄，有風時選擇避風地點，以防感冒。\");\n\t\tEXERCISE_INDEX_DETAIL_TW_MAP.put(\"较不宜\",\n\t\t\t\t\"早晨天氣變化較大，較不宜晨練，若堅持晨練，請適當減少晨練時間，注意保暖並攜帶雨具。\");\n\t\tEXERCISE_INDEX_DETAIL_TW_MAP.put(\"不宜\",\n\t\t\t\t\"早晨天氣很差，請避免戶外晨練，建議在室內做適當鍛煉，保持身體健康。\");\n\t\tEXERCISE_INDEX_DETAIL_TW_MAP.put(\"极适宜\",\n\t\t\t\t\"天氣晴朗，空氣清新，是您晨練的大好時機，建議不同年齡段的人們積極參加戶外健身活動。\");\n\t\tCARWASH_INDEX_DETAIL_TW_MAP.put(\"适宜\",\n\t\t\t\t\"適宜洗車，未來持續兩天無雨天氣較好，適合擦洗汽車，藍天白雲、風和日麗將伴您的車子連日潔淨。\");\n\t\tCARWASH_INDEX_DETAIL_TW_MAP.put(\"较适宜\",\n\t\t\t\t\"較適宜洗車，未來一天無雨，風力較小，擦洗一新的汽車至少能保持一天。\");\n\t\tCARWASH_INDEX_DETAIL_TW_MAP.put(\"较不宜\", \"較不宜洗車，如果執意擦洗汽車，要做好濺上泥水的心理準備。\");\n\t\tCARWASH_INDEX_DETAIL_TW_MAP.put(\"不宜\",\n\t\t\t\t\"不宜洗車，未來24小時會有天氣變化，如果在此期間洗車，可能會再次弄髒您的愛車。\");\n\t\tCARWASH_INDEX_DETAIL_TW_MAP.put(\"极适宜\",\n\t\t\t\t\"適宜洗車，未來持續兩天無雨天氣較好，適合擦洗汽車，藍天白雲、風和日麗將伴您的車子連日潔淨。\");\n\t\tAIRCURE_INDEX_DETAIL_TW_MAP.put(\"适宜\",\n\t\t\t\t\"萬里無雲，光照充足，適宜晾曬。趕緊把久未見陽光的衣物搬出來吸收一下太陽的味道吧！\");\n\t\tAIRCURE_INDEX_DETAIL_TW_MAP.put(\"较适宜\",\n\t\t\t\t\"天氣不錯，適宜晾曬。趕緊把久未見陽光的衣物搬出來吸收一下太陽的味道吧。\");\n\t\tAIRCURE_INDEX_DETAIL_TW_MAP.put(\"不太適宜\", \"天氣不好，不太適宜晾曬。請隨時注意天氣變化。\");\n\t\tAIRCURE_INDEX_DETAIL_TW_MAP.put(\"较不宜\",\n\t\t\t\t\"偶爾的降雨可能會淋濕晾曬的衣物，不太適宜晾曬。請隨時注意天氣變化。\");\n\t\tAIRCURE_INDEX_DETAIL_TW_MAP\n\t\t\t\t.put(\"不宜\", \"有降水，不適宜晾曬。如果非晾曬不可，請在室內準備出充足的空間。\");\n\t\tAIRCURE_INDEX_DETAIL_TW_MAP.put(\"极适宜\",\n\t\t\t\t\"萬里無雲，光照充足，適宜晾曬。趕緊把久未見陽光的衣物搬出來吸收一下太陽的味道吧！\");\n\t\tTRAVEL_INDEX_DETAIL_TW_MAP.put(\"适宜\",\n\t\t\t\t\"天氣晴朗，風和日麗，溫度適宜，是個好天氣哦。這樣的天氣很適宜旅遊，您可以盡情地享受大自然的風光。\");\n\t\tTRAVEL_INDEX_DETAIL_TW_MAP.put(\"较适宜\", \"今天氣候溫和，天氣變化不大，較適宜旅遊，是出行遊玩的好時機。\");\n\t\tTRAVEL_INDEX_DETAIL_TW_MAP.put(\"较不宜\", \"天氣變化較大，會給出行帶來麻煩，建議還是多選擇在室內活動！\");\n\t\tTRAVEL_INDEX_DETAIL_TW_MAP.put(\"不宜\", \"天氣變化很大，會給出行帶來很多麻煩，建議還是多選擇在室內活動！\");\n\t\tTRAVEL_INDEX_DETAIL_TW_MAP.put(\"极适宜\",\n\t\t\t\t\"天氣晴朗，風和日麗，溫度適宜，是個好天氣哦。這樣的天氣很適宜旅遊，您可以盡情地享受大自然的風光。\");\n\t\tULTRAVIOLET_INDEX_DETAIL_TW_MAP.put(\"中等\",\n\t\t\t\t\"屬中等強度紫外線輻射天氣，外出時建議塗擦SPF高於15、PA+的防曬護膚品，戴帽子、太陽鏡。\");\n\t\tULTRAVIOLET_INDEX_DETAIL_TW_MAP.put(\"最弱\",\n\t\t\t\t\"屬弱紫外線輻射天氣，無需特別防護。若長期在戶外，建議塗擦SPF在8-12之間的防曬護膚品。\");\n\t\tULTRAVIOLET_INDEX_DETAIL_TW_MAP.put(\"弱\",\n\t\t\t\t\"紫外線強度較弱，建議出門前塗擦SPF在12-15之間、PA+的防曬護膚品。\");\n\t\tULTRAVIOLET_INDEX_DETAIL_TW_MAP.put(\"强\",\n\t\t\t\t\"紫外線輻射強，建議塗擦SPF20左右、PA++的防曬護膚品。避免在10點至14點暴露於日光下。\");\n\t\tULTRAVIOLET_INDEX_DETAIL_TW_MAP.put(\"很强\",\n\t\t\t\t\"紫外線輻射極強，建議塗擦SPF20以上、PA++的防曬護膚品，盡量避免暴露於日光下。\");\n\t\tDRESS_INDEX_DETAIL_TW_MAP.put(\"凉\", \"建議著薄型套裝等春秋過渡裝。年老體弱者宜著套裝。\");\n\t\tDRESS_INDEX_DETAIL_TW_MAP.put(\"舒适\", \"建議著薄型套裝等春秋過渡裝。年老體弱者宜著套裝。\");\n\t\tDRESS_INDEX_DETAIL_TW_MAP.put(\"较舒适\",\n\t\t\t\t\"建議著薄外套、開衫牛仔衫褲等服裝。年老體弱者應適當添加衣物，宜著夾克衫、薄毛衣等。\");\n\t\tDRESS_INDEX_DETAIL_TW_MAP.put(\"炎热\",\n\t\t\t\t\"天氣炎熱，建議著短衫、短裙、短褲、薄型T恤衫、敞領短袖棉衫等清涼夏季服裝。\");\n\t\tDRESS_INDEX_DETAIL_TW_MAP.put(\"热\",\n\t\t\t\t\"天氣較熱，建議著短裙、短褲、短套裝、T恤等夏季服裝。年老體弱者宜著長袖襯衫和單褲。\");\n\t\tDRESS_INDEX_DETAIL_TW_MAP.put(\"暖\", \"較涼爽，建議著長袖襯褲等春秋過渡裝。體弱者宜著長袖襯衫和馬甲。\");\n\t\tDRESS_INDEX_DETAIL_TW_MAP.put(\"温凉\", \"較涼爽，建議著裌衣加薄羊毛衫等春秋服裝。體弱者宜著裌衣加羊毛衫。\");\n\t\tDRESS_INDEX_DETAIL_TW_MAP.put(\"气温较低\",\n\t\t\t\t\"天氣涼，建議著厚外套加毛衣等春秋服裝。年老體弱者宜著大衣、呢外套加羊毛衫。\");\n\t\tDRESS_INDEX_DETAIL_TW_MAP.put(\"冷\",\n\t\t\t\t\"天氣冷，建議著棉衣、皮夾克加羊毛衫等冬季服裝。年老體弱者宜著厚棉衣或冬大衣。\");\n\t\tDRESS_INDEX_DETAIL_TW_MAP.put(\"寒冷\",\n\t\t\t\t\"天氣冷，建議著棉衣、皮夾克加羊毛衫等冬季服裝。年老體弱者宜著厚棉衣或冬大衣。\");\n\t\tCOMFORT_INDEX_DETAIL_TW_MAP.put(\"较舒适\", \"今日溫度較為穩定，天氣變化較小，較為舒適\");\n\t\tCOMFORT_INDEX_DETAIL_TW_MAP.put(\"舒适\", \"今日溫度較為穩定，天氣變化較小，較為舒適\");\n\t\tCOMFORT_INDEX_DETAIL_TW_MAP.put(\"较不舒适\",\n\t\t\t\t\"白天雖然天氣以陰為主，但由於天熱，加上濕度較大，您會感到很悶熱，很不舒適。\");\n\t\tCOMFORT_INDEX_DETAIL_TW_MAP.put(\"很不舒适\", \"白天天氣晴好，但溫度不夠適宜，您可能會感到不舒適。\");\n\t\tCOMFORT_INDEX_DETAIL_TW_MAP.put(\"不宜\", \"今天天氣變化較大，可能會令人有些不舒適。\");\n\t\tCOMFORT_INDEX_DETAIL_TW_MAP.put(\"基本适宜\",\n\t\t\t\t\"白天不太熱也不太冷，風力不大，相信您在這樣的天氣條件下，應會感到比較清爽和舒適。\");\n\t\tCOMFORT_INDEX_DETAIL_TW_MAP.put(\"适宜\",\n\t\t\t\t\"白天不太熱也不太冷，風力不大，相信您在這樣的天氣條件下，應會感到比較清爽和舒適。\");\n\t\tINDEX_DETAIL_CN_MAP = new HashMap();\n\t\tINDEX_DETAIL_CN_MAP.put(\"穿衣指数\", DRESS_INDEX_DETAIL_CN_MAP);\n\t\tINDEX_DETAIL_CN_MAP.put(\"48小时穿衣指数\", DRESS_INDEX_DETAIL_CN_MAP);\n\t\tINDEX_DETAIL_CN_MAP.put(\"洗车指数\", CARWASH_INDEX_DETAIL_CN_MAP);\n\t\tINDEX_DETAIL_CN_MAP.put(\"旅游指数\", TRAVEL_INDEX_DETAIL_CN_MAP);\n\t\tINDEX_DETAIL_CN_MAP.put(\"紫外线指数\", ULTRAVIOLET_INDEX_DETAIL_CN_MAP);\n\t\tINDEX_DETAIL_CN_MAP.put(\"48小时紫外线指数\", ULTRAVIOLET_INDEX_DETAIL_CN_MAP);\n\t\tINDEX_DETAIL_CN_MAP.put(\"晾晒指数\", AIRCURE_INDEX_DETAIL_CN_MAP);\n\t\tINDEX_DETAIL_CN_MAP.put(\"晨练指数\", EXERCISE_INDEX_DETAIL_CN_MAP);\n\t\tINDEX_DETAIL_CN_MAP.put(\"舒适度指数\", COMFORT_INDEX_DETAIL_CN_MAP);\n\t\tINDEX_DETAIL_EN_MAP = new HashMap();\n\t\tINDEX_DETAIL_EN_MAP.put(\"穿衣指数\", DRESS_INDEX_DETAIL_EN_MAP);\n\t\tINDEX_DETAIL_EN_MAP.put(\"48小时穿衣指数\", DRESS_INDEX_DETAIL_EN_MAP);\n\t\tINDEX_DETAIL_EN_MAP.put(\"洗车指数\", CARWASH_INDEX_DETAIL_EN_MAP);\n\t\tINDEX_DETAIL_EN_MAP.put(\"旅游指数\", TRAVEL_INDEX_DETAIL_EN_MAP);\n\t\tINDEX_DETAIL_EN_MAP.put(\"紫外线指数\", ULTRAVIOLET_INDEX_DETAIL_EN_MAP);\n\t\tINDEX_DETAIL_EN_MAP.put(\"48小时紫外线指数\", ULTRAVIOLET_INDEX_DETAIL_EN_MAP);\n\t\tINDEX_DETAIL_EN_MAP.put(\"晾晒指数\", AIRCURE_INDEX_DETAIL_EN_MAP);\n\t\tINDEX_DETAIL_EN_MAP.put(\"晨练指数\", EXERCISE_INDEX_DETAIL_EN_MAP);\n\t\tINDEX_DETAIL_EN_MAP.put(\"舒适度指数\", COMFORT_INDEX_DETAIL_EN_MAP);\n\t\tINDEX_DETAIL_TW_MAP = new HashMap();\n\t\tINDEX_DETAIL_TW_MAP.put(\"穿衣指数\", DRESS_INDEX_DETAIL_TW_MAP);\n\t\tINDEX_DETAIL_TW_MAP.put(\"48小时穿衣指数\", DRESS_INDEX_DETAIL_TW_MAP);\n\t\tINDEX_DETAIL_TW_MAP.put(\"洗车指数\", CARWASH_INDEX_DETAIL_TW_MAP);\n\t\tINDEX_DETAIL_TW_MAP.put(\"旅游指数\", TRAVEL_INDEX_DETAIL_TW_MAP);\n\t\tINDEX_DETAIL_TW_MAP.put(\"紫外线指数\", ULTRAVIOLET_INDEX_DETAIL_TW_MAP);\n\t\tINDEX_DETAIL_TW_MAP.put(\"48小时紫外线指数\", ULTRAVIOLET_INDEX_DETAIL_TW_MAP);\n\t\tINDEX_DETAIL_TW_MAP.put(\"晾晒指数\", AIRCURE_INDEX_DETAIL_TW_MAP);\n\t\tINDEX_DETAIL_TW_MAP.put(\"晨练指数\", EXERCISE_INDEX_DETAIL_TW_MAP);\n\t\tINDEX_DETAIL_TW_MAP.put(\"舒适度指数\", COMFORT_INDEX_DETAIL_TW_MAP);\n\t\tINDEX_DESC_CN_MAP = new HashMap();\n\t\tINDEX_DESC_CN_MAP.put(\"穿衣指数\", DRESS_INDEX_DESC_CN_MAP);\n\t\tINDEX_DESC_CN_MAP.put(\"48小时穿衣指数\", DRESS_INDEX_DESC_CN_MAP);\n\t\tINDEX_DESC_CN_MAP.put(\"洗车指数\", CARWASH_INDEX_DESC_CN_MAP);\n\t\tINDEX_DESC_CN_MAP.put(\"旅游指数\", TRAVEL_INDEX_DESC_CN_MAP);\n\t\tINDEX_DESC_CN_MAP.put(\"紫外线指数\", ULTRAVIOLET_INDEX_DESC_CN_MAP);\n\t\tINDEX_DESC_CN_MAP.put(\"48小时紫外线指数\", ULTRAVIOLET_INDEX_DESC_CN_MAP);\n\t\tINDEX_DESC_CN_MAP.put(\"晾晒指数\", AIRCURE_INDEX_DESC_CN_MAP);\n\t\tINDEX_DESC_CN_MAP.put(\"晨练指数\", EXERCISE_INDEX_DESC_CN_MAP);\n\t\tINDEX_DESC_CN_MAP.put(\"舒适度指数\", COMFORT_INDEX_DESC_CN_MAP);\n\t\tINDEX_DESC_EN_MAP = new HashMap();\n\t\tINDEX_DESC_EN_MAP.put(\"穿衣指数\", DRESS_INDEX_DESC_EN_MAP);\n\t\tINDEX_DESC_EN_MAP.put(\"48小时穿衣指数\", DRESS_INDEX_DESC_EN_MAP);\n\t\tINDEX_DESC_EN_MAP.put(\"洗车指数\", CARWASH_INDEX_DESC_EN_MAP);\n\t\tINDEX_DESC_EN_MAP.put(\"旅游指数\", TRAVEL_INDEX_DESC_EN_MAP);\n\t\tINDEX_DESC_EN_MAP.put(\"紫外线指数\", ULTRAVIOLET_INDEX_DESC_EN_MAP);\n\t\tINDEX_DESC_EN_MAP.put(\"48小时紫外线指数\", ULTRAVIOLET_INDEX_DESC_EN_MAP);\n\t\tINDEX_DESC_EN_MAP.put(\"晾晒指数\", AIRCURE_INDEX_DESC_EN_MAP);\n\t\tINDEX_DESC_EN_MAP.put(\"晨练指数\", EXERCISE_INDEX_DESC_EN_MAP);\n\t\tINDEX_DESC_EN_MAP.put(\"舒适度指数\", COMFORT_INDEX_DESC_EN_MAP);\n\t\tINDEX_DESC_TW_MAP = new HashMap();\n\t\tINDEX_DESC_TW_MAP.put(\"穿衣指数\", DRESS_INDEX_DESC_TW_MAP);\n\t\tINDEX_DESC_TW_MAP.put(\"48小时穿衣指数\", DRESS_INDEX_DESC_TW_MAP);\n\t\tINDEX_DESC_TW_MAP.put(\"洗车指数\", CARWASH_INDEX_DESC_TW_MAP);\n\t\tINDEX_DESC_TW_MAP.put(\"旅游指数\", TRAVEL_INDEX_DESC_TW_MAP);\n\t\tINDEX_DESC_TW_MAP.put(\"紫外线指数\", ULTRAVIOLET_INDEX_DESC_TW_MAP);\n\t\tINDEX_DESC_TW_MAP.put(\"48小时紫外线指数\", ULTRAVIOLET_INDEX_DESC_TW_MAP);\n\t\tINDEX_DESC_TW_MAP.put(\"晾晒指数\", AIRCURE_INDEX_DESC_TW_MAP);\n\t\tINDEX_DESC_TW_MAP.put(\"晨练指数\", EXERCISE_INDEX_DESC_TW_MAP);\n\t\tINDEX_DESC_TW_MAP.put(\"舒适度指数\", COMFORT_INDEX_DESC_TW_MAP);\n\t\tINDEX_EN_MAP = new HashMap();\n\t\tINDEX_EN_MAP.put(\"风力指数\", \"Wind index\");\n\t\tINDEX_EN_MAP.put(\"穿衣指数\", \"Clothing\");\n\t\tINDEX_EN_MAP.put(\"48小时穿衣指数\", \"48 hours Clothing\");\n\t\tINDEX_EN_MAP.put(\"洗车指数\", \"Car wash index\");\n\t\tINDEX_EN_MAP.put(\"旅游指数\", \"Leisure\");\n\t\tINDEX_EN_MAP.put(\"紫外线指数\", \"UV index\");\n\t\tINDEX_EN_MAP.put(\"48小时紫外线指数\", \"48 hours UV index\");\n\t\tINDEX_EN_MAP.put(\"晾晒指数\", \"Drying index\");\n\t\tINDEX_EN_MAP.put(\"晨练指数\", \"Morning exercise\");\n\t\tINDEX_EN_MAP.put(\"舒适度指数\", \"Comfort\");\n\t\tINDEX_CN_MAP = new HashMap();\n\t\tINDEX_CN_MAP.put(\"风力指数\", \"风力指数\");\n\t\tINDEX_CN_MAP.put(\"穿衣指数\", \"穿衣指数\");\n\t\tINDEX_CN_MAP.put(\"48小时穿衣指数\", \"48小时穿衣指数\");\n\t\tINDEX_CN_MAP.put(\"洗车指数\", \"洗车指数\");\n\t\tINDEX_CN_MAP.put(\"旅游指数\", \"旅游指数\");\n\t\tINDEX_CN_MAP.put(\"紫外线指数\", \"紫外线指数\");\n\t\tINDEX_CN_MAP.put(\"48小时紫外线指数\", \"48小时紫外线指数\");\n\t\tINDEX_CN_MAP.put(\"晾晒指数\", \"晾晒指数\");\n\t\tINDEX_CN_MAP.put(\"晨练指数\", \"晨练指数\");\n\t\tINDEX_CN_MAP.put(\"舒适度指数\", \"舒适度指数\");\n\t\tINDEX_TW_MAP = new HashMap();\n\t\tINDEX_TW_MAP.put(\"风力指数\", \"風力指數\");\n\t\tINDEX_TW_MAP.put(\"穿衣指数\", \"穿衣指數\");\n\t\tINDEX_TW_MAP.put(\"48小时穿衣指数\", \"48小时穿衣指數\");\n\t\tINDEX_TW_MAP.put(\"洗车指数\", \"洗車指數\");\n\t\tINDEX_TW_MAP.put(\"旅游指数\", \"旅遊指數\");\n\t\tINDEX_TW_MAP.put(\"紫外线指数\", \"紫外線\");\n\t\tINDEX_TW_MAP.put(\"48小时紫外线指数\", \"48小时紫外線\");\n\t\tINDEX_TW_MAP.put(\"晾晒指数\", \"晾曬指數\");\n\t\tINDEX_TW_MAP.put(\"晨练指数\", \"晨練指數\");\n\t\tINDEX_TW_MAP.put(\"舒适度指数\", \"舒適指數\");\n\t\tSURPORTTED_LANGUAGE_LIST = new ArrayList();\n\t\tSURPORTTED_LANGUAGE_LIST.add(Locale.US.toString().toLowerCase());\n\t\tSURPORTTED_LANGUAGE_LIST.add(Locale.CHINA.toString().toLowerCase());\n\t\tSURPORTTED_LANGUAGE_LIST.add(Locale.TAIWAN.toString().toLowerCase());\n\t\tINDEX_LANGUAGE_MAP = new HashMap();\n\t\tINDEX_LANGUAGE_MAP\n\t\t\t\t.put(Locale.US.toString().toLowerCase(), INDEX_EN_MAP);\n\t\tINDEX_LANGUAGE_MAP.put(Locale.CHINA.toString().toLowerCase(),\n\t\t\t\tINDEX_CN_MAP);\n\t\tINDEX_LANGUAGE_MAP.put(Locale.TAIWAN.toString().toLowerCase(),\n\t\t\t\tINDEX_TW_MAP);\n\t\tINDEX_DESC_LANGUAGE_MAP = new HashMap();\n\t\tINDEX_DESC_LANGUAGE_MAP.put(Locale.US.toString().toLowerCase(),\n\t\t\t\tINDEX_DESC_EN_MAP);\n\t\tINDEX_DESC_LANGUAGE_MAP.put(Locale.CHINA.toString().toLowerCase(),\n\t\t\t\tINDEX_DESC_CN_MAP);\n\t\tINDEX_DESC_LANGUAGE_MAP.put(Locale.TAIWAN.toString().toLowerCase(),\n\t\t\t\tINDEX_DESC_TW_MAP);\n\t\tEXERCISE_INDEX_DESC_LANGUAGE_MAP = new HashMap();\n\t\tEXERCISE_INDEX_DESC_LANGUAGE_MAP.put(\n\t\t\t\tLocale.US.toString().toLowerCase(), EXERCISE_INDEX_DESC_EN_MAP);\n\t\tEXERCISE_INDEX_DESC_LANGUAGE_MAP.put(Locale.CHINA.toString()\n\t\t\t\t.toLowerCase(), EXERCISE_INDEX_DESC_CN_MAP);\n\t\tEXERCISE_INDEX_DESC_LANGUAGE_MAP.put(Locale.TAIWAN.toString()\n\t\t\t\t.toLowerCase(), EXERCISE_INDEX_DESC_TW_MAP);\n\t\tCARWASH_INDEX_DESC_LANGUAGE_MAP = new HashMap();\n\t\tCARWASH_INDEX_DESC_LANGUAGE_MAP.put(Locale.US.toString().toLowerCase(),\n\t\t\t\tCARWASH_INDEX_DESC_EN_MAP);\n\t\tCARWASH_INDEX_DESC_LANGUAGE_MAP.put(Locale.CHINA.toString()\n\t\t\t\t.toLowerCase(), CARWASH_INDEX_DESC_CN_MAP);\n\t\tCARWASH_INDEX_DESC_LANGUAGE_MAP.put(Locale.TAIWAN.toString()\n\t\t\t\t.toLowerCase(), CARWASH_INDEX_DESC_TW_MAP);\n\t\tAIRCURE_INDEX_DESC_LANGUAGE_MAP = new HashMap();\n\t\tAIRCURE_INDEX_DESC_LANGUAGE_MAP.put(Locale.US.toString().toLowerCase(),\n\t\t\t\tAIRCURE_INDEX_DESC_EN_MAP);\n\t\tAIRCURE_INDEX_DESC_LANGUAGE_MAP.put(Locale.CHINA.toString()\n\t\t\t\t.toLowerCase(), AIRCURE_INDEX_DESC_CN_MAP);\n\t\tAIRCURE_INDEX_DESC_LANGUAGE_MAP.put(Locale.TAIWAN.toString()\n\t\t\t\t.toLowerCase(), AIRCURE_INDEX_DESC_TW_MAP);\n\t\tULTRAVIOLET_INDEX_DESC_LANGUAGE_MAP = new HashMap();\n\t\tULTRAVIOLET_INDEX_DESC_LANGUAGE_MAP.put(Locale.US.toString()\n\t\t\t\t.toLowerCase(), ULTRAVIOLET_INDEX_DESC_EN_MAP);\n\t\tULTRAVIOLET_INDEX_DESC_LANGUAGE_MAP.put(Locale.CHINA.toString()\n\t\t\t\t.toLowerCase(), ULTRAVIOLET_INDEX_DESC_CN_MAP);\n\t\tULTRAVIOLET_INDEX_DESC_LANGUAGE_MAP.put(Locale.TAIWAN.toString()\n\t\t\t\t.toLowerCase(), ULTRAVIOLET_INDEX_DESC_TW_MAP);\n\t\tDRESS_INDEX_DESC_LANGUAGE_MAP = new HashMap();\n\t\tDRESS_INDEX_DESC_LANGUAGE_MAP.put(Locale.US.toString().toLowerCase(),\n\t\t\t\tDRESS_INDEX_DESC_EN_MAP);\n\t\tDRESS_INDEX_DESC_LANGUAGE_MAP.put(\n\t\t\t\tLocale.CHINA.toString().toLowerCase(), DRESS_INDEX_DESC_CN_MAP);\n\t\tDRESS_INDEX_DESC_LANGUAGE_MAP.put(Locale.TAIWAN.toString()\n\t\t\t\t.toLowerCase(), DRESS_INDEX_DESC_TW_MAP);\n\t\tCOMFORT_INDEX_DESC_LANGUAGE_MAP = new HashMap();\n\t\tCOMFORT_INDEX_DESC_LANGUAGE_MAP.put(Locale.US.toString().toLowerCase(),\n\t\t\t\tCOMFORT_INDEX_DESC_EN_MAP);\n\t\tCOMFORT_INDEX_DESC_LANGUAGE_MAP.put(Locale.CHINA.toString()\n\t\t\t\t.toLowerCase(), COMFORT_INDEX_DESC_CN_MAP);\n\t\tCOMFORT_INDEX_DESC_LANGUAGE_MAP.put(Locale.TAIWAN.toString()\n\t\t\t\t.toLowerCase(), COMFORT_INDEX_DESC_TW_MAP);\n\t\tTRAVEL_INDEX_DESC_LANGUAGE_MAP = new HashMap();\n\t\tTRAVEL_INDEX_DESC_LANGUAGE_MAP.put(Locale.US.toString().toLowerCase(),\n\t\t\t\tTRAVEL_INDEX_DESC_EN_MAP);\n\t\tTRAVEL_INDEX_DESC_LANGUAGE_MAP.put(Locale.CHINA.toString()\n\t\t\t\t.toLowerCase(), TRAVEL_INDEX_DESC_CN_MAP);\n\t\tTRAVEL_INDEX_DESC_LANGUAGE_MAP.put(Locale.TAIWAN.toString()\n\t\t\t\t.toLowerCase(), TRAVEL_INDEX_DESC_TW_MAP);\n\t\tINDEX_DETAIL_LANGUAGE_MAP = new HashMap();\n\t\tINDEX_DETAIL_LANGUAGE_MAP.put(Locale.US.toString().toLowerCase(),\n\t\t\t\tINDEX_DETAIL_EN_MAP);\n\t\tINDEX_DETAIL_LANGUAGE_MAP.put(Locale.CHINA.toString().toLowerCase(),\n\t\t\t\tINDEX_DETAIL_CN_MAP);\n\t\tINDEX_DETAIL_LANGUAGE_MAP.put(Locale.TAIWAN.toString().toLowerCase(),\n\t\t\t\tINDEX_DETAIL_TW_MAP);\n\t\tEXERCISE_INDEX_DETAIL_LANGUAGE_MAP = new HashMap();\n\t\tCARWASH_INDEX_DETAIL_LANGUAGE_MAP = new HashMap();\n\t\tAIRCURE_INDEX_DETAIL_LANGUAGE_MAP = new HashMap();\n\t\tULTRAVIOLET_INDEX_DETAIL_LANGUAGE_MAP = new HashMap();\n\t\tDRESS_INDEX_DETAIL_LANGUAGE_MAP = new HashMap();\n\t\tCOMFORT_INDEX_DETAIL_LANGUAGE_MAP = new HashMap();\n\t\tTRAVEL_INDEX_DETAIL_LANGUAGE_MAP = new HashMap();\n\t\tAQI_LEVEL_CN_MAP = new HashMap();\n\t\tAQI_LEVEL_CN_MAP.put(Integer.valueOf(0), \"无污染\");\n\t\tAQI_LEVEL_CN_MAP.put(Integer.valueOf(50), \"优\");\n\t\tAQI_LEVEL_CN_MAP.put(Integer.valueOf(100), \"良\");\n\t\tAQI_LEVEL_CN_MAP.put(Integer.valueOf(150), \"轻度污染\");\n\t\tAQI_LEVEL_CN_MAP.put(Integer.valueOf(200), \"中度污染\");\n\t\tAQI_LEVEL_CN_MAP.put(Integer.valueOf(300), \"重度污染\");\n\t\tAQI_LEVEL_CN_MAP.put(Integer.valueOf(500), \"严重污染\");\n\t\tAQI_LEVEL_EN_MAP = new HashMap();\n\t\tAQI_LEVEL_EN_MAP.put(Integer.valueOf(0), \"Excellent\");\n\t\tAQI_LEVEL_EN_MAP.put(Integer.valueOf(50), \"Good\");\n\t\tAQI_LEVEL_EN_MAP.put(Integer.valueOf(100), \"Moderate\");\n\t\tAQI_LEVEL_EN_MAP.put(Integer.valueOf(150), \"Caution\");\n\t\tAQI_LEVEL_EN_MAP.put(Integer.valueOf(200), \"Unhealthy\");\n\t\tAQI_LEVEL_EN_MAP.put(Integer.valueOf(300), \"Dangerous\");\n\t\tAQI_LEVEL_EN_MAP.put(Integer.valueOf(500), \"Hazardous\");\n\t\tAQI_LEVEL_TW_MAP = new HashMap();\n\t\tAQI_LEVEL_TW_MAP.put(Integer.valueOf(0), \"无汙染\");\n\t\tAQI_LEVEL_TW_MAP.put(Integer.valueOf(50), \"優\");\n\t\tAQI_LEVEL_TW_MAP.put(Integer.valueOf(100), \"良\");\n\t\tAQI_LEVEL_TW_MAP.put(Integer.valueOf(150), \"輕度汙染\");\n\t\tAQI_LEVEL_TW_MAP.put(Integer.valueOf(200), \"中度汙染\");\n\t\tAQI_LEVEL_TW_MAP.put(Integer.valueOf(300), \"重度汙染\");\n\t\tAQI_LEVEL_TW_MAP.put(Integer.valueOf(500), \"严重汙染\");\n\t\tAQI_LEVEL_LANGUAGE_MAP = new HashMap();\n\t\tAQI_LEVEL_LANGUAGE_MAP.put(Locale.US.toString().toLowerCase(),\n\t\t\t\tAQI_LEVEL_EN_MAP);\n\t\tAQI_LEVEL_LANGUAGE_MAP.put(Locale.CHINA.toString().toLowerCase(),\n\t\t\t\tAQI_LEVEL_CN_MAP);\n\t\tAQI_LEVEL_LANGUAGE_MAP.put(Locale.TAIWAN.toString().toLowerCase(),\n\t\t\t\tAQI_LEVEL_TW_MAP);\n\t\tAQI_DESC_CN_MAP = new HashMap();\n\t\tAQI_DESC_CN_MAP.put(Integer.valueOf(0), \"所有人群可在户外正常自由活动。\");\n\t\tAQI_DESC_CN_MAP.put(Integer.valueOf(50), \"所有人群可在户外正常自由活动。\");\n\t\tAQI_DESC_CN_MAP.put(Integer.valueOf(100), \"对空气质量特别敏感的人群应该减少户外活动。\");\n\t\tAQI_DESC_CN_MAP.put(Integer.valueOf(150),\n\t\t\t\t\"儿童、老人及心脏、呼吸系统疾病患者人群应该减少长时间、高强度的户外锻炼。\");\n\t\tAQI_DESC_CN_MAP.put(Integer.valueOf(200),\n\t\t\t\t\"儿童、老人及心脏、呼吸系统疾病患者人群应该减少长时间、高强度的户外锻炼，一般人群适量减少户外运动。\");\n\t\tAQI_DESC_CN_MAP.put(Integer.valueOf(300),\n\t\t\t\t\"儿童、老人及心脏、呼吸系统疾病患者人群应停留在室内，停止户外运动，一般人群减少户外运动。\");\n\t\tAQI_DESC_CN_MAP.put(Integer.valueOf(500),\n\t\t\t\t\"儿童、老人及心脏、呼吸系统疾病患者人群应停留在室内，并减少体力活动，一般人群应该避免户外活动并注意防护。\");\n\t\tAQI_DESC_EN_MAP = new HashMap();\n\t\tAQI_DESC_EN_MAP.put(Integer.valueOf(0),\n\t\t\t\t\"It's a great day to be active outside!\");\n\t\tAQI_DESC_EN_MAP.put(Integer.valueOf(50),\n\t\t\t\t\"It's a great day to be active outside!\");\n\t\tAQI_DESC_EN_MAP\n\t\t\t\t.put(Integer.valueOf(100),\n\t\t\t\t\t\t\"People with preexisting medical conditions should reduce prolonged or heavy exertion.\");\n\t\tAQI_DESC_EN_MAP\n\t\t\t\t.put(Integer.valueOf(150),\n\t\t\t\t\t\t\"People with heart or lung disease, older adults, and young children should reduce prolonged or heavy exertion.\");\n\t\tAQI_DESC_EN_MAP.put(Integer.valueOf(200),\n\t\t\t\t\"Reduce prolonged or heavy exertion.\");\n\t\tAQI_DESC_EN_MAP.put(Integer.valueOf(300),\n\t\t\t\t\"Avoid prolonged or heavy exertion.\");\n\t\tAQI_DESC_EN_MAP.put(Integer.valueOf(500),\n\t\t\t\t\"Remain indoors and reduce your level of activity.\");\n\t\tAQI_DESC_TW_MAP = new HashMap();\n\t\tAQI_DESC_TW_MAP.put(Integer.valueOf(0), \"所有人群可在戶外正常自由活動。\");\n\t\tAQI_DESC_TW_MAP.put(Integer.valueOf(50), \"所有人群可在戶外正常自由活動。\");\n\t\tAQI_DESC_TW_MAP.put(Integer.valueOf(100), \"對空氣質量特別敏感的人群應該減少戶外活動。\");\n\t\tAQI_DESC_TW_MAP.put(Integer.valueOf(150),\n\t\t\t\t\"兒童、老人及心臟、呼吸系統疾病患者人群應該減少長時間、高強度的戶外鍛煉。\");\n\t\tAQI_DESC_TW_MAP.put(Integer.valueOf(200),\n\t\t\t\t\"兒童、老人及心臟、呼吸系統疾病患者人群應該減少長時間、高強度的戶外鍛煉，一般人群適量減少戶外運動。\");\n\t\tAQI_DESC_TW_MAP.put(Integer.valueOf(300),\n\t\t\t\t\"兒童、老人及心臟、呼吸系統疾病患者人群應停留在室內，停止戶外運動，一般人群減少戶外運動。\");\n\t\tAQI_DESC_TW_MAP.put(Integer.valueOf(500),\n\t\t\t\t\"兒童、老人及心臟、呼吸系統疾病患者人群應停留在室內，並減少體力活動，一般人群應該避免戶外活動並注意防護。\");\n\t\tAQI_DESC_LANGUAGE_MAP = new HashMap();\n\t\tAQI_DESC_LANGUAGE_MAP.put(Locale.US.toString().toLowerCase(),\n\t\t\t\tAQI_DESC_EN_MAP);\n\t\tAQI_DESC_LANGUAGE_MAP.put(Locale.CHINA.toString().toLowerCase(),\n\t\t\t\tAQI_DESC_CN_MAP);\n\t\tAQI_DESC_LANGUAGE_MAP.put(Locale.TAIWAN.toString().toLowerCase(),\n\t\t\t\tAQI_DESC_TW_MAP);\n\t\tAQI_SOURCE_LANGUAGE_MAP = new HashMap();\n\t\tAQI_SOURCE_LANGUAGE_MAP.put(Locale.US.toString().toLowerCase(),\n\t\t\t\t\"China National Environmental Monitoring Center\");\n\t\tAQI_SOURCE_LANGUAGE_MAP.put(Locale.CHINA.toString().toLowerCase(),\n\t\t\t\t\"中国环境监测总站\");\n\t\tAQI_SOURCE_LANGUAGE_MAP.put(Locale.TAIWAN.toString().toLowerCase(),\n\t\t\t\t\"中國環境監測總站\");\n\t\tPROVINCE_FAKE_CITY_MAP = new HashMap<String,String>();\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101050000\", \"黑龙江\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101060000\", \"吉林\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101070000\", \"辽宁\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101080000\", \"内蒙古\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101090000\", \"河北\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101100000\", \"山西\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101110000\", \"陕西\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101120000\", \"山东\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101130000\", \"新疆\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101140000\", \"西藏\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101150000\", \"青海\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101160000\", \"甘肃\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101170000\", \"宁夏\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101180000\", \"河南\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101190000\", \"江苏\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101200000\", \"湖北\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101210000\", \"浙江\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101220000\", \"安徽\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101230000\", \"福建\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101240000\", \"江西\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101250000\", \"湖南\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101260000\", \"贵州\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101270000\", \"四川\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101280000\", \"广东\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101290000\", \"云南\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101300000\", \"广西\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101310000\", \"海南\");\n\t\tPROVINCE_FAKE_CITY_MAP.put(\"101340000\", \"台湾\");\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/weather/plugin/spider/WeatherController.java",
    "content": "package com.way.weather.plugin.spider;\n\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.Map;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport android.util.Log;\n\nimport com.way.weather.plugin.bean.AQI;\nimport com.way.weather.plugin.bean.Alerts;\nimport com.way.weather.plugin.bean.Alerts.Alert;\nimport com.way.weather.plugin.bean.Forecast;\nimport com.way.weather.plugin.bean.Index;\nimport com.way.weather.plugin.bean.IndexDetail;\nimport com.way.weather.plugin.bean.RealTime;\n\npublic class WeatherController {\n\tpublic static AQI convertToNewAQI(JSONObject aqiJSONObject,\n\t\t\tString language, String pid) {\n\t\tAQI aqi = new AQI();\n\t\taqi.setCity_code(pid);\n\t\ttry {\n\t\t\taqi.setPub_time(getAQITime(aqiJSONObject.getString(\"pub_time\")));\n\n\t\t\tint aqiValue = WeatherUtilities.getAqi(aqiJSONObject\n\t\t\t\t\t.getString(\"aqi\"));\n\t\t\taqi.setAqi(aqiValue);\n\t\t\taqi.setPm25(WeatherUtilities.getAqi(aqiJSONObject.getString(\"pm25\")));\n\t\t\taqi.setPm10(WeatherUtilities.getAqi(aqiJSONObject.getString(\"pm10\")));\n\t\t\taqi.setNo2(WeatherUtilities.getAqi(aqiJSONObject.getString(\"no2\")));\n\t\t\taqi.setSo2(WeatherUtilities.getAqi(aqiJSONObject.getString(\"so2\")));\n\t\t\taqi.setCo(WeatherConstants.NO_VALUE_FLAG);\n\t\t\taqi.setO3(WeatherConstants.NO_VALUE_FLAG);\n\t\t\taqi.setAqi_level(WeatherUtilities.getAqiLevel(aqiValue, language));\n\t\t\taqi.setAqi_desc(WeatherUtilities.getAqiDesc(aqiValue, language));\n\t\t\taqi.setSource(WeatherUtilities.getAQISource(language));\n\t\t\taqi.setSpot(aqiJSONObject.getString(\"spot\"));\n\t\t} catch (JSONException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn aqi;\n\t}\n\n\tpublic static Alerts convertToNewAlert(JSONArray alertJSONArray,\n\t\t\tString language) {\n\t\tArrayList<Alert> alertLists = new ArrayList<Alert>();\n\t\tAlerts alerts = new Alerts();\n\t\ttry {\n\t\t\tfor (int i = 0; i < alertJSONArray.length(); ++i) {\n\t\t\t\tJSONObject jsonObject = alertJSONArray.getJSONObject(i);\n\t\t\t\tAlerts.Alert alert = new Alerts.Alert();\n\t\t\t\talert.setAbnormal(jsonObject.getString(\"abnormal\"));\n\t\t\t\talert.setDetail(jsonObject.getString(\"detail\"));\n\t\t\t\talert.setHoliday(jsonObject.getString(\"holiday\"));\n\t\t\t\talert.setLevel(jsonObject.getString(\"level\"));\n\t\t\t\talert.setPubTime(Long.valueOf(jsonObject.getLong(\"pub_time\")));\n\t\t\t\talert.setTitle(jsonObject.getString(\"title\"));\n\t\t\t\talertLists.add(alert);\n\t\t\t}\n\t\t\talerts.setPid(language);\n\t\t\talerts.setArryAlert(alertLists);\n\t\t} catch (JSONException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn alerts;\n\t}\n\n\tpublic static Forecast convertToNewForecast(JSONObject forecastJSONObject,\n\t\t\tString language, String pid) throws JSONException,\n\t\t\tNumberFormatException {\n\t\tForecast forecast = new Forecast();\n\n\t\tJSONObject forcastJSONObject = forecastJSONObject\n\t\t\t\t.getJSONObject(\"forecast\");\n\n\t\t// JSONObject todayJSONObject =\n\t\t// forecastJSONObject.getJSONObject(\"today\");\n\t\tJSONObject yestodayJSONObject = forecastJSONObject\n\t\t\t\t.getJSONObject(\"yestoday\");\n\n\t\tforecast.setPid(pid);\n\t\tforecast.setWinds(0, \"\");\n\t\tfor (int i = 1; i < Forecast.DAY_NUM; i++) {\n\t\t\tforecast.setWinds(i, WeatherUtilities.getWind(\n\t\t\t\t\tforcastJSONObject.getString(\"fx\" + (i > 2 ? 2 : i)),\n\t\t\t\t\tforcastJSONObject.getString(\"fl\" + i), language));\n\t\t}\n\n\t\tWeatherUtilities.WeatherName[] weatherName = new WeatherUtilities.WeatherName[5];\n\t\tfor (int i = 1; i < Forecast.DAY_NUM; i++) {\n\t\t\tweatherName[i - 1] = WeatherUtilities.getWeatherName(\n\t\t\t\t\tforcastJSONObject.getString(\"weather\" + i), language);\n\t\t}\n\t\tfor (int i = 1; i < Forecast.DAY_NUM; i++) {\n\t\t\tforecast.setWeatherNames(i, weatherName[i - 1].getName());\n\t\t}\n\t\tforecast.setWeatherNames(0, yestodayJSONObject.getString(\"weatherEnd\"));\n\n\t\tforecast.setWeatherNamesFrom(\n\t\t\t\t0,\n\t\t\t\tWeatherUtilities.getWeatherName(\n\t\t\t\t\t\tyestodayJSONObject.getString(\"weatherStart\"), language)\n\t\t\t\t\t\t.getFrom());\n\t\tfor (int i = 1; i < Forecast.DAY_NUM; i++) {\n\t\t\tforecast.setWeatherNamesFrom(i, weatherName[i - 1].getFrom());\n\t\t}\n\t\tforecast.setWeatherNamesTo(\n\t\t\t\t0,\n\t\t\t\tWeatherUtilities.getWeatherName(\n\t\t\t\t\t\tyestodayJSONObject.getString(\"weatherEnd\"), language)\n\t\t\t\t\t\t.getTo());\n\t\tfor (int i = 1; i < Forecast.DAY_NUM; i++) {\n\t\t\tforecast.setWeatherNamesTo(i, weatherName[i - 1].getTo());\n\t\t}\n\t\tlong forcastTime = getForcastTime(forcastJSONObject.getString(\"date_y\")\n\t\t\t\t+ \" \" + forcastJSONObject.getString(\"fchh\"));\n\t\tforecast.setPubtime(forcastTime);\n\n\t\tint[] forecastTemps = new int[12];\n\t\tfor (int i = 0; i < (Forecast.DAY_NUM - 1); ++i) {\n\t\t\tString[] temps = forcastJSONObject.getString(\"temp\" + (i + 1))\n\t\t\t\t\t.split(\"~\");\n\t\t\tString[] minTemp = temps[0].split(\"℃\");\n\t\t\tString[] maxTemp = temps[1].split(\"℃\");\n\t\t\tforecastTemps[(i * 2)] = Integer.parseInt(minTemp[0]);\n\t\t\tforecastTemps[(1 + i * 2)] = Integer.parseInt(maxTemp[0]);\n\t\t}\n\t\tforecast.setTmpHighs(0, yestodayJSONObject.getInt(\"tempMax\"));\n\t\tforecast.setTmpLows(0, yestodayJSONObject.getInt(\"tempMin\"));\n\t\tfor (int i = 0; i < (Forecast.DAY_NUM - 1); ++i) {\n\t\t\tforecast.setTmpHighs(i + 1, forecastTemps[(i * 2)]);\n\t\t\tforecast.setTmpLows(i + 1, forecastTemps[(1 + i * 2)]);\n\t\t}\n\t\tforecast.setType(0, WeatherUtilities\n\t\t\t\t.getAnimationType(yestodayJSONObject.getString(\"weatherEnd\")));\n\t\tfor (int i = 1; i < Forecast.DAY_NUM; i++) {\n\t\t\tforecast.setType(i, WeatherUtilities\n\t\t\t\t\t.getAnimationType(forcastJSONObject\n\t\t\t\t\t\t\t.getString(\"weather\" + i)));\n\t\t}\n\t\t// for (int i = 0; i < Forecast.DAY_NUM; ++i)\n\t\t// forecast.setSunrise(i, WeatherConstants.NO_VALUE_FLAG);\n\t\t// for (int i = 0; i < Forecast.DAY_NUM; ++i)\n\t\t// forecast.setSunset(i, WeatherConstants.NO_VALUE_FLAG);\n\t\tfor (int i = 0; i < Forecast.DAY_NUM; ++i)\n\t\t\tforecast.setPressures(i, WeatherConstants.NO_VALUE_FLAG);\n\t\tfor (int i = 0; i < Forecast.DAY_NUM; ++i)\n\t\t\tforecast.setHumiditys(i, WeatherConstants.NO_VALUE_FLAG);\n\t\tJSONArray jsonArray = forecastJSONObject.getJSONObject(\"accu_f5\")\n\t\t\t\t.getJSONArray(\"DailyForecasts\");\n\t\tfor (int i = 1; i < Forecast.DAY_NUM; ++i) {\n\t\t\tforecast.setSunrise(\n\t\t\t\t\ti,\n\t\t\t\t\tLong.valueOf(jsonArray.getJSONObject(i - 1).getString(\n\t\t\t\t\t\t\t\"Sun_EpochRise\")));\n\t\t\tforecast.setSunset(\n\t\t\t\t\ti,\n\t\t\t\t\tLong.valueOf(jsonArray.getJSONObject(i - 1).getString(\n\t\t\t\t\t\t\t\"Sun_EpochSet\")));\n\t\t}\n\t\treturn forecast;\n\t}\n\n\tpublic static Index convertToNewIndex(JSONObject indexInfo,\n\t\t\tString language, String pid) throws JSONException {\n\t\tIndex index = new Index();\n\t\tindex.setCity_code(pid);\n\n\t\tJSONObject indexJSONObject = indexInfo.getJSONObject(\"forecast\");\n\t\tArrayList<IndexDetail> indexDetailLists = new ArrayList<IndexDetail>();\n\t\tIndexDetail windIndexDetail = new IndexDetail();\n\t\twindIndexDetail.setDesc(WeatherUtilities.getWind(\n\t\t\t\tindexJSONObject.getString(\"fx1\"),\n\t\t\t\tindexJSONObject.getString(\"fl1\"), language));\n\t\twindIndexDetail.setTitle(WeatherUtilities.getIndexTitle(\n\t\t\t\tWeatherConstants.WIND_LEVEL_INDEX, language));\n\t\twindIndexDetail.setDetail(WeatherUtilities.getWindIndexDetail(\n\t\t\t\tindexJSONObject.getString(\"fl1\"), language));\n\t\twindIndexDetail.setType(WeatherUtilities\n\t\t\t\t.getIndexType(WeatherConstants.WIND_LEVEL_INDEX));\n\t\tindexDetailLists.add(windIndexDetail);\n\n\t\tfor (Map.Entry<String, String> entry : WeatherConstants.INDEX_OLD\n\t\t\t\t.entrySet()) {\n\t\t\tString key = entry.getKey();\n\t\t\tIndexDetail indexDetail = new IndexDetail();\n\t\t\tindexDetail.setType(WeatherUtilities.getIndexType(key).intValue());\n\t\t\tindexDetail.setTitle(WeatherUtilities.getIndexTitle(key, language));\n\t\t\tString indexValue = indexJSONObject\n\t\t\t\t\t.getString(WeatherConstants.INDEX_OLD.get(key));\n\t\t\tindexDetail.setDesc(WeatherUtilities.getIndexDesc(key, indexValue,\n\t\t\t\t\tlanguage));\n\n\t\t\tindexDetail.setDetail(WeatherUtilities.getIndexDetail(key,\n\t\t\t\t\tindexValue, language));\n\n\t\t\tindexDetailLists.add(indexDetail);\n\t\t}\n\t\tindex.setIndex(indexDetailLists);\n\t\treturn index;\n\t}\n\n\tpublic static RealTime convertToNewRealTime(JSONObject realTimeJSONObject,\n\t\t\tString language, String pid) throws JSONException {\n\t\tRealTime realTime = new RealTime();\n\t\trealTime.setCity_code(pid);\n\t\trealTime.setPub_time(parseTime(realTimeJSONObject.getString(\"time\")));\n\t\trealTime.setTemp(WeatherUtilities.getIntFromJSON(realTimeJSONObject,\n\t\t\t\t\"temp\"));\n\t\trealTime.setWind(WeatherUtilities.getWind(\n\t\t\t\trealTimeJSONObject.getString(\"WD\"),\n\t\t\t\trealTimeJSONObject.getString(\"WS\"), language));\n\t\trealTime.setAnimation_type(WeatherUtilities\n\t\t\t\t.getAnimationType(realTimeJSONObject.getString(\"weather\")));\n\t\trealTime.setHumidity(WeatherUtilities.getHumidity(realTimeJSONObject\n\t\t\t\t.getString(\"SD\")));\n\t\trealTime.setRising_tide(WeatherConstants.NO_VALUE_FLAG);\n\t\trealTime.setFalling_tide(WeatherConstants.NO_VALUE_FLAG);\n\t\trealTime.setPressure(WeatherConstants.NO_VALUE_FLAG);\n\t\trealTime.setWater(WeatherConstants.NO_VALUE_FLAG);\n\t\trealTime.setWeather_name(WeatherUtilities.getWeatherName(\n\t\t\t\trealTimeJSONObject.getString(\"weather\"), language).getName());\n\t\treturn realTime;\n\t}\n\n\tprivate static long getAQITime(String timeStr) {\n\t\tlong aqiTime = -7754570281926000640L;\n\t\ttry {\n\t\t\taqiTime = new SimpleDateFormat(\"yyyy-MM-dd HH:mm\").parse(timeStr)\n\t\t\t\t\t.getTime();\n\t\t} catch (ParseException e) {\n\t\t}\n\t\treturn aqiTime;\n\t}\n\n\tprivate static long getForcastTime(String timeStr) {\n\t\tlong forcastTime = -7754570281926000640L;\n\t\ttry {\n\t\t\tforcastTime = new SimpleDateFormat(\"yyyy年M月d日 HH\").parse(timeStr)\n\t\t\t\t\t.getTime();\n\n\t\t} catch (ParseException e) {\n\t\t}\n\t\treturn forcastTime;\n\t}\n\n\tprivate static long parseTime(String timeStr) {\n\t\tSimpleDateFormat simpleDateFormat = new SimpleDateFormat(\"yyyy-MM-dd\");\n\t\tString data = simpleDateFormat.format(new Date(System\n\t\t\t\t.currentTimeMillis()));\n\t\ttimeStr = data + \" \" + timeStr;\n\t\tlong realTime = getAQITime(timeStr);\n\t\tLog.i(\"HeHe\", \"timeStr = \" + timeStr + \", realTime = \" + realTime);\n\t\treturn realTime;\n\t}\n}"
  },
  {
    "path": "WayHoo/src/com/way/weather/plugin/spider/WeatherSpider.java",
    "content": "package com.way.weather.plugin.spider;\n\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.text.TextUtils;\n\nimport com.android.volley.DefaultRetryPolicy;\nimport com.android.volley.toolbox.RequestFuture;\nimport com.android.volley.toolbox.StringRequest;\nimport com.way.common.util.NetUtil;\nimport com.way.db.CityProvider;\nimport com.way.db.CityProvider.CityConstants;\nimport com.way.fragment.TaskException;\nimport com.way.weather.plugin.bean.AQI;\nimport com.way.weather.plugin.bean.Alerts;\nimport com.way.weather.plugin.bean.Forecast;\nimport com.way.weather.plugin.bean.Index;\nimport com.way.weather.plugin.bean.RealTime;\nimport com.way.weather.plugin.bean.WeatherInfo;\nimport com.way.yahoo.App;\n\npublic class WeatherSpider {\n\n\tpublic static final String WEATHER_ALL = \"http://weatherapi.market.xiaomi.com/wtr-v2/weather?cityId=%s\";\n\n\tpublic static WeatherInfo getWeatherInfo(String postID) throws TaskException {\n\t\tif (NetUtil.getNetworkState(App.getApplication()) == NetUtil.NETWORN_NONE)\n\t\t\tthrow new TaskException(\n\t\t\t\t\tTaskException.TaskError.noneNetwork.toString());\n\t\tRequestFuture<String> future = RequestFuture.newFuture();\n\t\tStringRequest request = new StringRequest(String.format(\n\t\t\t\tWeatherSpider.WEATHER_ALL, postID), future, future);\n\t\tApp.getVolleyRequestQueue().add(request);\n\t\ttry {\n\t\t\tString result = future.get(DefaultRetryPolicy.DEFAULT_TIMEOUT_MS,\n\t\t\t\t\tTimeUnit.MILLISECONDS);\n\t\t\tWeatherInfo weatherInfo = WeatherSpider.getWeatherInfo(\n\t\t\t\t\tApp.getApplication(), postID, result);\n\t\t\tif (!WeatherSpider.isEmpty(weatherInfo)) {\n\t\t\t\tsave2Database(weatherInfo, postID, result);// 保存到数据库\n\t\t\t\treturn weatherInfo;\n\t\t\t}\n\t\t} catch (InterruptedException e) {\n\t\t\tthrow new TaskException(TextUtils.isEmpty(e.getMessage()) ? \"\"\n\t\t\t\t\t: e.getMessage());\n\t\t} catch (ExecutionException e) {\n\t\t\tthrow new TaskException(TextUtils.isEmpty(e.getMessage()) ? \"\"\n\t\t\t\t\t: e.getMessage());\n\t\t} catch (TimeoutException e) {\n\t\t\tthrow new TaskException(TaskException.TaskError.timeout.toString());\n\t\t} catch (JSONException e) {\n\t\t\tthrow new TaskException(\n\t\t\t\t\tTaskException.TaskError.resultIllegal.toString());\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic static void save2Database(WeatherInfo weatherInfo, String postID,\n\t\t\tString response) {\n\t\tlong pubTime = weatherInfo.getRealTime().getPub_time();\n\t\tlong savePubTime = getPubTime(postID);\n\t\tif (pubTime != savePubTime) {\n\t\t\tContentValues contentValues = new ContentValues();\n\t\t\tcontentValues.put(CityConstants.REFRESH_TIME,\n\t\t\t\t\tSystem.currentTimeMillis());\n\t\t\tcontentValues.put(CityConstants.PUB_TIME, pubTime);\n\t\t\tcontentValues.put(CityConstants.WEATHER_INFO, response);\n\t\t\tApp.getApplication()\n\t\t\t\t\t.getContentResolver()\n\t\t\t\t\t.update(CityProvider.TMPCITY_CONTENT_URI, contentValues,\n\t\t\t\t\t\t\tCityConstants.POST_ID + \"=?\",\n\t\t\t\t\t\t\tnew String[] { postID });\n\t\t}\n\t}\n\n\tpublic static long getPubTime(String postID) {\n\t\tCursor c = App\n\t\t\t\t.getApplication()\n\t\t\t\t.getContentResolver()\n\t\t\t\t.query(CityProvider.TMPCITY_CONTENT_URI,\n\t\t\t\t\t\tnew String[] { CityConstants.PUB_TIME },\n\t\t\t\t\t\tCityConstants.POST_ID + \"=?\", new String[] { postID },\n\t\t\t\t\t\tnull);\n\n\t\tlong time = 0L;\n\t\tif (c.moveToFirst())\n\t\t\ttime = c.getLong(c.getColumnIndex(CityConstants.PUB_TIME));\n\t\treturn time;\n\t}\n\n\tpublic static WeatherInfo getWeatherInfo(Context context, String postID,\n\t\t\tString result) throws JSONException {\n\t\tString language = context.getResources().getConfiguration().locale\n\t\t\t\t.toString();\n\t\tJSONObject response = new JSONObject(TextUtils.isEmpty(result) ? \"\"\n\t\t\t\t: result);\n\t\tForecast forecast = WeatherController.convertToNewForecast(response,\n\t\t\t\tlanguage, postID);\n\t\t// Log.i(\"way\", \"jsonObjectRequest forecast = \" + forecast);\n\n\t\tRealTime realTime = WeatherController.convertToNewRealTime(\n\t\t\t\tresponse.getJSONObject(\"realtime\"), language, postID);\n\t\t// Log.i(\"way\", \"realTime = \" + realTime);\n\n\t\tAlerts alerts = WeatherController.convertToNewAlert(\n\t\t\t\tresponse.getJSONArray(\"alert\"), postID);\n\t\t// Log.i(\"way\", \"alerts = \" + alerts);\n\n\t\tIndex index = WeatherController.convertToNewIndex(response, language,\n\t\t\t\tpostID);\n\t\t// Log.i(\"way\", \"index = \" + index);\n\n\t\tAQI aqi = WeatherController.convertToNewAQI(\n\t\t\t\tresponse.getJSONObject(\"aqi\"), language, postID);\n\t\treturn new WeatherInfo(realTime, forecast, aqi, index, alerts);\n\t}\n\n\tpublic static boolean isEmpty(WeatherInfo info) {\n\t\tif (info == null)\n\t\t\treturn true;\n\t\tif (info.getRealTime() == null\n\t\t\t\t|| info.getRealTime().getAnimation_type() < 0)\n\t\t\treturn true;\n\t\tif (info.getForecast() == null || info.getForecast().getType(1) < 0)\n\t\t\treturn true;\n\t\t// if (info.getAqi() == null || info.getAqi().getAqi() < 0)\n\t\t// return true;\n\t\tif (info.getIndex() == null || info.getIndex().getIndex() == null)\n\t\t\treturn true;\n\t\treturn false;\n\t}\n\n\tpublic static boolean isEmpty(RealTime info) {\n\t\tif (info == null || info.getAnimation_type() < 0)\n\t\t\treturn true;\n\t\treturn false;\n\t}\n\n\tpublic static boolean isEmpty(Forecast info) {\n\t\tif (info == null || info.getType(1) < 0)\n\t\t\treturn true;\n\t\treturn false;\n\t}\n\n\tpublic static boolean isEmpty(AQI info) {\n\t\tif (info == null || info.getAqi() < 0)\n\t\t\treturn true;\n\t\treturn false;\n\t}\n\n\tpublic static boolean isEmpty(Index info) {\n\t\tif (info == null || info.getIndex() == null\n\t\t\t\t|| info.getIndex().get(0) == null)\n\t\t\treturn true;\n\t\treturn false;\n\t}\n\n\tpublic static boolean isEmpty(Alerts info) {\n\t\tif (info == null || info.getArryAlert() == null\n\t\t\t\t|| info.getArryAlert().get(0) == null)\n\t\t\treturn true;\n\t\treturn false;\n\t}\n\n}"
  },
  {
    "path": "WayHoo/src/com/way/weather/plugin/spider/WeatherUtilities.java",
    "content": "package com.way.weather.plugin.spider;\n\nimport java.util.Locale;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport android.text.TextUtils;\n\npublic class WeatherUtilities {\n\tprivate static String checkLanguageSuport(String language) {\n\t\tif (WeatherConstants.SURPORTTED_LANGUAGE_LIST.contains(language\n\t\t\t\t.toLowerCase()))\n\t\t\treturn language.toLowerCase();\n\t\treturn Locale.US.toString().toLowerCase();\n\t}\n\n\tpublic static String getAQISource(String paramString) {\n\t\tString str = checkLanguageSuport(paramString);\n\t\treturn ((String) WeatherConstants.AQI_SOURCE_LANGUAGE_MAP.get(str));\n\t}\n\n\tpublic static int getAnimationType(String weatherStr) {\n\t\tint type = -1;\n\t\tString[] strs = weatherStr.split(\"转\");\n\t\tif (strs.length > 1) {\n\t\t\tString[] arrayOfString3 = strs[0].split(\"到\");\n\t\t\tif (arrayOfString3.length > 1)\n\t\t\t\ttype =  WeatherConstants.WEATHER_ANIMATION_MAP\n\t\t\t\t\t\t.get(arrayOfString3[1]);\n\t\t\telse {\n\t\t\t\ttype =  WeatherConstants.WEATHER_ANIMATION_MAP\n\t\t\t\t\t\t.get(strs[0]);\n\t\t\t}\n\t\t} else {\n\t\t\tString[] arrayOfString2 = weatherStr.split(\"到\");\n\t\t\tif (arrayOfString2.length > 1)\n\t\t\t\ttype =  WeatherConstants.WEATHER_ANIMATION_MAP\n\t\t\t\t\t\t.get(arrayOfString2[1]);\n\t\t\telse\n\t\t\t\ttype =  WeatherConstants.WEATHER_ANIMATION_MAP\n\t\t\t\t\t\t.get(weatherStr);\n\t\t}\n\t\treturn type;\n\t}\n\n\tpublic static int getAqi(String aqiStr) {\n\t\tint aqi = WeatherConstants.NO_VALUE_FLAG;\n\t\ttry {\n\t\t\taqi = Integer.parseInt(aqiStr);\n\t\t} catch (NumberFormatException e) {\n\t\t}\n\t\treturn aqi;\n\t}\n\n\tpublic static String getAqiDesc(int aqiLevel, String language) {\n\t\tString languageKey = checkLanguageSuport(language);\n\t\tif (aqiLevel == 0)\n\t\t\treturn WeatherConstants.AQI_DESC_LANGUAGE_MAP.get(languageKey).get(\n\t\t\t\t\tInteger.valueOf(0));\n\t\telse if ((aqiLevel > 0) && (aqiLevel <= 50))\n\t\t\treturn WeatherConstants.AQI_DESC_LANGUAGE_MAP.get(languageKey).get(\n\t\t\t\t\tInteger.valueOf(50));\n\t\telse if ((aqiLevel > 50) && (aqiLevel <= 100))\n\t\t\treturn WeatherConstants.AQI_DESC_LANGUAGE_MAP.get(languageKey).get(\n\t\t\t\t\tInteger.valueOf(100));\n\t\telse if ((aqiLevel > 100) && (aqiLevel <= 150))\n\t\t\treturn WeatherConstants.AQI_DESC_LANGUAGE_MAP.get(languageKey).get(\n\t\t\t\t\tInteger.valueOf(150));\n\t\telse if ((aqiLevel > 150) && (aqiLevel <= 200))\n\t\t\treturn WeatherConstants.AQI_DESC_LANGUAGE_MAP.get(languageKey).get(\n\t\t\t\t\tInteger.valueOf(200));\n\t\telse if ((aqiLevel > 200) && (aqiLevel <= 300))\n\t\t\treturn WeatherConstants.AQI_DESC_LANGUAGE_MAP.get(languageKey).get(\n\t\t\t\t\tInteger.valueOf(300));\n\t\telse if ((aqiLevel > 300) && (aqiLevel < 500))\n\t\t\treturn WeatherConstants.AQI_DESC_LANGUAGE_MAP.get(languageKey).get(\n\t\t\t\t\tInteger.valueOf(500));\n\t\treturn WeatherConstants.AQI_DESC_LANGUAGE_MAP.get(languageKey).get(\n\t\t\t\tInteger.valueOf(500));\n\t}\n\n\tpublic static String getAqiLevel(int aqiLevel, String language) {\n\t\tString languageKey = checkLanguageSuport(language);\n\t\tif (aqiLevel == 0)\n\t\t\treturn WeatherConstants.AQI_LEVEL_LANGUAGE_MAP.get(languageKey)\n\t\t\t\t\t.get(Integer.valueOf(0));\n\t\telse if ((aqiLevel > 0) && (aqiLevel <= 50))\n\t\t\treturn WeatherConstants.AQI_LEVEL_LANGUAGE_MAP.get(languageKey)\n\t\t\t\t\t.get(Integer.valueOf(50));\n\t\telse if ((aqiLevel > 50) && (aqiLevel <= 100))\n\t\t\treturn WeatherConstants.AQI_LEVEL_LANGUAGE_MAP.get(languageKey)\n\t\t\t\t\t.get(Integer.valueOf(100));\n\t\telse if ((aqiLevel > 100) && (aqiLevel <= 150))\n\t\t\treturn WeatherConstants.AQI_LEVEL_LANGUAGE_MAP.get(languageKey)\n\t\t\t\t\t.get(Integer.valueOf(150));\n\t\telse if ((aqiLevel > 150) && (aqiLevel <= 200))\n\t\t\treturn WeatherConstants.AQI_LEVEL_LANGUAGE_MAP.get(languageKey)\n\t\t\t\t\t.get(Integer.valueOf(200));\n\t\telse if ((aqiLevel > 200) && (aqiLevel <= 300))\n\t\t\treturn WeatherConstants.AQI_LEVEL_LANGUAGE_MAP.get(languageKey)\n\t\t\t\t\t.get(Integer.valueOf(300));\n\t\telse if ((aqiLevel > 300) && (aqiLevel < 500))\n\t\t\treturn WeatherConstants.AQI_LEVEL_LANGUAGE_MAP.get(languageKey)\n\t\t\t\t\t.get(Integer.valueOf(500));\n\t\treturn WeatherConstants.AQI_LEVEL_LANGUAGE_MAP.get(languageKey).get(\n\t\t\t\tInteger.valueOf(500));\n\t}\n\n\tpublic static String getChina(String paramString) {\n\t\tString str = checkLanguageSuport(paramString);\n\t\treturn ((String) WeatherConstants.CHINA_LANGUAGE_MAP.get(str));\n\t}\n\n\tpublic static int getHumidity(String humidity) {\n\t\treturn Integer.parseInt(humidity.split(\"%\")[0]);\n\t}\n\n\tpublic static String getIndexDesc(String indexTitle, String indexType,\n\t\t\tString language) {\n\t\tString languageKey = checkLanguageSuport(language);\n\t\treturn WeatherConstants.INDEX_DESC_LANGUAGE_MAP.get(languageKey)\n\t\t\t\t.get(indexTitle).get(indexType);\n\t}\n\n\tpublic static String getIndexDetail(String paramString1,\n\t\t\tString paramString2, String language) {\n\t\tString languageKey = checkLanguageSuport(language);\n\t\treturn WeatherConstants.INDEX_DETAIL_LANGUAGE_MAP.get(languageKey)\n\t\t\t\t.get(paramString1).get(paramString2);\n\t}\n\n\tpublic static String getIndexTitle(String indexTitleKey, String language) {\n\t\tString languageKey = checkLanguageSuport(language);\n\t\treturn WeatherConstants.INDEX_LANGUAGE_MAP.get(languageKey).get(\n\t\t\t\tindexTitleKey);\n\t}\n\n\tpublic static Integer getIndexType(String indexTypeKey) {\n\t\treturn WeatherConstants.INDEX_TYPE.get(indexTypeKey);\n\t}\n\n\tpublic static int getIntFromJSON(JSONObject jsonObject, String paramString) {\n\t\tint result = WeatherConstants.NO_VALUE_FLAG;\n\t\ttry {\n\t\t\tresult = jsonObject.getInt(paramString);\n\t\t} catch (JSONException e) {\n\t\t}\n\t\treturn result;\n\t}\n\n\tpublic static String getWeather(String weatherStr) {\n\t\tString[] arrayOfString = weatherStr.split(\"转\");\n\t\tif (arrayOfString.length > 1) {\n\t\t\tString str1 = (String) WeatherConstants.CN_WEATHER_TYPE_MAP\n\t\t\t\t\t.get(arrayOfString[0]);\n\t\t\tString str2 = (String) WeatherConstants.CN_WEATHER_TYPE_MAP\n\t\t\t\t\t.get(arrayOfString[1]);\n\t\t\treturn str1 + \"-\" + str2;\n\t\t}\n\t\treturn ((String) WeatherConstants.CN_WEATHER_TYPE_MAP.get(weatherStr));\n\t}\n\n\tpublic static WeatherName getWeatherName(String weatherStr, String language) {\n\t\tWeatherName weatherName = new WeatherName();\n\t\tString languageKey = checkLanguageSuport(language);\n\t\tString[] strs = weatherStr.split(\"转\");\n\t\tif (strs.length > 1) {\n\t\t\tString str4 = WeatherConstants.CN_WEATHER_TYPE_MAP.get(strs[0]);\n\t\t\tString str5 = WeatherConstants.CN_WEATHER_TYPE_MAP.get(strs[1]);\n\t\t\tString str6 = WeatherConstants.WEATHER_TYPE_LANGUAGE_MAP.get(\n\t\t\t\t\tlanguageKey).get(str4);\n\t\t\tString str7 = WeatherConstants.WEATHER_TYPE_LANGUAGE_MAP.get(\n\t\t\t\t\tlanguageKey).get(str5);\n\t\t\tString str8 = WeatherConstants.TRANSFER_LANGUAGE_MAP\n\t\t\t\t\t.get(languageKey);\n\t\t\tweatherName.setName(str6 + str8 + str7);\n\t\t\tweatherName.setFrom(str6);\n\t\t\tweatherName.setTo(str7);\n\t\t\treturn weatherName;\n\t\t}\n\t\tString key = WeatherConstants.CN_WEATHER_TYPE_MAP.get(weatherStr);\n\t\tString name = WeatherConstants.WEATHER_TYPE_LANGUAGE_MAP.get(\n\t\t\t\tlanguageKey).get(key);\n\t\tweatherName.setName(name);\n\t\tweatherName.setFrom(name);\n\t\tweatherName.setTo(name);\n\t\treturn weatherName;\n\t}\n\n\tpublic static String getWind(String windStr, String language) {\n\t\tString resultStr = \"\";\n\t\tString[] winds = windStr.split(\"转\");\n\t\tString languageKey = checkLanguageSuport(language);\n\t\tif (winds.length > 1) {\n\t\t\tString windReal = WeatherConstants.CN_WIND_TYPE_MAP.get(winds[0]);\n\t\t\tresultStr = WeatherConstants.WIND_TYPE_LANGUAGE_MAP\n\t\t\t\t\t.get(languageKey).get(windReal);\n\t\t\tif (resultStr == null)\n\t\t\t\tresultStr = (String) WeatherConstants.CN_WIND_TYPE_MAP\n\t\t\t\t\t\t.get(winds[1]);\n\t\t} else {\n\t\t\tresultStr = WeatherConstants.WIND_TYPE_LANGUAGE_MAP\n\t\t\t\t\t.get(languageKey).get(windStr);\n\t\t\tif (resultStr == null)\n\t\t\t\tresultStr = (String) WeatherConstants.CN_WIND_TYPE_MAP\n\t\t\t\t\t\t.get(resultStr);\n\t\t}\n\t\treturn resultStr;\n\t}\n\n\tpublic static String getWind(String fx, String fl, String language) {\n\t\tString resultStr = \"\";\n\t\tString fxValue = WeatherConstants.CN_WIND_TYPE_MAP.get(fx);\n\t\tif (fxValue == null)\n\t\t\tfxValue = \"0\";\n\t\tString languageKey = checkLanguageSuport(language);\n\t\tString[] winds = fl.split(\"转\");\n\t\tif (winds.length > 1) {\n\t\t\tString windBefore = (String) (WeatherConstants.WIND_LEVEL_LANGUAGE_MAP\n\t\t\t\t\t.get(languageKey)).get(winds[0]);\n\t\t\tString windAfter = (String) (WeatherConstants.WIND_LEVEL_LANGUAGE_MAP\n\t\t\t\t\t.get(languageKey)).get(winds[1]);\n\t\t\tString transferStr = (String) WeatherConstants.TRANSFER_LANGUAGE_MAP\n\t\t\t\t\t.get(languageKey);\n\t\t\tresultStr = WeatherConstants.WIND_TYPE_LANGUAGE_MAP\n\t\t\t\t\t.get(languageKey).get(fxValue)\n\t\t\t\t\t+ WeatherConstants.WIND_TYPE_CONNECTTOR_LANGUAGE_MAP\n\t\t\t\t\t\t\t.get(languageKey)\n\t\t\t\t\t+ windBefore\n\t\t\t\t\t+ transferStr\n\t\t\t\t\t+ windAfter;\n\t\t} else {\n\t\t\tresultStr = (WeatherConstants.WIND_TYPE_LANGUAGE_MAP\n\t\t\t\t\t.get(languageKey)).get(fxValue)\n\t\t\t\t\t+ WeatherConstants.WIND_TYPE_CONNECTTOR_LANGUAGE_MAP\n\t\t\t\t\t\t\t.get(languageKey)\n\t\t\t\t\t+ WeatherConstants.WIND_LEVEL_LANGUAGE_MAP.get(languageKey)\n\t\t\t\t\t\t\t.get(fl);\n\t\t}\n\t\tif ((!(TextUtils.isEmpty(language)))\n\t\t\t\t&& (language.toLowerCase().contains(\"en\")))\n\t\t\tresultStr = \"Wind: \" + resultStr;\n\t\treturn resultStr;\n\t}\n\n\tpublic static String getWindIndexDetail(String windIndex, String language) {\n\t\tString languageKey = checkLanguageSuport(language);\n\t\tString[] strs = windIndex.split(\"转\");\n\t\tif (strs.length > 1)\n\t\t\treturn WeatherConstants.WIND_LEVEL_DETAIL_LANGUAGE_MAP.get(languageKey)\n\t\t\t\t\t.get(strs[1]);\n\t\treturn WeatherConstants.WIND_LEVEL_DETAIL_LANGUAGE_MAP.get(languageKey).get(\n\t\t\t\twindIndex);\n\t}\n\n\tpublic static class WeatherName {\n\t\tString from;\n\t\tString name;\n\t\tString to;\n\n\t\tpublic String getFrom() {\n\t\t\treturn this.from;\n\t\t}\n\n\t\tpublic String getName() {\n\t\t\treturn this.name;\n\t\t}\n\n\t\tpublic String getTo() {\n\t\t\treturn this.to;\n\t\t}\n\n\t\tpublic void setFrom(String paramString) {\n\t\t\tthis.from = paramString;\n\t\t}\n\n\t\tpublic void setName(String paramString) {\n\t\t\tthis.name = paramString;\n\t\t}\n\n\t\tpublic void setTo(String paramString) {\n\t\t\tthis.to = paramString;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/weather/plugin/util/Constants.java",
    "content": "package com.way.weather.plugin.util;\n\npublic class Constants {\n\tpublic static final int NO_VALUE_FLAG = -999;//无\n\tpublic static final int SUNNY = 0;//晴\n\tpublic static final int CLOUDY = 1;//多云\n\tpublic static final int OVERCAST = 2;//阴\n\tpublic static final int FOGGY = 3;//雾\n\tpublic static final int SEVERE_STORM = 4;//飓风\n\tpublic static final int HEAVY_STORM = 5;//大暴风雨\n\tpublic static final int STORM = 6;//暴风雨\n\tpublic static final int THUNDERSHOWER = 7;//雷阵雨\n\tpublic static final int SHOWER = 8;//阵雨\n\tpublic static final int HEAVY_RAIN = 9;//大雨\n\tpublic static final int MODERATE_RAIN = 10;//中雨\n\tpublic static final int LIGHT_RAIN = 11;//小雨\n\tpublic static final int SLEET = 12;//雨夹雪\n\tpublic static final int SNOWSTORM = 13;//暴雪\n\tpublic static final int SNOW_SHOWER = 14;//阵雪\n\tpublic static final int HEAVY_SNOW = 15;//大雪\n\tpublic static final int MODERATE_SNOW = 16;//中雪\n\tpublic static final int LIGHT_SNOW = 17;//小雪\n\tpublic static final int STRONGSANDSTORM = 18;//强沙尘暴\n\tpublic static final int SANDSTORM = 19;//沙尘暴\n\tpublic static final int SAND = 20;//沙尘\n\tpublic static final int BLOWING_SAND = 21;//风沙\n\tpublic static final int ICE_RAIN = 22;//冻雨\n\tpublic static final int DUST = 23;//尘土\n\tpublic static final int HAZE = 24;//霾\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/weather/plugin/util/NetUtil.java",
    "content": "package com.way.weather.plugin.util;\r\n\r\nimport android.content.Context;\r\nimport android.net.ConnectivityManager;\r\nimport android.net.NetworkInfo.State;\r\n\r\npublic class NetUtil {\r\n\tpublic static final int NETWORN_NONE = 0;\r\n\tpublic static final int NETWORN_WIFI = 1;\r\n\tpublic static final int NETWORN_MOBILE = 2;\r\n\r\n\tpublic static int getNetworkState(Context context) {\r\n\t\tConnectivityManager connManager = (ConnectivityManager) context\r\n\t\t\t\t.getSystemService(Context.CONNECTIVITY_SERVICE);\r\n\r\n\t\t// Wifi\r\n\t\tState state = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI)\r\n\t\t\t\t.getState();\r\n\t\tif (state == State.CONNECTED || state == State.CONNECTING) {\r\n\t\t\treturn NETWORN_WIFI;\r\n\t\t}\r\n\r\n\t\t// 3G\r\n\t\tstate = connManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE)\r\n\t\t\t\t.getState();\r\n\t\tif (state == State.CONNECTED || state == State.CONNECTING) {\r\n\t\t\treturn NETWORN_MOBILE;\r\n\t\t}\r\n\t\treturn NETWORN_NONE;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "WayHoo/src/com/way/yahoo/AboutActivity.java",
    "content": "package com.way.yahoo;\n\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager.NameNotFoundException;\nimport android.os.Bundle;\nimport android.text.util.Linkify;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.TextView;\n\nimport com.way.ui.swipeback.SwipeBackActivity;\nimport com.way.upgrade.core.CheckNewVersionListener;\nimport com.way.upgrade.core.UpgradeManager;\n\npublic class AboutActivity extends SwipeBackActivity {\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.about);\n\t\tTextView tv = (TextView) findViewById(R.id.app_information);\n\t\tLinkify.addLinks(tv, Linkify.ALL);\n\t\t((TextView) findViewById(R.id.city_title)).setText(\"关 于\");\n\t\tfindViewById(R.id.back_image).setOnClickListener(new OnClickListener() {\n\n\t\t\t@Override\n\t\t\tpublic void onClick(View v) {\n\t\t\t\tfinish();\n\t\t\t}\n\t\t});\n\t\tTextView version = (TextView) findViewById(R.id.subVersion);\n\t\ttry {\n\t\t\tfinal PackageInfo packageInfo = getPackageManager().getPackageInfo(\n\t\t\t\t\tgetPackageName(), 0);\n\t\t\tversion.setText(packageInfo.versionName);\n\t\t} catch (final NameNotFoundException e) {\n\t\t\tversion.setText(\"?\");\n\t\t}\n\t\tfinal UpgradeManager upgradeMangeer = UpgradeManager.newInstance(this);\n\t\tupgradeMangeer.askForNewVersionFlag(new CheckNewVersionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void checkNewVersion(boolean result) {\n\t\t\t\tif (result)\n\t\t\t\t\tupgradeMangeer.askForNewVersion();\n\t\t\t}\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/yahoo/App.java",
    "content": "package com.way.yahoo;\n\nimport im.fir.sdk.FIR;\nimport android.app.Application;\n\nimport com.android.volley.RequestQueue;\nimport com.android.volley.toolbox.Volley;\nimport com.way.common.util.SystemUtils;\n\npublic class App extends Application {\n\tprivate static App mApplication;\n\tprivate static RequestQueue mVolleyRequestQueue;\n\n\tpublic static synchronized RequestQueue getVolleyRequestQueue() {\n\t\tif (mVolleyRequestQueue == null)\n\t\t\tmVolleyRequestQueue = Volley.newRequestQueue(mApplication);\n\t\treturn mVolleyRequestQueue;\n\t}\n\n\tpublic static synchronized App getApplication() {\n\t\treturn mApplication;\n\t}\n\n\t@Override\n\tpublic void onCreate() {\n\t\tFIR.init(this);\n\t\tsuper.onCreate();\n\t\tmApplication = this;\n\t\tSystemUtils.copyDB(this);// 程序第一次运行将数据库copy过去\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/yahoo/BaseActivity.java",
    "content": "package com.way.yahoo;\n\nimport java.util.List;\n\nimport android.app.Activity;\nimport android.content.ContentResolver;\nimport android.content.ContentValues;\nimport android.database.Cursor;\nimport android.os.Bundle;\nimport android.widget.Toast;\n\nimport com.way.beans.City;\nimport com.way.common.util.LocationUtils;\nimport com.way.common.util.LocationUtils.LocationListener;\nimport com.way.common.util.NetUtil;\nimport com.way.common.util.SystemUtils;\nimport com.way.db.CityProvider;\nimport com.way.db.CityProvider.CityConstants;\nimport com.way.ui.swipeback.SwipeBackActivity;\nimport com.way.weather.plugin.bean.WeatherInfo;\nimport com.way.weather.plugin.spider.WeatherSpider;\n\npublic class BaseActivity extends SwipeBackActivity {\n\t//public static final String AUTO_LOCATION_CITY_KEY = \"auto_location\";\n\tprotected ContentResolver mContentResolver;\n\tprotected Activity mActivity;\n\tprotected LocationUtils mLocationUtils;\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tinitDatas();\n\t}\n\n\tprivate void initDatas() {\n\t\tmActivity = this;\n\t\tmContentResolver = getContentResolver();\n\t}\n\n\t@Override\n\tprotected void onResume() {\n\t\tsuper.onResume();\n\t}\n\n\tprotected List<City> getTmpCities() {\n\t\tCursor tmpCityCursor = mContentResolver.query(\n\t\t\t\tCityProvider.TMPCITY_CONTENT_URI, null, null, null, CityConstants.ORDER_INDEX + \" ASC\");\n\t\treturn SystemUtils.getTmpCities(tmpCityCursor);\n\t}\n\n\tprotected void startLocation(LocationListener cityNameStatus) {\n\t\tif (NetUtil.getNetworkState(this) == NetUtil.NETWORN_NONE) {\n\t\t\tToast.makeText(this, R.string.net_error, Toast.LENGTH_SHORT).show();\n\t\t\treturn;\n\t\t}\n\t\tif (mLocationUtils == null)\n\t\t\tmLocationUtils = new LocationUtils(this, cityNameStatus);\n\t\tif (!mLocationUtils.isStarted()) {\n\t\t\tmLocationUtils.startLocation();// 开始定位\n\t\t}\n\t}\n\n\tprotected void stopLocation() {\n\t\tif (mLocationUtils != null && mLocationUtils.isStarted())\n\t\t\tmLocationUtils.stopLocation();\n\t}\n\n\tprotected City getLocationCityFromDB(String name) {\n\t\tCity city = new City();\n\t\tcity.setName(name);\n\t\tCursor c = mContentResolver.query(CityProvider.CITY_CONTENT_URI,\n\t\t\t\tnew String[] { CityConstants.POST_ID }, CityConstants.NAME\n\t\t\t\t\t\t+ \"=?\", new String[] { name }, null);\n\t\tif (c != null && c.moveToNext())\n\t\t\tcity.setPostID(c.getString(c.getColumnIndex(CityConstants.POST_ID)));\n\t\treturn city;\n\t}\n\n\tprotected void addOrUpdateLocationCity(City city) {\n\t\t// 先删除已定位城市\n\t\tmContentResolver.delete(CityProvider.TMPCITY_CONTENT_URI,\n\t\t\t\tCityConstants.ISLOCATION + \"=?\", new String[] { \"1\" });\n\n\t\t// 存储\n\t\tContentValues tmpContentValues = new ContentValues();\n\t\ttmpContentValues.put(CityConstants.NAME, city.getName());\n\t\ttmpContentValues.put(CityConstants.POST_ID, city.getPostID());\n\t\ttmpContentValues.put(CityConstants.REFRESH_TIME, 0L);// 无刷新时间\n\t\ttmpContentValues.put(CityConstants.ISLOCATION, 1);// 手动选择的城市存储为0\n\t\ttmpContentValues.put(CityConstants.ORDER_INDEX, getTmpCities().size());\n\t\tmContentResolver.insert(CityProvider.TMPCITY_CONTENT_URI,\n\t\t\t\ttmpContentValues);\n\t}\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/yahoo/FeedBackActivity.java",
    "content": "package com.way.yahoo;\n\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.text.TextUtils;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.Button;\nimport android.widget.EditText;\nimport android.widget.TextView;\n\nimport com.way.common.util.T;\nimport com.way.ui.swipeback.SwipeBackActivity;\n\npublic class FeedBackActivity extends SwipeBackActivity {\n\tprivate EditText mFeedBackEt;\n\tprivate Button mSendBtn;\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.feed_back_view);\n\t\t\n\t\t((TextView) findViewById(R.id.city_title)).setText(\"信息反馈\");\n\t\tfindViewById(R.id.back_image).setOnClickListener(new OnClickListener() {\n\n\t\t\t@Override\n\t\t\tpublic void onClick(View v) {\n\t\t\t\tfinish();\n\t\t\t}\n\t\t});\n\t\tmFeedBackEt = (EditText) findViewById(R.id.fee_back_edit);\n\t\tmSendBtn = (Button) findViewById(R.id.feed_back_btn);\n\t\tmSendBtn.setOnClickListener(new OnClickListener() {\n\n\t\t\t@Override\n\t\t\tpublic void onClick(View v) {\n\t\t\t\tString content = mFeedBackEt.getText().toString();\n\t\t\t\tif (!TextUtils.isEmpty(content)) {\n\t\t\t\t\tIntent intent = new Intent(Intent.ACTION_SENDTO);\n\t\t\t\t\tintent.setType(\"text/plain\");\n\t\t\t\t\tintent.putExtra(Intent.EXTRA_SUBJECT, \"威震天气 - 信息反馈\");\n\t\t\t\t\tintent.putExtra(Intent.EXTRA_TEXT, content);\n\t\t\t\t\tintent.setData(Uri.parse(\"mailto:way.ping.li@gmail.com\"));\n\t\t\t\t\tintent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n\t\t\t\t\tFeedBackActivity.this.startActivity(intent);\n\t\t\t\t} else {\n\t\t\t\t\tT.showShort(FeedBackActivity.this, \"亲,多说几句嘛!么么哒！\");\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/yahoo/MainActivity.java",
    "content": "package com.way.yahoo;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.util.List;\n\nimport net.simonvt.menudrawer.MenuDrawer;\nimport net.simonvt.menudrawer.MenuDrawer.OnDrawerStateChangeListener;\n\nimport org.json.JSONException;\n\nimport android.app.Dialog;\nimport android.content.Intent;\nimport android.graphics.Bitmap;\nimport android.net.Uri;\nimport android.os.AsyncTask;\nimport android.os.Bundle;\nimport android.support.v4.view.ViewPager;\nimport android.support.v4.view.ViewPager.OnPageChangeListener;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.AdapterView;\nimport android.widget.Button;\nimport android.widget.FrameLayout;\nimport android.widget.ImageView;\nimport android.widget.ListView;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.viewpagerindicator.CirclePageIndicator;\nimport com.way.adapter.ParallaxPagerTransformer;\nimport com.way.adapter.SideMenuAdapter;\nimport com.way.adapter.WeatherPagerAdapter;\nimport com.way.beans.Category;\nimport com.way.beans.City;\nimport com.way.beans.Item;\nimport com.way.common.util.L;\nimport com.way.common.util.PreferenceUtils;\nimport com.way.common.util.SystemUtils;\nimport com.way.common.util.T;\nimport com.way.common.util.TimeUtils;\nimport com.way.util.blur.jni.BitmapUtils;\nimport com.way.util.blur.jni.FrostedGlassUtil;\nimport com.way.weather.plugin.bean.WeatherInfo;\nimport com.way.weather.plugin.spider.WeatherSpider;\n\npublic class MainActivity extends BaseActivity implements OnClickListener,\n\t\tOnPageChangeListener {\n\tpublic static final String FIRST_RUN_APP = \"firstRunApp\";\n\tprivate static final String INSTANCESTATE_TAB = \"tab_index\";\n\tprivate String mShareNormalStr = \"#简洁天气#提醒您:今天%s,%s,%s,%s,\";// 日期、城市、天气、温度\n\tprivate String mAqiShareStr = \"空气质量指数(AQI):%s μg/m³,等级[%s];PM2.5浓度值:%s μg/m³。%s \";// aqi、等级、pm2.5、建议\n\tprivate String mShareEndStr = \"（请关注博客：http://blog.csdn.net/way_ping_li）\";\n\tprivate MenuDrawer mMenuDrawer;\n\tprivate SideMenuAdapter mMenuAdapter;\n\tprivate int mPagerOffsetPixels;\n\tprivate int mPagerPosition;\n\tprivate TextView mTitleTextView;\n\tprivate ImageView mBlurImageView;\n\tprivate ImageView mShareBtn;\n\tprivate ImageView mLocationIV;\n\tprivate Button mAddCityBtn;\n\n\tprivate ListView mMenuListView;\n\tprivate FrameLayout mRootView;\n\tprivate ViewPager mMainViewPager;\n\tprivate CirclePageIndicator mCirclePageIndicator;\n\tprivate WeatherPagerAdapter mFragmentAdapter;\n\tprivate List<City> mTmpCities;\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tinitMenuDrawer();\n\t\tmMenuDrawer.setContentView(R.layout.activity_main);\n\t\tinitViews();\n\t}\n\n\t/**\n\t * 连续按两次返回键就退出\n\t */\n\tprivate long firstTime;\n\n\t@Override\n\tpublic void onBackPressed() {\n\t\tif(mMenuDrawer.isMenuVisible()){\n\t\t\tmMenuDrawer.closeMenu(true);\n\t\t\treturn;\n\t\t}\n\t\tif (System.currentTimeMillis() - firstTime < 3000) {\n\t\t\tfinish();\n\t\t} else {\n\t\t\tfirstTime = System.currentTimeMillis();\n\t\t\tT.showShort(this, R.string.press_again_exit);\n\t\t}\n\t}\n\n\tprivate void initViews() {\n\t\tsetSwipeBackEnable(false);\n\t\tmBlurImageView = (ImageView) findViewById(R.id.blur_overlay_img);\n\t\tmRootView = (FrameLayout) findViewById(R.id.root_view);\n\t\tmAddCityBtn = (Button) findViewById(R.id.add_city_btn);\n\t\tmAddCityBtn.setOnClickListener(this);\n\t\tmTitleTextView = (TextView) findViewById(R.id.location_city_textview);\n\t\tmLocationIV = (ImageView) findViewById(R.id.curr_loc_icon);\n\t\tmMainViewPager = (ViewPager) findViewById(R.id.main_viewpager);\n\t\tParallaxPagerTransformer pt = new ParallaxPagerTransformer((R.id.top_part));\n        //pt.setBorder(20);\n        pt.setSpeed(0.5f);\n        mMainViewPager.setPageTransformer(false, pt);\n\t\tmFragmentAdapter = new WeatherPagerAdapter(this);\n\t\tmMainViewPager.setAdapter(mFragmentAdapter);\n\t\tmCirclePageIndicator = (CirclePageIndicator) findViewById(R.id.indicator);\n\t\tmCirclePageIndicator.setViewPager(mMainViewPager);\n\t\tmCirclePageIndicator.setOnPageChangeListener(this);\n\n\t\tmTitleTextView.setOnClickListener(this);\n\t\tfindViewById(R.id.sidebarButton).setOnClickListener(this);\n\t\tmShareBtn = (ImageView) findViewById(R.id.shareButton);\n\t\tmShareBtn.setOnClickListener(this);\n\t}\n\n\t@Override\n\tprotected void onResume() {\n\t\tsuper.onResume();\n\t\tmTmpCities = getTmpCities();\n\t\tif (!mTmpCities.isEmpty()) {\n\t\t\tupdateUI();\n\t\t} else {\n\t\t\t//如果第一次运行应用就需要定位\n\t\t\tif(PreferenceUtils.getPrefBoolean(this, FIRST_RUN_APP, true)){\n\t\t\t\tstartActivity(new Intent(MainActivity.this, QueryCityActivity.class));\n\t\t\t\tPreferenceUtils.setPrefBoolean(this, FIRST_RUN_APP, false);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tprotected void onPause() {\n\t\tsuper.onPause();\n\t\t// 保存默认选择页\n\t\tPreferenceUtils.setPrefInt(this, INSTANCESTATE_TAB,\n\t\t\t\tmMainViewPager.getCurrentItem());\n\t}\n\n\tprivate void visibleAddCityBtn() {\n\t\tmMainViewPager.removeAllViews();\n\t\tmTitleTextView.setText(\"--\");\n\t\tmLocationIV.setVisibility(View.GONE);\n\t\tmAddCityBtn.setVisibility(View.VISIBLE);\n\t\tmShareBtn.setEnabled(false);\n\t}\n\n\tprivate void updateUI() {\n\t\tL.i(\"MainActivity updateUI...\");\n\t\t// 第一次进来没有数据\n\t\tif (mTmpCities.isEmpty()) {\n\t\t\tvisibleAddCityBtn();\n\t\t\treturn;\n\t\t}\n\t\t//mFragmentAdapter = new WeatherPagerAdapter(this);\n\t\tmFragmentAdapter.setData(mTmpCities);\n\t\t//mMainViewPager.setAdapter(mFragmentAdapter);\n\t\tmMainViewPager.setOffscreenPageLimit(mFragmentAdapter.getCount() - 1);\n\t\t//mCirclePageIndicator.setViewPager(mMainViewPager);\n\t\t//mCirclePageIndicator.setOnPageChangeListener(this);\n\t\t\n\t\tmMenuAdapter.addContent(mTmpCities);\n\t\tif (mAddCityBtn.getVisibility() == View.VISIBLE)\n\t\t\tmAddCityBtn.setVisibility(View.GONE);\n\t\tif (mTmpCities.size() > 1)\n\t\t\tmCirclePageIndicator.setVisibility(View.VISIBLE);\n\t\telse\n\t\t\tmCirclePageIndicator.setVisibility(View.GONE);\n\t\tmShareBtn.setEnabled(true);\n\n\t\tint defaultTab = PreferenceUtils.getPrefInt(this, INSTANCESTATE_TAB, 0);\n\t\tif (defaultTab > (mTmpCities.size() - 1))// 防止手动删除城市之后出现数组越界\n\t\t\tdefaultTab = 0;\n\t\tmMainViewPager.setCurrentItem(defaultTab, true);\n\t\tmTitleTextView.setText(mFragmentAdapter.getPageTitle(defaultTab));\n\t\tif (mTmpCities.get(defaultTab).getIsLocation())\n\t\t\tmLocationIV.setVisibility(View.VISIBLE);\n\t\telse\n\t\t\tmLocationIV.setVisibility(View.GONE);\n\t}\n\n\n\t@Override\n\tpublic void onClick(View v) {\n\t\tswitch (v.getId()) {\n\t\tcase R.id.sidebarButton:\n\t\t\tmMenuDrawer.toggleMenu(true);\n\t\t\tbreak;\n\t\tcase R.id.shareButton:\n\t\t\tshareTo();\n\t\t\tbreak;\n\t\tcase R.id.location_city_textview:\n\t\tcase R.id.add_city_btn:\n\t\t\tstartActivity(new Intent(MainActivity.this,\n\t\t\t\t\tManagerCityActivity.class));\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate void shareTo() {\n\t\tnew AsyncTask<Void, Void, File>() {\n\t\t\tDialog dialog;\n\n\t\t\t@Override\n\t\t\tprotected void onPreExecute() {\n\t\t\t\tsuper.onPreExecute();\n\t\t\t\tdialog = SystemUtils.getCustomeDialog(MainActivity.this,\n\t\t\t\t\t\tR.style.load_dialog, R.layout.custom_progress_dialog);\n\t\t\t\tTextView titleTxtv = (TextView) dialog\n\t\t\t\t\t\t.findViewById(R.id.dialogText);\n\t\t\t\ttitleTxtv.setText(R.string.please_wait);\n\t\t\t\tdialog.show();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected File doInBackground(Void... params) {\n\t\t\t\ttry {\n\t\t\t\t\tnew File(getFilesDir(), \"share.png\").deleteOnExit();\n\t\t\t\t\tFileOutputStream fileOutputStream = openFileOutput(\n\t\t\t\t\t\t\t\"share.png\", 1);\n\t\t\t\t\tmRootView.setDrawingCacheEnabled(true);\n\t\t\t\t\tmRootView.getDrawingCache().compress(\n\t\t\t\t\t\t\tBitmap.CompressFormat.PNG, 100, fileOutputStream);\n\t\t\t\t\treturn new File(getFilesDir(), \"share.png\");\n\t\t\t\t} catch (FileNotFoundException e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void onPostExecute(File result) {\n\t\t\t\tsuper.onPostExecute(result);\n\t\t\t\tdialog.dismiss();\n\t\t\t\tif (result == null) {\n\t\t\t\t\tToast.makeText(MainActivity.this, R.string.share_fail,\n\t\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tWeatherInfo info = null;\n\t\t\t\tCity city = mTmpCities.get(\n\t\t\t\t\t\tmMainViewPager.getCurrentItem());\n\t\t\t\tif(city == null)\n\t\t\t\t\treturn;\n\t\t\t\ttry {\n\t\t\t\t\tinfo = WeatherSpider\n\t\t\t\t\t\t.getWeatherInfo(MainActivity.this, city.getPostID(), city.getWeatherInfoStr());\n\t\t\t\t} catch (JSONException e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}catch (Exception e){\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\t\t\t\tif (info == null || info.getRealTime() == null\n\t\t\t\t\t\t|| info.getRealTime().getAnimation_type() < 0) {\n\t\t\t\t\tToast.makeText(MainActivity.this, R.string.share_fail,\n\t\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tString time = TimeUtils.getDateTime(System.currentTimeMillis());\n\t\t\t\tString name = mFragmentAdapter.getPageTitle(\n\t\t\t\t\t\tmMainViewPager.getCurrentItem()).toString();\n\t\t\t\tString weather = info.getRealTime().getWeather_name();\n\t\t\t\tString temp = info.getRealTime().getTemp() + \"°\";\n\n\t\t\t\tString shareStr = mShareNormalStr + mAqiShareStr + mShareEndStr;\n\t\t\t\tif (info.getAqi() == null || info.getAqi().getAqi() < 0) {\n\t\t\t\t\tshareStr = mShareNormalStr + mShareEndStr;\n\t\t\t\t\tshareStr = String.format(shareStr, new Object[] { time,\n\t\t\t\t\t\t\tname, weather, temp });\n\t\t\t\t} else {\n\t\t\t\t\tshareStr = String.format(shareStr, new Object[] { time,\n\t\t\t\t\t\t\tname, weather, temp, info.getAqi().getAqi(),\n\t\t\t\t\t\t\tinfo.getAqi().getAqi_level(),\n\t\t\t\t\t\t\tinfo.getAqi().getPm25(),\n\t\t\t\t\t\t\tinfo.getAqi().getAqi_desc() });\n\t\t\t\t}\n\n\t\t\t\tIntent intent = new Intent(\"android.intent.action.SEND\");\n\t\t\t\tintent.setType(\"image/*\");\n\t\t\t\tintent.putExtra(\"sms_body\", shareStr);\n\t\t\t\tintent.putExtra(\"android.intent.extra.TEXT\", shareStr);\n\t\t\t\tintent.putExtra(\"android.intent.extra.STREAM\",\n\t\t\t\t\t\tUri.fromFile(result));\n\t\t\t\tintent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);\n\t\t\t\tstartActivity(Intent.createChooser(intent, getResources()\n\t\t\t\t\t\t.getString(R.string.share_to)));\n\t\t\t}\n\t\t}.execute();\n\t}\n\n\t@Override\n\tpublic void onPageScrollStateChanged(int state) {\n\t\t// do nothing\n\t}\n\n\t@Override\n\tpublic void onPageScrolled(int position, float positionOffset,\n\t\t\tint positionOffsetPixels) {\n\t\tmPagerPosition = position;\n\t\tmPagerOffsetPixels = positionOffsetPixels;\n\t}\n\n\t@Override\n\tpublic void onPageSelected(int position) {\n\t\tif (position < mFragmentAdapter.getCount() && position >= 0){\n\t\t\tmTitleTextView.setText(mFragmentAdapter.getPageTitle(position));\n\t\t\tmFragmentAdapter.setCurrentPage(position);\n\t\t}\n\t\tif (position >= mTmpCities.size()) {\n\t\t\tmLocationIV.setVisibility(View.GONE);\n\t\t\treturn;\n\t\t}\n\n\t\tCity city = mTmpCities.get(position);\n\t\tif (city != null && city.getIsLocation())\n\t\t\tmLocationIV.setVisibility(View.VISIBLE);\n\t\telse\n\t\t\tmLocationIV.setVisibility(View.GONE);\n\t}\n\n\tprivate void initMenuDrawer() {\n\t\t// 覆盖在View之前的侧边栏菜单\n\t\tmMenuDrawer = MenuDrawer.attach(this, MenuDrawer.Type.BEHIND);\n\t\tmMenuDrawer.setMenuSize(Math.round(0.6f * SystemUtils\n\t\t\t\t.getDisplayWidth(this)));\n\t\t// View之后的侧边栏菜单\n\t\t// mMenuDrawer = MenuDrawer.attach(this, MenuDrawer.Type.BEHIND,\n\t\t// Position.LEFT, MenuDrawer.MENU_DRAG_CONTENT);\n\t\tmMenuListView = (ListView) LayoutInflater.from(this).inflate(\n\t\t\t\tR.layout.sidemenu_listview, null);\n\t\tmMenuDrawer.setMenuView(mMenuListView);\n\t\tmMenuDrawer.setTouchMode(MenuDrawer.TOUCH_MODE_FULLSCREEN);\n\t\tmMenuDrawer\n\t\t\t\t.setOnInterceptMoveEventListener(new MenuDrawer.OnInterceptMoveEventListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic boolean isViewDraggable(View v, int dx, int x, int y) {\n\t\t\t\t\t\tif (v == mMainViewPager) {\n\t\t\t\t\t\t\treturn !(mPagerPosition == 0 && mPagerOffsetPixels == 0)\n\t\t\t\t\t\t\t\t\t|| dx < 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\tmMenuDrawer\n\t\t\t\t.setOnDrawerStateChangeListener(new OnDrawerStateChangeListener() {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onDrawerStateChange(int oldState, int newState) {\n\t\t\t\t\t\t// TODO Auto-generated method stub\n\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onDrawerSlide(float openRatio, int offsetPixels) {\n\t\t\t\t\t\t// TODO Auto-generated method stub\n\t\t\t\t\t\tchangeBlurImageViewAlpha(openRatio);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\tmMenuAdapter = new SideMenuAdapter(this);\n\t\t// mMenuAdapter.addContent(mTmpCities);\n\t\tmMenuListView.setAdapter(mMenuAdapter);\n\t\tmMenuListView.setOnItemClickListener(mItemClickListener);\n\t}\n\n\tprivate void changeBlurImageViewAlpha(float slideOffset) {\n\t\tif (slideOffset <= 0) {\n\t\t\tmBlurImageView.setImageBitmap(null);\n\t\t\tmBlurImageView.setVisibility(View.GONE);\n\t\t\treturn;\n\t\t}\n\t\tif (mBlurImageView.getVisibility() != View.VISIBLE) {\n\t\t\tsetBlurImage();\n\t\t}\n\t\tmBlurImageView.setAlpha(slideOffset);\n\t}\n\n\tprivate void setBlurImage() {\n\t\tmBlurImageView.setImageBitmap(null);\n\t\tmBlurImageView.setVisibility(View.VISIBLE);\n\t\t// do the downscaling for faster processing\n\t\tlong beginBlur = System.currentTimeMillis();\n\t\tBitmap downScaled = BitmapUtils.drawViewToBitmap(mRootView,\n\t\t\t\tmRootView.getWidth(), mRootView.getHeight(), 10);\n\t\t// apply the blur using the renderscript\n\t\tFrostedGlassUtil.getInstance().stackBlur(downScaled, 4);\n\t\t// FrostedGlassUtil.getInstance().boxBlur(downScaled, 4);\n\t\t// FrostedGlassUtil.getInstance().colorWaterPaint(downScaled, 4);\n\t\t// FrostedGlassUtil.getInstance().oilPaint(downScaled, 4);\n\t\tlong engBlur = System.currentTimeMillis();\n\t\tL.i(\"stackBlur cost \" + (engBlur - beginBlur) + \"ms\");\n\t\tmBlurImageView.setImageBitmap(downScaled);\n\t}\n\n\tprivate AdapterView.OnItemClickListener mItemClickListener = new AdapterView.OnItemClickListener() {\n\t\t@Override\n\t\tpublic void onItemClick(AdapterView<?> parent, View view, int position,\n\t\t\t\tlong id) {\n\t\t\tObject item = mMenuAdapter.getItem(position);\n\t\t\tif (item instanceof Category) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tonMenuItemClicked(position, (Item) item);\n\t\t}\n\t};\n\n\tprotected void onMenuItemClicked(int position, Item item) {\n\t\tmMenuDrawer.toggleMenu(true);// 关闭此窗口\n\t\t// Toast.makeText(this, item.mTitleStr, Toast.LENGTH_SHORT).show();\n\t\tswitch (item.mId) {\n\t\tcase Item.INFINITE_ID:\n\t\t\tstartActivity(new Intent(MainActivity.this,\n\t\t\t\t\tManagerCityActivity.class));\n\t\t\tbreak;\n\t\tcase Item.SETTING_ID:\n\n\t\t\tbreak;\n\t\tcase Item.SHARE_ID:\n\n\t\t\tbreak;\n\t\tcase Item.FEEDBACK_ID:\n\t\t\tstartActivity(new Intent(MainActivity.this, FeedBackActivity.class));\n\t\t\tbreak;\n\t\tcase Item.ABOUT_ID:\n\t\t\tstartActivity(new Intent(MainActivity.this, AboutActivity.class));\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tif (mMainViewPager.getCurrentItem() != item.mId)\n\t\t\t\tmMainViewPager.setCurrentItem(item.mId);\n\t\t\tbreak;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/yahoo/ManagerCityActivity.java",
    "content": "package com.way.yahoo;\n\nimport java.util.List;\n\nimport org.json.JSONException;\n\nimport android.content.ContentValues;\nimport android.content.Intent;\nimport android.database.Cursor;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.view.ViewGroup;\nimport android.widget.AdapterView;\nimport android.widget.BaseAdapter;\nimport android.widget.ImageView;\nimport android.widget.ProgressBar;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.android.volley.Response;\nimport com.android.volley.VolleyError;\nimport com.android.volley.toolbox.StringRequest;\nimport com.way.beans.City;\nimport com.way.common.util.L;\nimport com.way.common.util.NetUtil;\nimport com.way.common.util.WeatherIconUtils;\nimport com.way.db.CityProvider;\nimport com.way.db.CityProvider.CityConstants;\nimport com.way.ui.view.DragSortGridView;\nimport com.way.weather.plugin.bean.WeatherInfo;\nimport com.way.weather.plugin.spider.WeatherSpider;\n\npublic class ManagerCityActivity extends BaseActivity implements\n\t\tOnClickListener {\n\tpublic static final int MAX_CITY_NUM = 9;\n\tprivate DragSortGridView mGridView;\n\tprivate CityGridAdapter mAdapter;\n\tprivate ImageView mBackBtn, mRefreshCityBtn, mDividerLine, mEditCityBtn,\n\t\t\tmConfirmCityBtn;\n\tprivate ProgressBar mRefreshProgressBar;\n\tprivate LayoutInflater mInflater;\n\tprivate List<City> mTmpCitys;\n\tprivate static boolean isRefreshMode;\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.city_manager_layout);\n\t\tmTmpCitys = getTmpCities();\n\t\tinitViews();\n\t}\n\n\t@Override\n\tprotected void onPause() {\n\t\tsuper.onPause();\n\t\tL.i(\"liweiping\", ManagerCityActivity.this.getClass().getName()\n\t\t\t\t+ \" onPause...\");\n\t\tupdateRefreshMode(false);// 暂停时更新刷新模式\n\t}\n\n\t@Override\n\tprotected void onStop() {\n\t\tsuper.onStop();\n\t\tL.i(\"liweiping\", \"ManagerCityActivity onStop...\");\n\t}\n\n\tprivate void initViews() {\n\t\tmGridView = (DragSortGridView) findViewById(R.id.my_city);\n\t\tmInflater = LayoutInflater.from(this);\n\t\tmAdapter = new CityGridAdapter();\n\t\tmGridView.setAdapter(mAdapter);\n\t\tmGridView.setOnReorderingListener(dragSortListener);\n\n\t\tmBackBtn = (ImageView) findViewById(R.id.back_image);\n\t\tmRefreshCityBtn = (ImageView) findViewById(R.id.refresh_city);\n\t\tmDividerLine = (ImageView) findViewById(R.id.divider_line);\n\t\tmEditCityBtn = (ImageView) findViewById(R.id.edit_city);\n\t\tmConfirmCityBtn = (ImageView) findViewById(R.id.confirm_city);\n\t\tmRefreshProgressBar = (ProgressBar) findViewById(R.id.refresh_progress);\n\n\t\tmBackBtn.setOnClickListener(this);\n\t\tmRefreshCityBtn.setOnClickListener(this);\n\t\tmEditCityBtn.setOnClickListener(this);\n\t\tmConfirmCityBtn.setOnClickListener(this);\n\t\tmRefreshProgressBar.setOnClickListener(this);\n\t\tupdateBtnStates();\n\t}\n\n\t@Override\n\tpublic void onClick(View v) {\n\t\tswitch (v.getId()) {\n\t\tcase R.id.back_image:\n\t\t\tfinish();\n\t\t\tbreak;\n\t\tcase R.id.refresh_city:// 开始刷新\n\t\t\tupdateRefreshMode(true);\n\t\t\tbreak;\n\t\tcase R.id.refresh_progress:// 取消刷新\n\t\t\tupdateRefreshMode(false);\n\t\t\tbreak;\n\t\tcase R.id.edit_city:\n\t\tcase R.id.confirm_city:\n\t\t\tchangeEditMode();\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate void updateRefreshMode(boolean isRefresh) {\n\t\tif (isRefresh && NetUtil.getNetworkState(this) == NetUtil.NETWORN_NONE) {\n\t\t\tToast.makeText(this, R.string.net_error, Toast.LENGTH_SHORT).show();\n\t\t\treturn;\n\t\t}\n\t\tisRefreshMode = isRefresh;\n\t\tmRefreshProgressBar.setVisibility(isRefresh ? View.VISIBLE\n\t\t\t\t: View.INVISIBLE);\n\t\tmRefreshCityBtn\n\t\t\t\t.setVisibility(isRefresh ? View.INVISIBLE : View.VISIBLE);\n\t\tmEditCityBtn.setEnabled(!isRefresh && (mTmpCitys.size() > 1));\n\t\tmGridView.setEnabled(!isRefresh);\n\t\tmGridView.setOnReorderingListener(isRefresh ? null : dragSortListener);\n\t\t// 开一个异步线程去更新天气或者取消更新\n\t\tif (isRefresh) {\n\t\t\tgetAllWeather();\n\t\t} else {\n\t\t\tmIndex = -1;\n\t\t\tmAdapter.setRefreshingIndex(mIndex);\n\t\t\tApp.getVolleyRequestQueue().cancelAll(\"All\");\n\t\t}\n\n\t}\n\n\tprivate DragSortGridView.OnReorderingListener dragSortListener = new DragSortGridView.OnReorderingListener() {\n\n\t\t@Override\n\t\tpublic void onReordering(int fromPosition, int toPosition) {\n\t\t\tL.d(\"liweiping\", \"onReordering fromPosition:\" + fromPosition\n\t\t\t\t\t+ \",toPosition:\" + toPosition);\n\t\t\tmAdapter.reorder(fromPosition, toPosition);\n\t\t\tchangeSortIndex();\n\t\t}\n\n\t\t@Override\n\t\tpublic void beginRecordering(AdapterView<?> parent, View view,\n\t\t\t\tint position, long id) {\n\t\t\tif (mAdapter.isEditMode)\n\t\t\t\treturn;\n\t\t\tchangeEditMode();\n\t\t}\n\n\t};\n\n\tprivate void changeEditMode() {\n\t\tmAdapter.changeEditMode();\n\t\tif (mAdapter.isEditMode) {\n\t\t\tmConfirmCityBtn.setVisibility(View.VISIBLE);\n\t\t\tmRefreshCityBtn.setVisibility(View.INVISIBLE);\n\t\t\tmDividerLine.setVisibility(View.INVISIBLE);\n\t\t\tmEditCityBtn.setVisibility(View.INVISIBLE);\n\t\t} else {\n\t\t\tmConfirmCityBtn.setVisibility(View.INVISIBLE);\n\t\t\tif (mRefreshProgressBar.getVisibility() != View.VISIBLE)\n\t\t\t\tmRefreshCityBtn.setVisibility(View.VISIBLE);\n\t\t\tmDividerLine.setVisibility(View.VISIBLE);\n\t\t\tmEditCityBtn.setVisibility(View.VISIBLE);\n\t\t}\n\t\tupdateBtnStates();\n\t}\n\n\tprivate void changeSortIndex() {\n\t\tfor (int i = 0; i < mTmpCitys.size(); i++) {\n\t\t\tContentValues contentValues = new ContentValues();\n\t\t\tcontentValues.put(CityConstants.ORDER_INDEX, i);\n\t\t\tString postID = mTmpCitys.get(i).getPostID();\n\t\t\tmContentResolver.update(CityProvider.TMPCITY_CONTENT_URI,\n\t\t\t\t\tcontentValues, CityConstants.POST_ID + \"=?\",\n\t\t\t\t\tnew String[] { postID });// 更新位置\n\t\t}\n\t}\n\n\tprivate void deleteCityFromTable(int position) {\n\t\tCity city = mAdapter.getItem(position);\n\t\t// 从临时城市表中删除\n\t\tmContentResolver\n\t\t\t\t.delete(CityProvider.TMPCITY_CONTENT_URI, CityConstants.POST_ID\n\t\t\t\t\t\t+ \"=?\", new String[] { city.getPostID() });\n\n\t\tupdateUI(false);\n\t\tif (mTmpCitys.isEmpty())// 如果全部被删除完了，更新一下编辑状态\n\t\t\tchangeEditMode();\n\t\tchangeSortIndex();\n\t}\n\n\t@Override\n\tprotected void onActivityResult(int requestCode, int resultCode, Intent data) {\n\t\tsuper.onActivityResult(requestCode, resultCode, data);\n\t\tif (requestCode == 0) {\n\t\t\tif (resultCode == RESULT_OK) {\n\t\t\t\tupdateUI(true);\n\t\t\t\tCity city = (City) data\n\t\t\t\t\t\t.getParcelableExtra(QueryCityActivity.CITY_EXTRA_KEY);\n\t\t\t\tif (city == null)\n\t\t\t\t\treturn;\n\t\t\t\tif (NetUtil.getNetworkState(this) == NetUtil.NETWORN_NONE) {\n\t\t\t\t\tToast.makeText(this, R.string.net_error, Toast.LENGTH_SHORT)\n\t\t\t\t\t\t\t.show();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tgetWeather(city);\n\t\t\t}\n\n\t\t}\n\t}\n\n\tprivate void updateUI(boolean isAdd) {\n\t\tmTmpCitys = getTmpCities();\n\t\tif (isAdd && mTmpCitys.size() >= MAX_CITY_NUM)\n\t\t\tToast.makeText(this, R.string.city_max_toast, Toast.LENGTH_LONG)\n\t\t\t\t\t.show();\n\t\tmAdapter.notifyDataSetChanged();\n\t\tupdateBtnStates();\n\t}\n\n\t/**\n\t * 更新ActionBar按钮状态\n\t */\n\tprivate void updateBtnStates() {\n\t\tmEditCityBtn.setEnabled(mTmpCitys.size() > 1);\n\t\tmRefreshCityBtn.setEnabled(mTmpCitys.size() > 1);\n\t\tmRefreshProgressBar.setEnabled(mTmpCitys.size() > 1);\n\t}\n\n\tprivate class CityGridAdapter extends BaseAdapter {\n\t\tpublic static final int NORMAL_CITY_TYPE = 0;\n\t\tpublic static final int ADD_CITY_TYPE = 1;\n\t\tprivate int refreshingIndex = -1;\n\t\tprivate boolean isEditMode;\n\n\t\tpublic CityGridAdapter() {\n\t\t\tif (mTmpCitys.size() < MAX_CITY_NUM)\n\t\t\t\tmTmpCitys.add(null);\n\t\t}\n\n\t\tpublic void setRefreshingIndex(int position) {\n\t\t\trefreshingIndex = position;\n\t\t\tL.i(\"CityGridAdapter setRefreshingIndex = \" + position);\n\t\t\tnotifyDataSetChanged();\n\t\t}\n\n\t\tpublic boolean isEditMode() {\n\t\t\treturn isEditMode;\n\t\t}\n\n\t\t@Override\n\t\tpublic void notifyDataSetChanged() {\n\t\t\tint lastPosition = ((getCount() - 1) < 0) ? 0 : (getCount() - 1);\n\t\t\tif (isEditMode) {\n\t\t\t\tif (!mTmpCitys.isEmpty() && mTmpCitys.get(lastPosition) == null)// 如果最后一个是空,则编辑模式下移出\n\t\t\t\t\tmTmpCitys.remove(lastPosition);\n\t\t\t} else {\n\t\t\t\tif (mTmpCitys.isEmpty()\n\t\t\t\t\t\t|| ((mTmpCitys.get(lastPosition) != null) && (getCount() < MAX_CITY_NUM)))// 如果最后一个不为空，并且数量小于9个，则添加一个\n\t\t\t\t\tmTmpCitys.add(null);\n\t\t\t}\n\t\t\tsuper.notifyDataSetChanged();\n\t\t}\n\n\t\tpublic void changeEditMode() {\n\t\t\tisEditMode = !isEditMode;\n\n\t\t\tnotifyDataSetChanged();\n\t\t}\n\n\t\tpublic void reorder(int from, int to) {\n\t\t\tif (from != to) {\n\t\t\t\tCity oldCity = mTmpCitys.get(from);\n\t\t\t\tmTmpCitys.remove(from);\n\t\t\t\tmTmpCitys.add(to, oldCity);\n\n\t\t\t\tnotifyDataSetChanged();\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic int getCount() {\n\t\t\treturn mTmpCitys.size();\n\t\t}\n\n\t\t@Override\n\t\tpublic City getItem(int position) {\n\t\t\treturn mTmpCitys.get(position);\n\t\t}\n\n\t\t@Override\n\t\tpublic long getItemId(int position) {\n\t\t\treturn position;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getItemViewType(int position) {\n\t\t\t// return super.getItemViewType(position);\n\t\t\tif (getItem(position) == null)\n\t\t\t\treturn ADD_CITY_TYPE;\n\t\t\treturn NORMAL_CITY_TYPE;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getViewTypeCount() {\n\t\t\treturn 2;\n\t\t}\n\n\t\t@Override\n\t\tpublic View getView(int position, View convertView, ViewGroup parent) {\n\t\t\tViewHolder viewHolder;\n\t\t\tint type = getItemViewType(position);\n\t\t\tif (convertView == null\n\t\t\t\t\t|| convertView.getTag(R.drawable.ic_launcher + type) == null) {\n\t\t\t\tswitch (type) {\n\t\t\t\tcase NORMAL_CITY_TYPE:\n\t\t\t\t\tconvertView = mInflater.inflate(\n\t\t\t\t\t\t\tR.layout.city_manger_grid_item_normal, parent,\n\t\t\t\t\t\t\tfalse);\n\t\t\t\t\tbreak;\n\t\t\t\tcase ADD_CITY_TYPE:\n\t\t\t\t\tconvertView = mInflater.inflate(\n\t\t\t\t\t\t\tR.layout.city_manger_grid_item_add, parent, false);\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tviewHolder = buildHolder(convertView);\n\t\t\t\t// 因为类型不同，所以给viewHolder设置一个标识,标识必须是资源id，不然会挂掉\n\t\t\t\t// 我这里为了区分不同的type，所以加上类型\n\t\t\t\tconvertView.setTag(R.drawable.ic_launcher + type, viewHolder);\n\t\t\t} else {\n\t\t\t\tviewHolder = (ViewHolder) convertView\n\t\t\t\t\t\t.getTag(R.drawable.ic_launcher + type);\n\t\t\t\t// L.i(\"liweiping\", \"getView from cache viewHolder = \"\n\t\t\t\t// + viewHolder.toString());\n\t\t\t}\n\t\t\tbindViewData(viewHolder, position);\n\t\t\treturn convertView;\n\t\t}\n\n\t\tprivate void bindViewData(ViewHolder holder, final int position) {\n\t\t\tCity city = mTmpCitys.get(position);\n\t\t\tWeatherInfo weatherInfo = null;\n\t\t\ttry {\n\t\t\t\tif (city != null) {\n\t\t\t\t\tweatherInfo = WeatherSpider.getWeatherInfo(\n\t\t\t\t\t\t\tManagerCityActivity.this, city.getPostID(),\n\t\t\t\t\t\t\tcity.getWeatherInfoStr());\n\t\t\t\t\t//Log.i(\"way\", \"bindViewData position = \" + position\n\t\t\t\t\t//\t\t+ \"  city.getPostID() = \" + city.getPostID());\n\t\t\t\t}\n\t\t\t} catch (JSONException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t} catch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\tswitch (getItemViewType(position)) {\n\t\t\tcase NORMAL_CITY_TYPE:\n\t\t\t\tif (refreshingIndex == position) {\n\t\t\t\t\tholder.loadingBar.setVisibility(View.VISIBLE);\n\t\t\t\t\tholder.weatherIV.setVisibility(View.GONE);\n\t\t\t\t\tholder.tempTV.setText(\"加载中...\");\n\t\t\t\t} else {\n\t\t\t\t\tholder.loadingBar.setVisibility(View.GONE);\n\t\t\t\t\tholder.weatherIV.setVisibility(View.VISIBLE);\n\t\t\t\t\tif (!WeatherSpider.isEmpty(weatherInfo)\n\t\t\t\t\t\t\t&& !WeatherSpider\n\t\t\t\t\t\t\t\t\t.isEmpty(weatherInfo.getForecast())\n\t\t\t\t\t\t\t&& !WeatherSpider\n\t\t\t\t\t\t\t\t\t.isEmpty(weatherInfo.getRealTime())) {\n\t\t\t\t\t\tholder.tempTV\n\t\t\t\t\t\t\t\t.setText(weatherInfo.getForecast().getTmpLow(1)\n\t\t\t\t\t\t\t\t\t\t+ \"~\"\n\t\t\t\t\t\t\t\t\t\t+ weatherInfo.getForecast().getTmpHigh(\n\t\t\t\t\t\t\t\t\t\t\t\t1) + \"°\");\n\t\t\t\t\t\tholder.weatherIV.setImageResource(WeatherIconUtils\n\t\t\t\t\t\t\t\t.getWeatherIcon(weatherInfo.getRealTime()\n\t\t\t\t\t\t\t\t\t\t.getAnimation_type()));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tholder.tempTV.setText(\"--~--°\");\n\t\t\t\t\t\tholder.weatherIV\n\t\t\t\t\t\t\t\t.setImageResource(R.drawable.xy_weather_ic_default);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tholder.cityTV.setText(city.getName());\n\t\t\t\tif (city.getIsLocation()) {\n\t\t\t\t\tholder.cityTV.setCompoundDrawablesWithIntrinsicBounds(\n\t\t\t\t\t\t\tR.drawable.current_loc_active_26x26, 0, 0, 0);\n\t\t\t\t} else {\n\t\t\t\t\tholder.cityTV.setCompoundDrawablesWithIntrinsicBounds(0, 0,\n\t\t\t\t\t\t\t0, 0);\n\t\t\t\t}\n\t\t\t\tif (isEditMode && !city.getIsLocation())\n\t\t\t\t\tholder.deleteIV.setVisibility(View.VISIBLE);\n\t\t\t\telse\n\t\t\t\t\tholder.deleteIV.setVisibility(View.GONE);\n\t\t\t\tholder.deleteIV.setOnClickListener(new OnClickListener() {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onClick(View v) {\n\t\t\t\t\t\t// 从数据库中删除城市\n\t\t\t\t\t\tdeleteCityFromTable(position);\n\t\t\t\t\t}\n\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\tcase ADD_CITY_TYPE:\n\t\t\t\tholder.addView.setOnClickListener(new OnClickListener() {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onClick(View v) {\n\t\t\t\t\t\tif (isRefreshMode) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tL.i(\"liweiping\", \"addView.onClickListener...\");\n\t\t\t\t\t\tManagerCityActivity.this.startActivityForResult(\n\t\t\t\t\t\t\t\t(new Intent(ManagerCityActivity.this,\n\t\t\t\t\t\t\t\t\t\tQueryCityActivity.class)), 0);\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tprivate ViewHolder buildHolder(View convertView) {\n\t\t\tViewHolder holder = new ViewHolder();\n\t\t\tholder.cityTV = (TextView) convertView\n\t\t\t\t\t.findViewById(R.id.city_manager_name_tv);\n\t\t\tholder.tempTV = (TextView) convertView\n\t\t\t\t\t.findViewById(R.id.city_manager_temp_tv);\n\t\t\tholder.weatherIV = (ImageView) convertView\n\t\t\t\t\t.findViewById(R.id.city_manager_icon_iv);\n\t\t\tholder.deleteIV = (ImageView) convertView\n\t\t\t\t\t.findViewById(R.id.city_delete_btn);\n\t\t\tholder.loadingBar = (ProgressBar) convertView\n\t\t\t\t\t.findViewById(R.id.city_manager_progressbar);\n\t\t\tholder.addView = convertView;\n\t\t\treturn holder;\n\t\t}\n\n\t}\n\n\tprivate static class ViewHolder {\n\t\tTextView cityTV;\n\t\tTextView tempTV;\n\t\tImageView weatherIV;\n\t\tProgressBar loadingBar;\n\t\tImageView deleteIV;\n\t\tView addView;\n\t}\n\n\n\tprivate void getWeather(City city) {\n\t\tif (city == null)\n\t\t\treturn;\n\t\tint index = mTmpCitys.indexOf(city);\n\t\tmAdapter.setRefreshingIndex(index);// 开始刷新\n\n\t\tfinal String postID = city.getPostID();\n\t\tStringRequest sr = new StringRequest(\n\t\t\t\tString.format(WeatherSpider.WEATHER_ALL, postID),\n\t\t\t\tnew Response.Listener<String>() {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onResponse(String response) {\n\t\t\t\t\t\t// sr.getTag();\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tWeatherInfo weatherInfo = WeatherSpider\n\t\t\t\t\t\t\t\t\t.getWeatherInfo(mActivity, postID, response);\n\t\t\t\t\t\t\tif (!WeatherSpider.isEmpty(weatherInfo)) {\n\t\t\t\t\t\t\t\tsave2Database(postID, response, weatherInfo);\n\t\t\t\t\t\t\t\tmTmpCitys = getTmpCities();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (JSONException e) {\n\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmAdapter.setRefreshingIndex(-1);// 重置\n\t\t\t\t\t\tupdateRefreshMode(false);// 结束刷新\n\t\t\t\t\t}\n\t\t\t\t}, new Response.ErrorListener() {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onErrorResponse(VolleyError error) {\n\t\t\t\t\t\tmAdapter.setRefreshingIndex(-1);// 重置\n\t\t\t\t\t\tupdateRefreshMode(false);// 结束刷新\n\t\t\t\t\t}\n\t\t\t\t});\n\t\tsr.setTag(postID);\n\t\tApp.getVolleyRequestQueue().add(sr);\n\n\t}\n\n\tprotected void save2Database(String postID, String response,\n\t\t\tWeatherInfo weatherInfo) {\n\t\tlong pubTime = weatherInfo.getRealTime().getPub_time();\n\t\tlong savePubTime = getPubTime(postID);\n\t\tif (pubTime != savePubTime) {\n\t\t\tContentValues contentValues = new ContentValues();\n\t\t\tcontentValues.put(CityConstants.REFRESH_TIME,\n\t\t\t\t\tSystem.currentTimeMillis());\n\t\t\tcontentValues.put(CityConstants.PUB_TIME, pubTime);\n\t\t\tcontentValues.put(CityConstants.WEATHER_INFO, response);\n\t\t\tmContentResolver.update(CityProvider.TMPCITY_CONTENT_URI,\n\t\t\t\t\tcontentValues, CityConstants.POST_ID + \"=?\",\n\t\t\t\t\tnew String[] { postID });\n\t\t}\n\t}\n\n\tprivate long getPubTime(String postID) {\n\t\tCursor c = mContentResolver.query(CityProvider.TMPCITY_CONTENT_URI,\n\t\t\t\tnew String[] { CityConstants.REFRESH_TIME },\n\t\t\t\tCityConstants.POST_ID + \"=?\", new String[] { postID }, null);\n\n\t\tlong time = 0L;\n\t\tif (c.moveToFirst())\n\t\t\ttime = c.getLong(c.getColumnIndex(CityConstants.REFRESH_TIME));\n\t\treturn time;\n\t}\n\n\tprivate int mIndex;\n\n\tprivate void getAllWeather() {\n\t\tif (mTmpCitys == null || mTmpCitys.size() < 1)\n\t\t\treturn;\n\t\tfor (City city : mTmpCitys) {\n\t\t\tLog.i(\"way\", \"getAllWeather city = \" + city);\n\t\t\tif (city == null)\n\t\t\t\tcontinue;\n\t\t\tfinal String postID = city.getPostID();\n\t\t\tStringRequest sr = new StringRequest(String.format(WeatherSpider.WEATHER_ALL,\n\t\t\t\t\tpostID), new Response.Listener<String>() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void onResponse(String response) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tWeatherInfo weatherInfo = WeatherSpider.getWeatherInfo(\n\t\t\t\t\t\t\t\tmActivity, postID, response);\n\t\t\t\t\t\tif (!WeatherSpider.isEmpty(weatherInfo)) {\n\t\t\t\t\t\t\tsave2Database(postID, response, weatherInfo);\n\t\t\t\t\t\t\tmTmpCitys = getTmpCities();\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (JSONException e) {\n\n\t\t\t\t\t} catch (Exception e) {\n\n\t\t\t\t\t}\n\t\t\t\t\tmIndex++;\n\t\t\t\t\tif (mIndex >= mTmpCitys.size() - 1) {\n\t\t\t\t\t\tmIndex = -1;\n\t\t\t\t\t\tupdateRefreshMode(false);// 结束刷新\n\t\t\t\t\t}\n\t\t\t\t\tmAdapter.setRefreshingIndex(mIndex);// 重置\n\t\t\t\t}\n\t\t\t}, new Response.ErrorListener() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void onErrorResponse(VolleyError error) {\n\t\t\t\t\tmIndex++;\n\t\t\t\t\tif (mIndex >= mTmpCitys.size() - 1) {\n\t\t\t\t\t\tmIndex = -1;\n\t\t\t\t\t\tupdateRefreshMode(false);// 结束刷新\n\t\t\t\t\t}\n\t\t\t\t\tmAdapter.setRefreshingIndex(mIndex);// 重置\n\t\t\t\t}\n\t\t\t});\n\t\t\tsr.setTag(\"All\");\n\t\t\tApp.getVolleyRequestQueue().add(sr);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/yahoo/QueryCityActivity.java",
    "content": "package com.way.yahoo;\n\nimport java.util.List;\n\nimport android.content.ContentValues;\nimport android.content.Intent;\nimport android.database.Cursor;\nimport android.os.Bundle;\nimport android.text.Editable;\nimport android.text.SpannableStringBuilder;\nimport android.text.TextUtils;\nimport android.text.TextWatcher;\nimport android.text.style.TextAppearanceSpan;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.view.ViewGroup;\nimport android.widget.AdapterView;\nimport android.widget.AdapterView.OnItemClickListener;\nimport android.widget.BaseAdapter;\nimport android.widget.Button;\nimport android.widget.EditText;\nimport android.widget.Filter;\nimport android.widget.GridView;\nimport android.widget.ImageButton;\nimport android.widget.ImageView;\nimport android.widget.ListView;\nimport android.widget.RelativeLayout;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.way.adapter.QueryCityAdapter;\nimport com.way.beans.City;\nimport com.way.common.util.L;\nimport com.way.common.util.LocationUtils.LocationListener;\nimport com.way.common.util.SystemUtils;\nimport com.way.common.util.T;\nimport com.way.db.CityProvider;\nimport com.way.db.CityProvider.CityConstants;\nimport com.way.ui.view.CountDownView;\nimport com.way.ui.view.CountDownView.OnCountDownFinishedListener;\n\npublic class QueryCityActivity extends BaseActivity implements OnClickListener,\n\t\tTextWatcher, OnItemClickListener {\n\tpublic static final String CITY_EXTRA_KEY = \"city\";\n\tprivate LayoutInflater mInflater;\n\tprivate RelativeLayout mRootView;\n\tprivate CountDownView mCountDownView;\n\tprivate ImageView mBackBtn;\n\tprivate TextView mLocationTV;\n\tprivate EditText mQueryCityET;\n\tprivate ImageButton mQueryCityExitBtn;\n\tprivate ListView mQueryCityListView;\n\tprivate GridView mHotCityGridView;\n\tprivate List<City> mTmpCitys;\n\tprivate List<City> mHotCitys;\n\tprivate List<City> mCities;\n\tprivate QueryCityAdapter mSearchCityAdapter;\n\tprivate Filter mFilter;\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.city_query_layout);\n\t\tL.i(\"liweiping\", \"QueryCityActivity onCreate...\");\n\t\tinitDatas();\n\t\tinitViews();\n\t}\n\n\t@Override\n\tprotected void onResume() {\n\t\tsuper.onResume();\n\t\tL.i(\"liweiping\", \"QueryCityActivity onResume...\");\n\t}\n\n\tprivate void initViews() {\n\t\tmInflater = LayoutInflater.from(this);\n\t\tmRootView = (RelativeLayout) findViewById(R.id.city_add_bg);\n\t\tmBackBtn = (ImageView) findViewById(R.id.back_image);\n\t\tmLocationTV = (TextView) findViewById(R.id.location_text);\n\t\tmQueryCityET = (EditText) findViewById(R.id.queryCityText);\n\t\tmQueryCityExitBtn = (ImageButton) findViewById(R.id.queryCityExit);\n\n\t\tmQueryCityListView = (ListView) findViewById(R.id.cityList);\n\t\tmQueryCityListView.setOnItemClickListener(this);\n\t\tmSearchCityAdapter = new QueryCityAdapter(QueryCityActivity.this,\n\t\t\t\tmCities);\n\t\tmQueryCityListView.setAdapter(mSearchCityAdapter);\n\t\tmQueryCityListView.setTextFilterEnabled(true);\n\t\tmFilter = mSearchCityAdapter.getFilter();\n\n\t\t// mEmptyCityView = (TextView) findViewById(R.id.noCityText);\n\n\t\tmHotCityGridView = (GridView) findViewById(R.id.hotCityGrid);\n\t\tmHotCityGridView.setOnItemClickListener(this);\n\t\tmHotCityGridView.setAdapter(new HotCityAdapter());\n\n\t\tmBackBtn.setOnClickListener(this);\n\t\tmLocationTV.setOnClickListener(this);\n\t\tmQueryCityExitBtn.setOnClickListener(this);\n\t\tmQueryCityET.addTextChangedListener(this);\n\n\t\tString cityName = getCityName();\n\t\tif (TextUtils.isEmpty(cityName)) {\n\t\t\tstartLocation(mCityNameStatus);\n\t\t} else {\n\t\t\tmLocationTV.setText(formatBigMessage(cityName));\n\t\t}\n\t}\n\n\t// This is the message string used in bigText and bigPicture notifications.\n\tpublic CharSequence formatBigMessage(String city) {\n\t\tfinal TextAppearanceSpan notificationSubjectSpan = new TextAppearanceSpan(\n\t\t\t\tthis, R.style.NotificationPrimaryText);\n\n\t\t// Change multiple newlines (with potential white space between), into a\n\t\t// single new line\n\t\tfinal String message = !TextUtils.isEmpty(city) ? city : \"\";\n\t\tString afterStr = \"(点击重新定位)\";\n\t\tSpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(\n\t\t\t\tmessage);\n\t\tif (!TextUtils.isEmpty(afterStr)) {\n\t\t\tspannableStringBuilder.append(afterStr);\n\t\t\tspannableStringBuilder.setSpan(notificationSubjectSpan,\n\t\t\t\t\tmessage.length(), message.length() + afterStr.length(), 0);\n\t\t}\n\t\treturn spannableStringBuilder;\n\t}\n\n\tprivate String getCityName() {\n\t\tCursor c = mContentResolver.query(CityProvider.TMPCITY_CONTENT_URI,\n\t\t\t\tnew String[] { CityConstants.NAME }, CityConstants.ISLOCATION\n\t\t\t\t\t\t+ \"=?\", new String[] { \"1\" }, null);\n\n\t\tString name = \"\";\n\t\tif (c.moveToFirst())\n\t\t\tname = c.getString(c.getColumnIndex(CityConstants.NAME));\n\t\treturn name;\n\t}\n\n\tprivate void initDatas() {\n\t\tCursor cityCursor = mContentResolver.query(\n\t\t\t\tCityProvider.CITY_CONTENT_URI, null, null, null, null);\n\t\tmCities = SystemUtils.getAllCities(cityCursor);\n\n\t\tCursor hotCityCursor = mContentResolver.query(\n\t\t\t\tCityProvider.HOTCITY_CONTENT_URI, null, null, null, null);\n\t\tmHotCitys = SystemUtils.getHotCities(hotCityCursor);\n\t\tCursor tmpCityCursor = mContentResolver.query(\n\t\t\t\tCityProvider.TMPCITY_CONTENT_URI, null, null, null, null);\n\t\tmTmpCitys = SystemUtils.getTmpCities(tmpCityCursor);\n\t}\n\n\t@Override\n\tpublic void onItemClick(AdapterView<?> parent, View view, int position,\n\t\t\tlong id) {\n\t\tswitch (parent.getId()) {\n\t\tcase R.id.cityList:\n\t\t\tCity city = mSearchCityAdapter.getItem(position);\n\t\t\tL.i(\"liweiping\", city.getName());\n\t\t\taddToTmpCityTable(city);\n\t\t\tbreak;\n\t\tcase R.id.hotCityGrid:\n\t\t\tCity hotCity = mHotCitys.get(position);\n\t\t\tL.i(\"liweiping\", hotCity.getName());\n\t\t\taddToTmpCityTable(hotCity);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate void addToTmpCityTable(City city) {\n\t\t// 已经存在此城市，提示一下，直接返回\n\t\tif (mTmpCitys.contains(city)) {\n\t\t\tToast.makeText(this, R.string.city_exists, Toast.LENGTH_SHORT)\n\t\t\t\t\t.show();\n\t\t\treturn;\n\t\t}\n\t\t// 存储\n\t\tContentValues tmpContentValues = new ContentValues();\n\t\ttmpContentValues.put(CityConstants.NAME, city.getName());\n\t\ttmpContentValues.put(CityConstants.POST_ID, city.getPostID());\n\t\ttmpContentValues.put(CityConstants.REFRESH_TIME, 0L);// 无刷新时间\n\t\ttmpContentValues.put(CityConstants.ISLOCATION, 0);// 手动选择的城市存储为0\n\t\ttmpContentValues.put(CityConstants.ORDER_INDEX, mTmpCitys.size());\n\t\tmContentResolver.insert(CityProvider.TMPCITY_CONTENT_URI,\n\t\t\t\ttmpContentValues);\n\n\t\tIntent i = new Intent();\n\t\ti.putExtra(CITY_EXTRA_KEY, city);\n\t\tsetResult(RESULT_OK, i);\n\t\tfinish();\n\t}\n\n\t@Override\n\tpublic void onClick(View v) {\n\t\tswitch (v.getId()) {\n\t\tcase R.id.back_image:\n\t\t\tfinish();\n\t\t\tbreak;\n\t\tcase R.id.location_text:\n\t\t\tstartLocation(mCityNameStatus);\n\t\t\tbreak;\n\t\tcase R.id.queryCityExit:\n\t\t\tmQueryCityET.setText(\"\");\n\t\t\tbreak;\n\t\tcase R.id.cancel_locate_city_btn:\n\t\t\tT.showShort(this, R.string.cancle_auto_get_location);\n\t\t\tstopLocation();\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t@Override\n\tprotected void onStop() {\n\t\tsuper.onStop();\n\t\tstopLocation();\n\t}\n\n\t@Override\n\tprotected void stopLocation() {\n\t\tsuper.stopLocation();\n\t\tdismissCountDownView();\n\t}\n\n\t@Override\n\tpublic void onBackPressed() {\n\t\tif (mCountDownView != null && mCountDownView.isCountingDown()) {\n\t\t\tT.showShort(this, R.string.cancle_auto_get_location);\n\t\t\tmCountDownView.cancelCountDown();\n\t\t} else {\n\t\t\tsuper.onBackPressed();\n\t\t}\n\t}\n\n\tpublic boolean enoughToFilter() {\n\t\treturn mQueryCityET.getText().length() > 0;\n\t}\n\n\tprivate void doBeforeTextChanged() {\n\t\tif (mQueryCityListView.getVisibility() == View.GONE) {\n\t\t\tmQueryCityListView.setVisibility(View.VISIBLE);\n\t\t}\n\t}\n\n\tprivate void doAfterTextChanged() {\n\t\tif (enoughToFilter()) {\n\t\t\tL.i(\"liweiping\", \"onTextChanged  s = \"\n\t\t\t\t\t+ mQueryCityET.getText().toString());\n\t\t\tif (mFilter != null) {\n\t\t\t\tmFilter.filter(mQueryCityET.getText().toString().trim());\n\t\t\t}\n\t\t} else {\n\t\t\tif (mQueryCityListView.getVisibility() == View.VISIBLE) {\n\t\t\t\tmQueryCityListView.setVisibility(View.GONE);\n\t\t\t}\n\t\t\tif (mFilter != null) {\n\t\t\t\tmFilter.filter(null);\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void beforeTextChanged(CharSequence s, int start, int count,\n\t\t\tint after) {\n\t\tdoBeforeTextChanged();\n\t}\n\n\t@Override\n\tpublic void onTextChanged(CharSequence s, int start, int before, int count) {\n\t\tif (TextUtils.isEmpty(s)) {\n\t\t\tmQueryCityExitBtn.setVisibility(View.GONE);\n\t\t} else {\n\t\t\tmQueryCityExitBtn.setVisibility(View.VISIBLE);\n\t\t}\n\t\tdoAfterTextChanged();\n\t}\n\n\t@Override\n\tpublic void afterTextChanged(Editable s) {\n\t\t// do nothing\n\t}\n\n\tOnCountDownFinishedListener countDownFinishedListener = new OnCountDownFinishedListener() {\n\n\t\t@Override\n\t\tpublic void onCountDownFinished() {\n\t\t\tToast.makeText(QueryCityActivity.this, R.string.getlocation_fail,\n\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\tstopLocation();\n\t\t}\n\t};\n\n\tLocationListener mCityNameStatus = new LocationListener() {\n\n\t\t@Override\n\t\tpublic void detecting() {\n\t\t\tL.i(\"liweiping\", \"detecting...\");\n\t\t\tshowCountDownView();\n\t\t}\n\n\t\t@Override\n\t\tpublic void succeed(String name) {\n\t\t\tL.i(\"liweiping\", name);\n\t\t\tdismissCountDownView();\n\n\t\t\tCity city = getLocationCityFromDB(name);\n\t\t\tif (TextUtils.isEmpty(city.getPostID())) {\n\t\t\t\tToast.makeText(QueryCityActivity.this, R.string.no_this_city,\n\t\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t\t} else {\n\t\t\t\tL.i(\"liweiping\", \"location\" + city.toString());\n\t\t\t\taddOrUpdateLocationCity(city);\n\t\t\t\tT.showShort(\n\t\t\t\t\t\tQueryCityActivity.this,\n\t\t\t\t\t\tString.format(\n\t\t\t\t\t\t\t\tgetResources().getString(\n\t\t\t\t\t\t\t\t\t\tR.string.get_location_scuess), name));\n\t\t\t\tmLocationTV.setText(formatBigMessage(name));\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void failed() {\n\t\t\tToast.makeText(QueryCityActivity.this, R.string.getlocation_fail,\n\t\t\t\t\tToast.LENGTH_SHORT).show();\n\t\t}\n\n\t};\n\n\tprivate void showCountDownView() {\n\t\tmInflater.inflate(R.layout.count_down_to_location, mRootView, true);\n\t\tmCountDownView = (CountDownView) mRootView\n\t\t\t\t.findViewById(R.id.count_down_to_locate);\n\t\tButton btn = (Button) mRootView\n\t\t\t\t.findViewById(R.id.cancel_locate_city_btn);\n\t\tbtn.setOnClickListener(this);\n\t\tmCountDownView.setCountDownFinishedListener(countDownFinishedListener);\n\t\tmCountDownView.startCountDown(30);\n\t}\n\n\tprivate void dismissCountDownView() {\n\t\tif (mCountDownView != null && mCountDownView.isCountingDown())\n\t\t\tmCountDownView.cancelCountDown();\n\t}\n\n\tprivate class HotCityAdapter extends BaseAdapter {\n\n\t\t@Override\n\t\tpublic int getCount() {\n\t\t\treturn mHotCitys.size();\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getItem(int position) {\n\t\t\treturn mHotCitys.get(position);\n\t\t}\n\n\t\t@Override\n\t\tpublic long getItemId(int position) {\n\t\t\treturn position;\n\t\t}\n\n\t\t@Override\n\t\tpublic View getView(int position, View convertView, ViewGroup parent) {\n\t\t\tCity hotCity = (City) getItem(position);\n\t\t\tViewHoler viewHoler;\n\t\t\tif (convertView == null) {\n\t\t\t\tconvertView = mInflater.inflate(\n\t\t\t\t\t\tR.layout.city_query_hotcity_grid_item, parent, false);\n\t\t\t\tviewHoler = new ViewHoler();\n\t\t\t\tviewHoler.hotCityTV = (TextView) convertView\n\t\t\t\t\t\t.findViewById(R.id.grid_city_name);\n\t\t\t\tviewHoler.selectedIV = (ImageView) convertView\n\t\t\t\t\t\t.findViewById(R.id.grid_city_selected_iv);\n\t\t\t\tconvertView.setTag(viewHoler);\n\t\t\t} else {\n\t\t\t\tviewHoler = (ViewHoler) convertView.getTag();\n\t\t\t}\n\t\t\tviewHoler.hotCityTV.setText(hotCity.getName());\n\t\t\tif (mTmpCitys.contains(hotCity)) {\n\t\t\t\tviewHoler.selectedIV.setVisibility(View.VISIBLE);\n\t\t\t} else {\n\t\t\t\tviewHoler.selectedIV.setVisibility(View.GONE);\n\t\t\t}\n\n\t\t\treturn convertView;\n\t\t}\n\n\t}\n\n\tstatic class ViewHoler {\n\t\tTextView hotCityTV;\n\t\tImageView selectedIV;\n\t}\n\n}\n"
  },
  {
    "path": "WayHoo/src/com/way/yahoo/SplashActivity.java",
    "content": "package com.way.yahoo;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.view.View;\nimport android.view.Window;\nimport android.view.WindowManager;\n\npublic class SplashActivity extends Activity {\n\tprivate static final int ACTIVITY_TIMEOUT_GOTO_NEXT = 0;\n\tprivate static final int ACTIVITY_TIME = 2000;\n\tprivate Window mWindow;\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\n\t\trequestWindowFeature(Window.FEATURE_NO_TITLE);\n\n\t\tmWindow = getWindow();\n\t\tWindowManager.LayoutParams params = mWindow.getAttributes();\n\t\tparams.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN\n\t\t\t\t| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;\n\t\tmWindow.setAttributes(params);\n\n\t\tsetContentView(R.layout.splash_activity_layout);\n\t\tmHandler.sendEmptyMessageDelayed(ACTIVITY_TIMEOUT_GOTO_NEXT,\n\t\t\t\tACTIVITY_TIME);\n\t}\n\t@Override\n\tpublic void onBackPressed() {\n\t\t//super.onBackPressed();\n\t}\n\t@Override\n\tprotected void onDestroy() {\n\t\tsuper.onDestroy();\n\t\tmHandler.removeMessages(ACTIVITY_TIMEOUT_GOTO_NEXT);\n\t}\n\n\tprivate Handler mHandler = new Handler() {\n\n\t\tpublic void handleMessage(android.os.Message msg) {\n\t\t\tswitch (msg.what) {\n\t\t\tcase ACTIVITY_TIMEOUT_GOTO_NEXT:\n\t\t\t\tIntent intent = new Intent();\n\t\t\t\tintent.setClass(SplashActivity.this, MainActivity.class);\n\t\t\t\tstartActivity(intent);\n\t\t\t\tSplashActivity.this.finish();\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t};\n}\n"
  },
  {
    "path": "WayHoo/src/net/simonvt/menudrawer/BuildLayerFrameLayout.java",
    "content": "package net.simonvt.menudrawer;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.util.AttributeSet;\nimport android.widget.FrameLayout;\n\n/**\n * FrameLayout which caches the hardware layer if available.\n * <p/>\n * If it's not posted twice the layer either wont be built on start, or it'll be built twice.\n */\nclass BuildLayerFrameLayout extends FrameLayout {\n\n    private boolean mChanged;\n\n    private boolean mHardwareLayersEnabled = true;\n\n    private boolean mAttached;\n\n    private boolean mFirst = true;\n\n    public BuildLayerFrameLayout(Context context) {\n        super(context);\n        if (MenuDrawer.USE_TRANSLATIONS) {\n            setLayerType(LAYER_TYPE_HARDWARE, null);\n        }\n    }\n\n    public BuildLayerFrameLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        if (MenuDrawer.USE_TRANSLATIONS) {\n            setLayerType(LAYER_TYPE_HARDWARE, null);\n        }\n    }\n\n    public BuildLayerFrameLayout(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        if (MenuDrawer.USE_TRANSLATIONS) {\n            setLayerType(LAYER_TYPE_HARDWARE, null);\n        }\n    }\n\n    void setHardwareLayersEnabled(boolean enabled) {\n        mHardwareLayersEnabled = enabled;\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        mAttached = true;\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        super.onDetachedFromWindow();\n        mAttached = false;\n    }\n\n    @Override\n    protected void onSizeChanged(int w, int h, int oldw, int oldh) {\n        super.onSizeChanged(w, h, oldw, oldh);\n\n        if (MenuDrawer.USE_TRANSLATIONS && mHardwareLayersEnabled) {\n            post(new Runnable() {\n                @Override\n                public void run() {\n                    mChanged = true;\n                    invalidate();\n                }\n            });\n        }\n    }\n\n    @Override\n    protected void dispatchDraw(Canvas canvas) {\n        super.dispatchDraw(canvas);\n\n        if (mChanged && MenuDrawer.USE_TRANSLATIONS) {\n            post(new Runnable() {\n                @Override\n                public void run() {\n                    if (mAttached) {\n                        final int layerType = getLayerType();\n                        // If it's already a hardware layer, it'll be built anyway.\n                        if (layerType != LAYER_TYPE_HARDWARE || mFirst) {\n                            mFirst = false;\n                            setLayerType(LAYER_TYPE_HARDWARE, null);\n                            buildLayer();\n                            setLayerType(LAYER_TYPE_NONE, null);\n                        }\n                    }\n                }\n            });\n\n            mChanged = false;\n        }\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/net/simonvt/menudrawer/ColorDrawable.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage net.simonvt.menudrawer;\n\nimport android.content.res.Resources;\nimport android.graphics.Canvas;\nimport android.graphics.ColorFilter;\nimport android.graphics.Paint;\nimport android.graphics.PixelFormat;\nimport android.graphics.drawable.Drawable;\n\n/**\n * A specialized Drawable that fills the Canvas with a specified color.\n * Note that a ColorDrawable ignores the ColorFilter.\n * <p/>\n * <p>It can be defined in an XML file with the <code>&lt;color></code> element.</p>\n *\n * @attr ref android.R.styleable#ColorDrawable_color\n */\nclass ColorDrawable extends Drawable {\n\n    private ColorState mState;\n    private final Paint mPaint = new Paint();\n\n    /** Creates a new black ColorDrawable. */\n    public ColorDrawable() {\n        this(null);\n    }\n\n    /**\n     * Creates a new ColorDrawable with the specified color.\n     *\n     * @param color The color to draw.\n     */\n    public ColorDrawable(int color) {\n        this(null);\n        setColor(color);\n    }\n\n    private ColorDrawable(ColorState state) {\n        mState = new ColorState(state);\n    }\n\n    @Override\n    public int getChangingConfigurations() {\n        return super.getChangingConfigurations() | mState.mChangingConfigurations;\n    }\n\n    @Override\n    public void draw(Canvas canvas) {\n        if ((mState.mUseColor >>> 24) != 0) {\n            mPaint.setColor(mState.mUseColor);\n            canvas.drawRect(getBounds(), mPaint);\n        }\n    }\n\n    /**\n     * Gets the drawable's color value.\n     *\n     * @return int The color to draw.\n     */\n    public int getColor() {\n        return mState.mUseColor;\n    }\n\n    /**\n     * Sets the drawable's color value. This action will clobber the results of prior calls to\n     * {@link #setAlpha(int)} on this object, which side-affected the underlying color.\n     *\n     * @param color The color to draw.\n     */\n    public void setColor(int color) {\n        if (mState.mBaseColor != color || mState.mUseColor != color) {\n            invalidateSelf();\n            mState.mBaseColor = mState.mUseColor = color;\n        }\n    }\n\n    /**\n     * Returns the alpha value of this drawable's color.\n     *\n     * @return A value between 0 and 255.\n     */\n    public int getAlpha() {\n        return mState.mUseColor >>> 24;\n    }\n\n    /**\n     * Sets the color's alpha value.\n     *\n     * @param alpha The alpha value to set, between 0 and 255.\n     */\n    public void setAlpha(int alpha) {\n        alpha += alpha >> 7;   // make it 0..256\n        int baseAlpha = mState.mBaseColor >>> 24;\n        int useAlpha = baseAlpha * alpha >> 8;\n        int oldUseColor = mState.mUseColor;\n        mState.mUseColor = (mState.mBaseColor << 8 >>> 8) | (useAlpha << 24);\n        if (oldUseColor != mState.mUseColor) {\n            invalidateSelf();\n        }\n    }\n\n    /**\n     * Setting a color filter on a ColorDrawable has no effect.\n     *\n     * @param colorFilter Ignore.\n     */\n    public void setColorFilter(ColorFilter colorFilter) {\n    }\n\n    public int getOpacity() {\n        switch (mState.mUseColor >>> 24) {\n            case 255:\n                return PixelFormat.OPAQUE;\n            case 0:\n                return PixelFormat.TRANSPARENT;\n        }\n        return PixelFormat.TRANSLUCENT;\n    }\n\n    @Override\n    public ConstantState getConstantState() {\n        mState.mChangingConfigurations = getChangingConfigurations();\n        return mState;\n    }\n\n    static final class ColorState extends ConstantState {\n\n        int mBaseColor; // base color, independent of setAlpha()\n        int mUseColor;  // basecolor modulated by setAlpha()\n        int mChangingConfigurations;\n\n        ColorState(ColorState state) {\n            if (state != null) {\n                mBaseColor = state.mBaseColor;\n                mUseColor = state.mUseColor;\n            }\n        }\n\n        @Override\n        public Drawable newDrawable() {\n            return new ColorDrawable(this);\n        }\n\n        @Override\n        public Drawable newDrawable(Resources res) {\n            return new ColorDrawable(this);\n        }\n\n        @Override\n        public int getChangingConfigurations() {\n            return mChangingConfigurations;\n        }\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/net/simonvt/menudrawer/DraggableDrawer.java",
    "content": "package net.simonvt.menudrawer;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Parcelable;\nimport android.os.SystemClock;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.VelocityTracker;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.view.ViewGroup;\nimport android.view.animation.Interpolator;\n\npublic abstract class DraggableDrawer extends MenuDrawer {\n\n    /**\n     * Key used when saving menu visibility state.\n     */\n    private static final String STATE_MENU_VISIBLE = \"net.simonvt.menudrawer.MenuDrawer.menuVisible\";\n\n    /**\n     * Interpolator used for peeking at the drawer.\n     */\n    private static final Interpolator PEEK_INTERPOLATOR = new PeekInterpolator();\n\n    /**\n     * The maximum alpha of the dark menu overlay used for dimming the menu.\n     */\n    protected static final int MAX_MENU_OVERLAY_ALPHA = 185;\n\n    /**\n     * Default delay from {@link #peekDrawer()} is called until first animation is run.\n     */\n    private static final long DEFAULT_PEEK_START_DELAY = 5000;\n\n    /**\n     * Default delay between each subsequent animation, after {@link #peekDrawer()} has been called.\n     */\n    private static final long DEFAULT_PEEK_DELAY = 10000;\n\n    /**\n     * The duration of the peek animation.\n     */\n    protected static final int PEEK_DURATION = 5000;\n\n    /**\n     * Distance in dp from closed position from where the drawer is considered closed with regards to touch events.\n     */\n    private static final int CLOSE_ENOUGH = 3;\n\n    protected static final int INVALID_POINTER = -1;\n\n    /**\n     * Slop before starting a drag.\n     */\n    protected int mTouchSlop;\n\n    /**\n     * Runnable used when the peek animation is running.\n     */\n    protected final Runnable mPeekRunnable = new Runnable() {\n        @Override\n        public void run() {\n            peekDrawerInvalidate();\n        }\n    };\n\n    /**\n     * Runnable used when animating the drawer open/closed.\n     */\n    private final Runnable mDragRunnable = new Runnable() {\n        @Override\n        public void run() {\n            postAnimationInvalidate();\n        }\n    };\n\n    /**\n     * Indicates whether the drawer is currently being dragged.\n     */\n    protected boolean mIsDragging;\n\n    /**\n     * The current pointer id.\n     */\n    protected int mActivePointerId = INVALID_POINTER;\n\n    /**\n     * The initial X position of a drag.\n     */\n    protected float mInitialMotionX;\n\n    /**\n     * The initial Y position of a drag.\n     */\n    protected float mInitialMotionY;\n\n    /**\n     * The last X position of a drag.\n     */\n    protected float mLastMotionX = -1;\n\n    /**\n     * The last Y position of a drag.\n     */\n    protected float mLastMotionY = -1;\n\n    /**\n     * Default delay between each subsequent animation, after {@link #peekDrawer()} has been called.\n     */\n    protected long mPeekDelay;\n\n    /**\n     * Scroller used for the peek drawer animation.\n     */\n    protected Scroller mPeekScroller;\n\n    /**\n     * Velocity tracker used when animating the drawer open/closed after a drag.\n     */\n    protected VelocityTracker mVelocityTracker;\n\n    /**\n     * Maximum velocity allowed when animating the drawer open/closed.\n     */\n    protected int mMaxVelocity;\n\n    /**\n     * Indicates whether the menu should be offset when dragging the drawer.\n     */\n    protected boolean mOffsetMenu = true;\n\n    /**\n     * Distance in px from closed position from where the drawer is considered closed with regards to touch events.\n     */\n    protected int mCloseEnough;\n\n    /**\n     * Runnable used for first call to {@link #startPeek()} after {@link #peekDrawer()}  has been called.\n     */\n    private Runnable mPeekStartRunnable;\n\n    protected boolean mIsPeeking;\n\n    /**\n     * Scroller used when animating the drawer open/closed.\n     */\n    private Scroller mScroller;\n\n    /**\n     * Indicates whether the current layer type is {@link android.view.View#LAYER_TYPE_HARDWARE}.\n     */\n    protected boolean mLayerTypeHardware;\n\n    DraggableDrawer(Activity activity, int dragMode) {\n        super(activity, dragMode);\n    }\n\n    public DraggableDrawer(Context context) {\n        super(context);\n    }\n\n    public DraggableDrawer(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public DraggableDrawer(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n    }\n\n    @Override\n    protected void initDrawer(Context context, AttributeSet attrs, int defStyle) {\n        super.initDrawer(context, attrs, defStyle);\n\n        final ViewConfiguration configuration = ViewConfiguration.get(context);\n        mTouchSlop = configuration.getScaledTouchSlop();\n        mMaxVelocity = configuration.getScaledMaximumFlingVelocity();\n\n        mScroller = new Scroller(context, MenuDrawer.SMOOTH_INTERPOLATOR);\n        mPeekScroller = new Scroller(context, DraggableDrawer.PEEK_INTERPOLATOR);\n\n        mCloseEnough = dpToPx(DraggableDrawer.CLOSE_ENOUGH);\n    }\n\n    public void toggleMenu(boolean animate) {\n        if (mDrawerState == STATE_OPEN || mDrawerState == STATE_OPENING) {\n            closeMenu(animate);\n        } else if (mDrawerState == STATE_CLOSED || mDrawerState == STATE_CLOSING) {\n            openMenu(animate);\n        }\n    }\n\n    public boolean isMenuVisible() {\n        return mMenuVisible;\n    }\n\n    public void setMenuSize(final int size) {\n        mMenuSize = size;\n        if (mDrawerState == STATE_OPEN || mDrawerState == STATE_OPENING) {\n            setOffsetPixels(mMenuSize);\n        }\n        requestLayout();\n        invalidate();\n    }\n\n    public void setOffsetMenuEnabled(boolean offsetMenu) {\n        if (offsetMenu != mOffsetMenu) {\n            mOffsetMenu = offsetMenu;\n            requestLayout();\n            invalidate();\n        }\n    }\n\n    public boolean getOffsetMenuEnabled() {\n        return mOffsetMenu;\n    }\n\n    public void peekDrawer() {\n        peekDrawer(DEFAULT_PEEK_START_DELAY, DEFAULT_PEEK_DELAY);\n    }\n\n    public void peekDrawer(long delay) {\n        peekDrawer(DEFAULT_PEEK_START_DELAY, delay);\n    }\n\n    public void peekDrawer(final long startDelay, final long delay) {\n        if (startDelay < 0) {\n            throw new IllegalArgumentException(\"startDelay must be zero or larger.\");\n        }\n        if (delay < 0) {\n            throw new IllegalArgumentException(\"delay must be zero or larger\");\n        }\n\n        removeCallbacks(mPeekRunnable);\n        removeCallbacks(mPeekStartRunnable);\n\n        mPeekDelay = delay;\n        mPeekStartRunnable = new Runnable() {\n            @Override\n            public void run() {\n                startPeek();\n            }\n        };\n        postDelayed(mPeekStartRunnable, startDelay);\n    }\n\n    public void setHardwareLayerEnabled(boolean enabled) {\n        if (enabled != mHardwareLayersEnabled) {\n            mHardwareLayersEnabled = enabled;\n            mMenuContainer.setHardwareLayersEnabled(enabled);\n            mContentContainer.setHardwareLayersEnabled(enabled);\n            stopLayerTranslation();\n        }\n    }\n\n    public int getTouchMode() {\n        return mTouchMode;\n    }\n\n    public void setTouchMode(int mode) {\n        if (mTouchMode != mode) {\n            mTouchMode = mode;\n            updateTouchAreaSize();\n        }\n    }\n\n    public void setTouchBezelSize(int size) {\n        mTouchBezelSize = size;\n    }\n\n    public int getTouchBezelSize() {\n        return mTouchBezelSize;\n    }\n\n    /**\n     * If possible, set the layer type to {@link android.view.View#LAYER_TYPE_HARDWARE}.\n     */\n    protected void startLayerTranslation() {\n        if (USE_TRANSLATIONS && mHardwareLayersEnabled && !mLayerTypeHardware) {\n            mLayerTypeHardware = true;\n            mContentContainer.setLayerType(View.LAYER_TYPE_HARDWARE, null);\n            mMenuContainer.setLayerType(View.LAYER_TYPE_HARDWARE, null);\n        }\n    }\n\n    /**\n     * If the current layer type is {@link android.view.View#LAYER_TYPE_HARDWARE}, this will set it to\n     * {@link View#LAYER_TYPE_NONE}.\n     */\n    protected void stopLayerTranslation() {\n        if (mLayerTypeHardware) {\n            mLayerTypeHardware = false;\n            mContentContainer.setLayerType(View.LAYER_TYPE_NONE, null);\n            mMenuContainer.setLayerType(View.LAYER_TYPE_NONE, null);\n        }\n    }\n\n    /**\n     * Called when a drag has been ended.\n     */\n    protected void endDrag() {\n        mIsDragging = false;\n\n        if (mVelocityTracker != null) {\n            mVelocityTracker.recycle();\n            mVelocityTracker = null;\n        }\n    }\n\n    /**\n     * Stops ongoing animation of the drawer.\n     */\n    protected void stopAnimation() {\n        removeCallbacks(mDragRunnable);\n        mScroller.abortAnimation();\n        stopLayerTranslation();\n    }\n\n    /**\n     * Called when a drawer animation has successfully completed.\n     */\n    private void completeAnimation() {\n        mScroller.abortAnimation();\n        final int finalX = mScroller.getFinalX();\n        setOffsetPixels(finalX);\n        setDrawerState(finalX == 0 ? STATE_CLOSED : STATE_OPEN);\n        stopLayerTranslation();\n    }\n\n    protected void cancelContentTouch() {\n        final long now = SystemClock.uptimeMillis();\n        final MotionEvent cancelEvent = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);\n        final int childCount = getChildCount();\n        for (int i = 0; i < childCount; i++) {\n            getChildAt(i).dispatchTouchEvent(cancelEvent);\n        }\n        mContentContainer.dispatchTouchEvent(cancelEvent);\n        cancelEvent.recycle();\n    }\n\n    /**\n     * Moves the drawer to the position passed.\n     *\n     * @param position The position the content is moved to.\n     * @param velocity Optional velocity if called by releasing a drag event.\n     * @param animate  Whether the move is animated.\n     */\n    protected void animateOffsetTo(int position, int velocity, boolean animate) {\n        endDrag();\n        endPeek();\n\n        final int startX = (int) mOffsetPixels;\n        final int dx = position - startX;\n        if (dx == 0 || !animate) {\n            setOffsetPixels(position);\n            setDrawerState(position == 0 ? STATE_CLOSED : STATE_OPEN);\n            stopLayerTranslation();\n            return;\n        }\n\n        int duration;\n\n        velocity = Math.abs(velocity);\n        if (velocity > 0) {\n            duration = 4 * Math.round(1000.f * Math.abs((float) dx / velocity));\n        } else {\n            duration = (int) (600.f * Math.abs((float) dx / mMenuSize));\n        }\n\n        duration = Math.min(duration, mMaxAnimationDuration);\n        animateOffsetTo(position, duration);\n    }\n\n    protected void animateOffsetTo(int position, int duration) {\n        final int startX = (int) mOffsetPixels;\n        final int dx = position - startX;\n\n        if (dx > 0) {\n            setDrawerState(STATE_OPENING);\n            mScroller.startScroll(startX, 0, dx, 0, duration);\n        } else {\n            setDrawerState(STATE_CLOSING);\n            mScroller.startScroll(startX, 0, dx, 0, duration);\n        }\n\n        startLayerTranslation();\n\n        postAnimationInvalidate();\n    }\n\n    /**\n     * Callback when each frame in the drawer animation should be drawn.\n     */\n    private void postAnimationInvalidate() {\n        if (mScroller.computeScrollOffset()) {\n            final int oldX = (int) mOffsetPixels;\n            final int x = mScroller.getCurrX();\n\n            if (x != oldX) setOffsetPixels(x);\n            if (x != mScroller.getFinalX()) {\n                postOnAnimation(mDragRunnable);\n                return;\n            }\n        }\n\n        completeAnimation();\n    }\n\n    /**\n     * Starts peek drawer animation.\n     */\n    protected void startPeek() {\n        mIsPeeking = true;\n        initPeekScroller();\n\n        startLayerTranslation();\n        peekDrawerInvalidate();\n    }\n\n    protected abstract void initPeekScroller();\n\n    /**\n     * Callback when each frame in the peek drawer animation should be drawn.\n     */\n    private void peekDrawerInvalidate() {\n        if (mPeekScroller.computeScrollOffset()) {\n            final int oldX = (int) mOffsetPixels;\n            final int x = mPeekScroller.getCurrX();\n            if (x != oldX) setOffsetPixels(x);\n\n            if (!mPeekScroller.isFinished()) {\n                postOnAnimation(mPeekRunnable);\n                return;\n\n            } else if (mPeekDelay > 0) {\n                mPeekStartRunnable = new Runnable() {\n                    @Override\n                    public void run() {\n                        startPeek();\n                    }\n                };\n                postDelayed(mPeekStartRunnable, mPeekDelay);\n            }\n        }\n\n        completePeek();\n    }\n\n    /**\n     * Called when the peek drawer animation has successfully completed.\n     */\n    private void completePeek() {\n        mPeekScroller.abortAnimation();\n\n        setOffsetPixels(0);\n\n        setDrawerState(STATE_CLOSED);\n        stopLayerTranslation();\n        mIsPeeking = false;\n    }\n\n    /**\n     * Stops ongoing peek drawer animation.\n     */\n    protected void endPeek() {\n        removeCallbacks(mPeekStartRunnable);\n        removeCallbacks(mPeekRunnable);\n        stopLayerTranslation();\n        mIsPeeking = false;\n    }\n\n    protected boolean isCloseEnough() {\n        return Math.abs(mOffsetPixels) <= mCloseEnough;\n    }\n\n    protected boolean canChildrenScroll(int dx, int dy, int x, int y) {\n        boolean canScroll = false;\n\n        switch (getPosition()) {\n            case LEFT:\n            case RIGHT:\n                if (!mMenuVisible) {\n                    canScroll = canChildScrollHorizontally(mContentContainer, false, dx,\n                            x - ViewHelper.getLeft(mContentContainer), y - ViewHelper.getTop(mContentContainer));\n                } else {\n                    canScroll = canChildScrollHorizontally(mMenuContainer, false, dx,\n                            x - ViewHelper.getLeft(mMenuContainer), y - ViewHelper.getTop(mContentContainer));\n                }\n                break;\n\n            case TOP:\n            case BOTTOM:\n                if (!mMenuVisible) {\n                    canScroll = canChildScrollVertically(mContentContainer, false, dy,\n                            x - ViewHelper.getLeft(mContentContainer), y - ViewHelper.getTop(mContentContainer));\n                } else {\n                    canScroll = canChildScrollVertically(mMenuContainer, false, dy,\n                            x - ViewHelper.getLeft(mMenuContainer), y - ViewHelper.getTop(mContentContainer));\n                }\n        }\n\n        return canScroll;\n    }\n\n    /**\n     * Tests scrollability within child views of v given a delta of dx.\n     *\n     * @param v      View to test for horizontal scrollability\n     * @param checkV Whether the view should be checked for draggability\n     * @param dx     Delta scrolled in pixels\n     * @param x      X coordinate of the active touch point\n     * @param y      Y coordinate of the active touch point\n     * @return true if child views of v can be scrolled by delta of dx.\n     */\n    protected boolean canChildScrollHorizontally(View v, boolean checkV, int dx, int x, int y) {\n        if (v instanceof ViewGroup) {\n            final ViewGroup group = (ViewGroup) v;\n\n            final int count = group.getChildCount();\n            // Count backwards - let topmost views consume scroll distance first.\n            for (int i = count - 1; i >= 0; i--) {\n                final View child = group.getChildAt(i);\n\n                final int childLeft = child.getLeft() + supportGetTranslationX(child);\n                final int childRight = child.getRight() + supportGetTranslationX(child);\n                final int childTop = child.getTop() + supportGetTranslationY(child);\n                final int childBottom = child.getBottom() + supportGetTranslationY(child);\n\n                if (x >= childLeft && x < childRight && y >= childTop && y < childBottom\n                        && canChildScrollHorizontally(child, true, dx, x - childLeft, y - childTop)) {\n                    return true;\n                }\n            }\n        }\n\n        return checkV && mOnInterceptMoveEventListener.isViewDraggable(v, dx, x, y);\n    }\n\n    /**\n     * Tests scrollability within child views of v given a delta of dx.\n     *\n     * @param v      View to test for horizontal scrollability\n     * @param checkV Whether the view should be checked for draggability\n     * @param dx     Delta scrolled in pixels\n     * @param x      X coordinate of the active touch point\n     * @param y      Y coordinate of the active touch point\n     * @return true if child views of v can be scrolled by delta of dx.\n     */\n    protected boolean canChildScrollVertically(View v, boolean checkV, int dx, int x, int y) {\n        if (v instanceof ViewGroup) {\n            final ViewGroup group = (ViewGroup) v;\n\n            final int count = group.getChildCount();\n            // Count backwards - let topmost views consume scroll distance first.\n            for (int i = count - 1; i >= 0; i--) {\n                final View child = group.getChildAt(i);\n\n                final int childLeft = child.getLeft() + supportGetTranslationX(child);\n                final int childRight = child.getRight() + supportGetTranslationX(child);\n                final int childTop = child.getTop() + supportGetTranslationY(child);\n                final int childBottom = child.getBottom() + supportGetTranslationY(child);\n\n                if (x >= childLeft && x < childRight && y >= childTop && y < childBottom\n                        && canChildScrollVertically(child, true, dx, x - childLeft, y - childTop)) {\n                    return true;\n                }\n            }\n        }\n\n        return checkV && mOnInterceptMoveEventListener.isViewDraggable(v, dx, x, y);\n    }\n\n    protected float getXVelocity(VelocityTracker velocityTracker) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {\n            return velocityTracker.getXVelocity(mActivePointerId);\n        }\n\n        return velocityTracker.getXVelocity();\n    }\n\n    protected float getYVelocity(VelocityTracker velocityTracker) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {\n            return velocityTracker.getYVelocity(mActivePointerId);\n        }\n\n        return velocityTracker.getYVelocity();\n    }\n\n    private int supportGetTranslationY(View v) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {\n            return (int) v.getTranslationY();\n        }\n\n        return 0;\n    }\n\n    private int supportGetTranslationX(View v) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {\n            return (int) v.getTranslationX();\n        }\n\n        return 0;\n    }\n\n    void saveState(Bundle state) {\n        final boolean menuVisible = mDrawerState == STATE_OPEN || mDrawerState == STATE_OPENING;\n        state.putBoolean(STATE_MENU_VISIBLE, menuVisible);\n    }\n\n    public void restoreState(Parcelable in) {\n        super.restoreState(in);\n        Bundle state = (Bundle) in;\n        final boolean menuOpen = state.getBoolean(STATE_MENU_VISIBLE);\n        if (menuOpen) {\n            openMenu(false);\n        } else {\n            setOffsetPixels(0);\n        }\n        mDrawerState = menuOpen ? STATE_OPEN : STATE_CLOSED;\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/net/simonvt/menudrawer/FloatScroller.java",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage net.simonvt.menudrawer;\n\nimport android.view.animation.AnimationUtils;\nimport android.view.animation.Interpolator;\n\n/**\n * This class encapsulates scrolling.  The duration of the scroll\n * can be passed in the constructor and specifies the maximum time that\n * the scrolling animation should take.  Past this time, the scrolling is\n * automatically moved to its final stage and computeScrollOffset()\n * will always return false to indicate that scrolling is over.\n */\nclass FloatScroller {\n\n    private float mStart;\n    private float mFinal;\n\n    private float mCurr;\n    private long mStartTime;\n    private int mDuration;\n    private float mDurationReciprocal;\n    private float mDeltaX;\n    private boolean mFinished;\n    private Interpolator mInterpolator;\n\n    /**\n     * Create a Scroller with the specified interpolator. If the interpolator is\n     * null, the default (viscous) interpolator will be used. Specify whether or\n     * not to support progressive \"flywheel\" behavior in flinging.\n     */\n    public FloatScroller(Interpolator interpolator) {\n        mFinished = true;\n        mInterpolator = interpolator;\n    }\n\n    /**\n     * Returns whether the scroller has finished scrolling.\n     *\n     * @return True if the scroller has finished scrolling, false otherwise.\n     */\n    public final boolean isFinished() {\n        return mFinished;\n    }\n\n    /**\n     * Force the finished field to a particular value.\n     *\n     * @param finished The new finished value.\n     */\n    public final void forceFinished(boolean finished) {\n        mFinished = finished;\n    }\n\n    /**\n     * Returns how long the scroll event will take, in milliseconds.\n     *\n     * @return The duration of the scroll in milliseconds.\n     */\n    public final int getDuration() {\n        return mDuration;\n    }\n\n    /**\n     * Returns the current offset in the scroll.\n     *\n     * @return The new offset as an absolute distance from the origin.\n     */\n    public final float getCurr() {\n        return mCurr;\n    }\n\n    /**\n     * Returns the start offset in the scroll.\n     *\n     * @return The start offset as an absolute distance from the origin.\n     */\n    public final float getStart() {\n        return mStart;\n    }\n\n    /**\n     * Returns where the scroll will end. Valid only for \"fling\" scrolls.\n     *\n     * @return The final offset as an absolute distance from the origin.\n     */\n    public final float getFinal() {\n        return mFinal;\n    }\n\n    public boolean computeScrollOffset() {\n        if (mFinished) {\n            return false;\n        }\n\n        int timePassed = (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime);\n\n        if (timePassed < mDuration) {\n            float x = timePassed * mDurationReciprocal;\n            x = mInterpolator.getInterpolation(x);\n            mCurr = mStart + x * mDeltaX;\n\n        } else {\n            mCurr = mFinal;\n            mFinished = true;\n        }\n        return true;\n    }\n\n    public void startScroll(float start, float delta, int duration) {\n        mFinished = false;\n        mDuration = duration;\n        mStartTime = AnimationUtils.currentAnimationTimeMillis();\n        mStart = start;\n        mFinal = start + delta;\n        mDeltaX = delta;\n        mDurationReciprocal = 1.0f / (float) mDuration;\n    }\n\n    /**\n     * Stops the animation. Contrary to {@link #forceFinished(boolean)},\n     * aborting the animating cause the scroller to move to the final x and y\n     * position\n     *\n     * @see #forceFinished(boolean)\n     */\n    public void abortAnimation() {\n        mCurr = mFinal;\n        mFinished = true;\n    }\n\n    /**\n     * Extend the scroll animation. This allows a running animation to scroll\n     * further and longer, when used with {@link #setFinal(float)}.\n     *\n     * @param extend Additional time to scroll in milliseconds.\n     * @see #setFinal(float)\n     */\n    public void extendDuration(int extend) {\n        int passed = timePassed();\n        mDuration = passed + extend;\n        mDurationReciprocal = 1.0f / mDuration;\n        mFinished = false;\n    }\n\n    /**\n     * Returns the time elapsed since the beginning of the scrolling.\n     *\n     * @return The elapsed time in milliseconds.\n     */\n    public int timePassed() {\n        return (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime);\n    }\n\n    public void setFinal(float newVal) {\n        mFinal = newVal;\n        mDeltaX = mFinal - mStart;\n        mFinished = false;\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/net/simonvt/menudrawer/MenuDrawer.java",
    "content": "package net.simonvt.menudrawer;\n\nimport net.simonvt.menudrawer.compat.ActionBarHelper;\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.graphics.Canvas;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\nimport android.graphics.drawable.GradientDrawable;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewParent;\nimport android.view.ViewTreeObserver;\nimport android.view.animation.AccelerateInterpolator;\nimport android.view.animation.Interpolator;\n\nimport com.way.yahoo.R;\n\n@SuppressLint(\"NewApi\")\npublic abstract class MenuDrawer extends ViewGroup {\n\n    /**\n     * Callback interface for changing state of the drawer.\n     */\n    public interface OnDrawerStateChangeListener {\n\n        /**\n         * Called when the drawer state changes.\n         *\n         * @param oldState The old drawer state.\n         * @param newState The new drawer state.\n         */\n        void onDrawerStateChange(int oldState, int newState);\n\n        /**\n         * Called when the drawer slides.\n         *\n         * @param openRatio    Ratio for how open the menu is.\n         * @param offsetPixels Current offset of the menu in pixels.\n         */\n        void onDrawerSlide(float openRatio, int offsetPixels);\n    }\n\n    /**\n     * Callback that is invoked when the drawer is in the process of deciding whether it should intercept the touch\n     * event. This lets the listener decide if the pointer is on a view that would disallow dragging of the drawer.\n     * This is only called when the touch mode is {@link #TOUCH_MODE_FULLSCREEN}.\n     */\n    public interface OnInterceptMoveEventListener {\n\n        /**\n         * Called for each child the pointer i on when the drawer is deciding whether to intercept the touch event.\n         *\n         * @param v     View to test for draggability\n         * @param delta Delta drag in pixels\n         * @param x     X coordinate of the active touch point\n         * @param y     Y coordinate of the active touch point\n         * @return true if view is draggable by delta dx.\n         */\n        boolean isViewDraggable(View v, int delta, int x, int y);\n    }\n\n    public enum Type {\n        /**\n         * Positions the drawer behind the content.\n         */\n        BEHIND,\n\n        /**\n         * A static drawer that can not be dragged.\n         */\n        STATIC,\n\n        /**\n         * Positions the drawer on top of the content.\n         */\n        OVERLAY,\n    }\n\n    /**\n     * Tag used when logging.\n     */\n    private static final String TAG = \"MenuDrawer\";\n\n    /**\n     * Indicates whether debug code should be enabled.\n     */\n    private static final boolean DEBUG = false;\n\n    /**\n     * The time between each frame when animating the drawer.\n     */\n    protected static final int ANIMATION_DELAY = 1000 / 60;\n\n    /**\n     * The default touch bezel size of the drawer in dp.\n     */\n    private static final int DEFAULT_DRAG_BEZEL_DP = 24;\n\n    /**\n     * The default drop shadow size in dp.\n     */\n    private static final int DEFAULT_DROP_SHADOW_DP = 6;\n\n    /**\n     * Drag mode for sliding only the content view.\n     */\n    public static final int MENU_DRAG_CONTENT = 0;\n\n    /**\n     * Drag mode for sliding the entire window.\n     */\n    public static final int MENU_DRAG_WINDOW = 1;\n\n    /**\n     * Disallow opening the drawer by dragging the screen.\n     */\n    public static final int TOUCH_MODE_NONE = 0;\n\n    /**\n     * Allow opening drawer only by dragging on the edge of the screen.\n     */\n    public static final int TOUCH_MODE_BEZEL = 1;\n\n    /**\n     * Allow opening drawer by dragging anywhere on the screen.\n     */\n    public static final int TOUCH_MODE_FULLSCREEN = 2;\n\n    /**\n     * Indicates that the drawer is currently closed.\n     */\n    public static final int STATE_CLOSED = 0;\n\n    /**\n     * Indicates that the drawer is currently closing.\n     */\n    public static final int STATE_CLOSING = 1;\n\n    /**\n     * Indicates that the drawer is currently being dragged by the user.\n     */\n    public static final int STATE_DRAGGING = 2;\n\n    /**\n     * Indicates that the drawer is currently opening.\n     */\n    public static final int STATE_OPENING = 4;\n\n    /**\n     * Indicates that the drawer is currently open.\n     */\n    public static final int STATE_OPEN = 8;\n\n    /**\n     * Indicates whether to use {@link View#setTranslationX(float)} when positioning views.\n     */\n    static final boolean USE_TRANSLATIONS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;\n\n    /**\n     * Time to animate the indicator to the new active view.\n     */\n    static final int INDICATOR_ANIM_DURATION = 800;\n\n    /**\n     * The maximum animation duration.\n     */\n    private static final int DEFAULT_ANIMATION_DURATION = 600;\n\n    /**\n     * Interpolator used when animating the drawer open/closed.\n     */\n    protected static final Interpolator SMOOTH_INTERPOLATOR = new SmoothInterpolator();\n\n    /**\n     * Interpolator used for stretching/retracting the active indicator.\n     */\n    protected static final Interpolator INDICATOR_INTERPOLATOR = new AccelerateInterpolator();\n\n    /**\n     * Drawable used as menu overlay.\n     */\n    protected Drawable mMenuOverlay;\n\n    /**\n     * Defines whether the drop shadow is enabled.\n     */\n    protected boolean mDropShadowEnabled;\n\n    /**\n     * The color of the drop shadow.\n     */\n    protected int mDropShadowColor;\n\n    /**\n     * Drawable used as content drop shadow onto the menu.\n     */\n    protected Drawable mDropShadowDrawable;\n\n    private boolean mCustomDropShadow;\n\n    /**\n     * The size of the content drop shadow.\n     */\n    protected int mDropShadowSize;\n\n    /**\n     * Bitmap used to indicate the active view.\n     */\n    protected Bitmap mActiveIndicator;\n\n    /**\n     * The currently active view.\n     */\n    protected View mActiveView;\n\n    /**\n     * Position of the active view. This is compared to View#getTag(R.id.mdActiveViewPosition) when drawing the\n     * indicator.\n     */\n    protected int mActivePosition;\n\n    /**\n     * Whether the indicator should be animated between positions.\n     */\n    private boolean mAllowIndicatorAnimation;\n\n    /**\n     * Used when reading the position of the active view.\n     */\n    protected final Rect mActiveRect = new Rect();\n\n    /**\n     * Temporary {@link Rect} used for deciding whether the view should be invalidated so the indicator can be redrawn.\n     */\n    private final Rect mTempRect = new Rect();\n\n    /**\n     * The custom menu view set by the user.\n     */\n    private View mMenuView;\n\n    /**\n     * The parent of the menu view.\n     */\n    protected BuildLayerFrameLayout mMenuContainer;\n\n    /**\n     * The parent of the content view.\n     */\n    protected BuildLayerFrameLayout mContentContainer;\n\n    /**\n     * The size of the menu (width or height depending on the gravity).\n     */\n    protected int mMenuSize;\n\n    /**\n     * Indicates whether the menu is currently visible.\n     */\n    protected boolean mMenuVisible;\n\n    /**\n     * The drag mode of the drawer. Can be either {@link #MENU_DRAG_CONTENT} or {@link #MENU_DRAG_WINDOW}.\n     */\n    private int mDragMode = MENU_DRAG_CONTENT;\n\n    /**\n     * The current drawer state.\n     *\n     * @see #STATE_CLOSED\n     * @see #STATE_CLOSING\n     * @see #STATE_DRAGGING\n     * @see #STATE_OPENING\n     * @see #STATE_OPEN\n     */\n    protected int mDrawerState = STATE_CLOSED;\n\n    /**\n     * The touch bezel size of the drawer in px.\n     */\n    protected int mTouchBezelSize;\n\n    /**\n     * The touch area size of the drawer in px.\n     */\n    protected int mTouchSize;\n\n    /**\n     * Listener used to dispatch state change events.\n     */\n    private OnDrawerStateChangeListener mOnDrawerStateChangeListener;\n\n    /**\n     * Touch mode for the Drawer.\n     * Possible values are {@link #TOUCH_MODE_NONE}, {@link #TOUCH_MODE_BEZEL} or {@link #TOUCH_MODE_FULLSCREEN}\n     * Default: {@link #TOUCH_MODE_BEZEL}\n     */\n    protected int mTouchMode = TOUCH_MODE_BEZEL;\n\n    /**\n     * Indicates whether to use {@link View#LAYER_TYPE_HARDWARE} when animating the drawer.\n     */\n    protected boolean mHardwareLayersEnabled = true;\n\n    /**\n     * The Activity the drawer is attached to.\n     */\n    private Activity mActivity;\n\n    /**\n     * Scroller used when animating the indicator to a new position.\n     */\n    private FloatScroller mIndicatorScroller;\n\n    /**\n     * Runnable used when animating the indicator to a new position.\n     */\n    private Runnable mIndicatorRunnable = new Runnable() {\n        @Override\n        public void run() {\n            animateIndicatorInvalidate();\n        }\n    };\n\n    /**\n     * The start position of the indicator when animating it to a new position.\n     */\n    protected int mIndicatorStartPos;\n\n    /**\n     * [0..1] value indicating the current progress of the animation.\n     */\n    protected float mIndicatorOffset;\n\n    /**\n     * Whether the indicator is currently animating.\n     */\n    protected boolean mIndicatorAnimating;\n\n    /**\n     * Bundle used to hold the drawers state.\n     */\n    protected Bundle mState;\n\n    /**\n     * The maximum duration of open/close animations.\n     */\n    protected int mMaxAnimationDuration = DEFAULT_ANIMATION_DURATION;\n\n    /**\n     * Callback that lets the listener override intercepting of touch events.\n     */\n    protected OnInterceptMoveEventListener mOnInterceptMoveEventListener;\n\n    protected SlideDrawable mSlideDrawable;\n\n    protected Drawable mThemeUpIndicator;\n\n    protected boolean mDrawerIndicatorEnabled;\n\n    private ActionBarHelper mActionBarHelper;\n\n    private int mCurrentUpContentDesc;\n\n    private int mDrawerOpenContentDesc;\n\n    private int mDrawerClosedContentDesc;\n\n    /**\n     * The position of the drawer.\n     */\n    private Position mPosition;\n\n    private Position mResolvedPosition;\n\n    private final Rect mIndicatorClipRect = new Rect();\n\n    protected boolean mIsStatic;\n\n    protected final Rect mDropShadowRect = new Rect();\n\n    /**\n     * Current offset.\n     */\n    protected float mOffsetPixels;\n\n    /**\n     * Whether an overlay should be drawn as the drawer is opened and closed.\n     */\n    protected boolean mDrawOverlay;\n\n    /**\n     * Attaches the MenuDrawer to the Activity.\n     *\n     * @param activity The activity that the MenuDrawer will be attached to.\n     * @return The created MenuDrawer instance.\n     */\n    public static MenuDrawer attach(Activity activity) {\n        return attach(activity, Type.BEHIND);\n    }\n\n    /**\n     * Attaches the MenuDrawer to the Activity.\n     *\n     * @param activity The activity the menu drawer will be attached to.\n     * @param type     The {@link Type} of the drawer.\n     * @return The created MenuDrawer instance.\n     */\n    public static MenuDrawer attach(Activity activity, Type type) {\n        return attach(activity, type, Position.START);\n    }\n\n    /**\n     * Attaches the MenuDrawer to the Activity.\n     *\n     * @param activity The activity the menu drawer will be attached to.\n     * @param position Where to position the menu.\n     * @return The created MenuDrawer instance.\n     */\n    public static MenuDrawer attach(Activity activity, Position position) {\n        return attach(activity, Type.BEHIND, position);\n    }\n\n    /**\n     * Attaches the MenuDrawer to the Activity.\n     *\n     * @param activity The activity the menu drawer will be attached to.\n     * @param type     The {@link Type} of the drawer.\n     * @param position Where to position the menu.\n     * @return The created MenuDrawer instance.\n     */\n    public static MenuDrawer attach(Activity activity, Type type, Position position) {\n        return attach(activity, type, position, MENU_DRAG_CONTENT);\n    }\n\n    /**\n     * Attaches the MenuDrawer to the Activity.\n     *\n     * @param activity The activity the menu drawer will be attached to.\n     * @param type     The {@link Type} of the drawer.\n     * @param position Where to position the menu.\n     * @param dragMode The drag mode of the drawer. Can be either {@link MenuDrawer#MENU_DRAG_CONTENT}\n     *                 or {@link MenuDrawer#MENU_DRAG_WINDOW}.\n     * @return The created MenuDrawer instance.\n     */\n    public static MenuDrawer attach(Activity activity, Type type, Position position, int dragMode) {\n        MenuDrawer menuDrawer = createMenuDrawer(activity, dragMode, position, type);\n        menuDrawer.setId(R.id.md__drawer);\n\n        switch (dragMode) {\n            case MenuDrawer.MENU_DRAG_CONTENT:\n                attachToContent(activity, menuDrawer);\n                break;\n\n            case MenuDrawer.MENU_DRAG_WINDOW:\n                attachToDecor(activity, menuDrawer);\n                break;\n\n            default:\n                throw new RuntimeException(\"Unknown menu mode: \" + dragMode);\n        }\n\n        return menuDrawer;\n    }\n\n    /**\n     * Constructs the appropriate MenuDrawer based on the position.\n     */\n    private static MenuDrawer createMenuDrawer(Activity activity, int dragMode, Position position, Type type) {\n        MenuDrawer drawer;\n\n        if (type == Type.STATIC) {\n            drawer = new StaticDrawer(activity);\n\n        } else if (type == Type.OVERLAY) {\n            drawer = new OverlayDrawer(activity, dragMode);\n            if (position == Position.LEFT || position == Position.START) {\n                drawer.setupUpIndicator(activity);\n            }\n\n        } else {\n            drawer = new SlidingDrawer(activity, dragMode);\n            if (position == Position.LEFT || position == Position.START) {\n                drawer.setupUpIndicator(activity);\n            }\n        }\n\n        drawer.mDragMode = dragMode;\n        drawer.setPosition(position);\n\n        return drawer;\n    }\n\n    /**\n     * Attaches the menu drawer to the content view.\n     */\n    private static void attachToContent(Activity activity, MenuDrawer menuDrawer) {\n        /**\n         * Do not call mActivity#setContentView.\n         * E.g. if using with a ListActivity, Activity#setContentView is overridden and dispatched to\n         * MenuDrawer#setContentView, which then again would call Activity#setContentView.\n         */\n        ViewGroup content = (ViewGroup) activity.findViewById(android.R.id.content);\n        content.removeAllViews();\n        content.addView(menuDrawer, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);\n    }\n\n    /**\n     * Attaches the menu drawer to the window.\n     */\n    private static void attachToDecor(Activity activity, MenuDrawer menuDrawer) {\n        ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();\n        ViewGroup decorChild = (ViewGroup) decorView.getChildAt(0);\n\n        decorView.removeAllViews();\n        decorView.addView(menuDrawer, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);\n\n        menuDrawer.mContentContainer.addView(decorChild, decorChild.getLayoutParams());\n    }\n\n    MenuDrawer(Activity activity, int dragMode) {\n        this(activity);\n\n        mActivity = activity;\n        mDragMode = dragMode;\n    }\n\n    public MenuDrawer(Context context) {\n        this(context, null);\n    }\n\n    public MenuDrawer(Context context, AttributeSet attrs) {\n        this(context, attrs, R.attr.menuDrawerStyle);\n    }\n\n    public MenuDrawer(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        initDrawer(context, attrs, defStyle);\n    }\n\n    protected void initDrawer(Context context, AttributeSet attrs, int defStyle) {\n        setWillNotDraw(false);\n        setFocusable(false);\n\n        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MenuDrawer, R.attr.menuDrawerStyle,\n                R.style.Widget_MenuDrawer);\n\n        final Drawable contentBackground = a.getDrawable(R.styleable.MenuDrawer_mdContentBackground);\n        final Drawable menuBackground = a.getDrawable(R.styleable.MenuDrawer_mdMenuBackground);\n\n        mMenuSize = a.getDimensionPixelSize(R.styleable.MenuDrawer_mdMenuSize, dpToPx(240));\n\n        final int indicatorResId = a.getResourceId(R.styleable.MenuDrawer_mdActiveIndicator, 0);\n        if (indicatorResId != 0) {\n            mActiveIndicator = BitmapFactory.decodeResource(getResources(), indicatorResId);\n        }\n\n        mDropShadowEnabled = a.getBoolean(R.styleable.MenuDrawer_mdDropShadowEnabled, true);\n\n        mDropShadowDrawable = a.getDrawable(R.styleable.MenuDrawer_mdDropShadow);\n\n        if (mDropShadowDrawable == null) {\n            mDropShadowColor = a.getColor(R.styleable.MenuDrawer_mdDropShadowColor, 0xFF000000);\n        } else {\n            mCustomDropShadow = true;\n        }\n\n        mDropShadowSize = a.getDimensionPixelSize(R.styleable.MenuDrawer_mdDropShadowSize,\n                dpToPx(DEFAULT_DROP_SHADOW_DP));\n\n        mTouchBezelSize = a.getDimensionPixelSize(R.styleable.MenuDrawer_mdTouchBezelSize,\n                dpToPx(DEFAULT_DRAG_BEZEL_DP));\n\n        mAllowIndicatorAnimation = a.getBoolean(R.styleable.MenuDrawer_mdAllowIndicatorAnimation, false);\n\n        mMaxAnimationDuration = a.getInt(R.styleable.MenuDrawer_mdMaxAnimationDuration, DEFAULT_ANIMATION_DURATION);\n\n        final int slideDrawableResId = a.getResourceId(R.styleable.MenuDrawer_mdSlideDrawable, -1);\n        if (slideDrawableResId != -1) {\n            setSlideDrawable(slideDrawableResId);\n        }\n\n        mDrawerOpenContentDesc = a.getResourceId(R.styleable.MenuDrawer_mdDrawerOpenUpContentDescription, 0);\n        mDrawerClosedContentDesc = a.getResourceId(R.styleable.MenuDrawer_mdDrawerClosedUpContentDescription, 0);\n\n        mDrawOverlay = a.getBoolean(R.styleable.MenuDrawer_mdDrawOverlay, true);\n\n        final int position = a.getInt(R.styleable.MenuDrawer_mdPosition, 0);\n        setPosition(Position.fromValue(position));\n\n        a.recycle();\n\n        mMenuContainer = new NoClickThroughFrameLayout(context);\n        mMenuContainer.setId(R.id.md__menu);\n        mMenuContainer.setBackgroundDrawable(menuBackground);\n\n        mContentContainer = new NoClickThroughFrameLayout(context);\n        mContentContainer.setId(R.id.md__content);\n        mContentContainer.setBackgroundDrawable(contentBackground);\n\n        mMenuOverlay = new ColorDrawable(0x00000000);//fix by liweiping 20140930\n\n        mIndicatorScroller = new FloatScroller(SMOOTH_INTERPOLATOR);\n    }\n\n    @Override\n    protected void onFinishInflate() {\n        super.onFinishInflate();\n        View menu = findViewById(R.id.mdMenu);\n        if (menu != null) {\n            removeView(menu);\n            setMenuView(menu);\n        }\n\n        View content = findViewById(R.id.mdContent);\n        if (content != null) {\n            removeView(content);\n            setContentView(content);\n        }\n\n        if (getChildCount() > 2) {\n            throw new IllegalStateException(\n                    \"Menu and content view added in xml must have id's @id/mdMenu and @id/mdContent\");\n        }\n    }\n\n    protected int dpToPx(int dp) {\n        return (int) (getResources().getDisplayMetrics().density * dp + 0.5f);\n    }\n\n    protected boolean isViewDescendant(View v) {\n        ViewParent parent = v.getParent();\n        while (parent != null) {\n            if (parent == this) {\n                return true;\n            }\n\n            parent = parent.getParent();\n        }\n\n        return false;\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        getViewTreeObserver().addOnScrollChangedListener(mScrollListener);\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        getViewTreeObserver().removeOnScrollChangedListener(mScrollListener);\n        super.onDetachedFromWindow();\n    }\n\n    private boolean shouldDrawIndicator() {\n        return mActiveView != null && mActiveIndicator != null && isViewDescendant(mActiveView);\n    }\n\n    @Override\n    protected void dispatchDraw(Canvas canvas) {\n        super.dispatchDraw(canvas);\n        final int offsetPixels = (int) mOffsetPixels;\n\n        if (mDrawOverlay && offsetPixels != 0) {\n            drawOverlay(canvas);\n        }\n        if (mDropShadowEnabled && (offsetPixels != 0 || mIsStatic)) {\n            drawDropShadow(canvas);\n        }\n        if (shouldDrawIndicator() && (offsetPixels != 0 || mIsStatic)) {\n            drawIndicator(canvas);\n        }\n    }\n\n    protected abstract void drawOverlay(Canvas canvas);\n\n    private void drawDropShadow(Canvas canvas) {\n        // Can't pass the position to the constructor, so wait with loading the drawable until the drop shadow is\n        // actually drawn.\n        if (mDropShadowDrawable == null) {\n            setDropShadowColor(mDropShadowColor);\n        }\n\n        updateDropShadowRect();\n        mDropShadowDrawable.setBounds(mDropShadowRect);\n        mDropShadowDrawable.draw(canvas);\n    }\n\n    protected void updateDropShadowRect() {\n        // This updates the rect for the static and sliding drawer. The overlay drawer has its own implementation.\n        switch (getPosition()) {\n            case LEFT:\n                mDropShadowRect.top = 0;\n                mDropShadowRect.bottom = getHeight();\n                mDropShadowRect.right = ViewHelper.getLeft(mContentContainer);\n                mDropShadowRect.left = mDropShadowRect.right - mDropShadowSize;\n                break;\n\n            case TOP:\n                mDropShadowRect.left = 0;\n                mDropShadowRect.right = getWidth();\n                mDropShadowRect.bottom = ViewHelper.getTop(mContentContainer);\n                mDropShadowRect.top = mDropShadowRect.bottom - mDropShadowSize;\n                break;\n\n            case RIGHT:\n                mDropShadowRect.top = 0;\n                mDropShadowRect.bottom = getHeight();\n                mDropShadowRect.left = ViewHelper.getRight(mContentContainer);\n                mDropShadowRect.right = mDropShadowRect.left + mDropShadowSize;\n                break;\n\n            case BOTTOM:\n                mDropShadowRect.left = 0;\n                mDropShadowRect.right = getWidth();\n                mDropShadowRect.top = ViewHelper.getBottom(mContentContainer);\n                mDropShadowRect.bottom = mDropShadowRect.top + mDropShadowSize;\n                break;\n        }\n    }\n\n    private void drawIndicator(Canvas canvas) {\n        Integer position = (Integer) mActiveView.getTag(R.id.mdActiveViewPosition);\n        final int pos = position == null ? 0 : position;\n        if (pos == mActivePosition) {\n            updateIndicatorClipRect();\n            canvas.save();\n            canvas.clipRect(mIndicatorClipRect);\n\n            int drawLeft = 0;\n            int drawTop = 0;\n            switch (getPosition()) {\n                case LEFT:\n                case TOP:\n                    drawLeft = mIndicatorClipRect.left;\n                    drawTop = mIndicatorClipRect.top;\n                    break;\n\n                case RIGHT:\n                    drawLeft = mIndicatorClipRect.right - mActiveIndicator.getWidth();\n                    drawTop = mIndicatorClipRect.top;\n                    break;\n\n                case BOTTOM:\n                    drawLeft = mIndicatorClipRect.left;\n                    drawTop = mIndicatorClipRect.bottom - mActiveIndicator.getHeight();\n            }\n\n            canvas.drawBitmap(mActiveIndicator, drawLeft, drawTop, null);\n            canvas.restore();\n        }\n    }\n\n    /**\n     * Update the {@link Rect} where the indicator is drawn.\n     */\n    protected void updateIndicatorClipRect() {\n        mActiveView.getDrawingRect(mActiveRect);\n        offsetDescendantRectToMyCoords(mActiveView, mActiveRect);\n\n        final float openRatio = mIsStatic ? 1.0f : Math.abs(mOffsetPixels) / mMenuSize;\n\n        final float interpolatedRatio = 1.f - INDICATOR_INTERPOLATOR.getInterpolation((1.f - openRatio));\n\n        final int indicatorWidth = mActiveIndicator.getWidth();\n        final int indicatorHeight = mActiveIndicator.getHeight();\n\n        final int interpolatedWidth = (int) (indicatorWidth * interpolatedRatio);\n        final int interpolatedHeight = (int) (indicatorHeight * interpolatedRatio);\n\n        final int startPos = mIndicatorStartPos;\n\n        int left = 0;\n        int top = 0;\n        int right = 0;\n        int bottom = 0;\n\n        switch (getPosition()) {\n            case LEFT:\n            case RIGHT:\n                final int finalTop = mActiveRect.top + ((mActiveRect.height() - indicatorHeight) / 2);\n                if (mIndicatorAnimating) {\n                    top = (int) (startPos + ((finalTop - startPos) * mIndicatorOffset));\n                } else {\n                    top = finalTop;\n                }\n                bottom = top + indicatorHeight;\n                break;\n\n            case TOP:\n            case BOTTOM:\n                final int finalLeft = mActiveRect.left + ((mActiveRect.width() - indicatorWidth) / 2);\n                if (mIndicatorAnimating) {\n                    left = (int) (startPos + ((finalLeft - startPos) * mIndicatorOffset));\n                } else {\n                    left = finalLeft;\n                }\n                right = left + indicatorWidth;\n                break;\n        }\n\n        switch (getPosition()) {\n            case LEFT: {\n                right = ViewHelper.getLeft(mContentContainer);\n                left = right - interpolatedWidth;\n                break;\n            }\n\n            case TOP: {\n                bottom = ViewHelper.getTop(mContentContainer);\n                top = bottom - interpolatedHeight;\n                break;\n            }\n\n            case RIGHT: {\n                left = ViewHelper.getRight(mContentContainer);\n                right = left + interpolatedWidth;\n                break;\n            }\n\n            case BOTTOM: {\n                top = ViewHelper.getBottom(mContentContainer);\n                bottom = top + interpolatedHeight;\n                break;\n            }\n        }\n\n        mIndicatorClipRect.left = left;\n        mIndicatorClipRect.top = top;\n        mIndicatorClipRect.right = right;\n        mIndicatorClipRect.bottom = bottom;\n    }\n\n    private void setPosition(Position position) {\n        mPosition = position;\n        mResolvedPosition = getPosition();\n    }\n\n    protected Position getPosition() {\n        final int layoutDirection = ViewHelper.getLayoutDirection(this);\n\n        switch (mPosition) {\n            case START:\n                if (layoutDirection == LAYOUT_DIRECTION_RTL) {\n                    return Position.RIGHT;\n                } else {\n                    return Position.LEFT;\n                }\n\n            case END:\n                if (layoutDirection == LAYOUT_DIRECTION_RTL) {\n                    return Position.LEFT;\n                } else {\n                    return Position.RIGHT;\n                }\n        }\n\n        return mPosition;\n    }\n\n    @Override\n    public void onRtlPropertiesChanged(int layoutDirection) {\n        super.onRtlPropertiesChanged(layoutDirection);\n\n        if (!mCustomDropShadow) setDropShadowColor(mDropShadowColor);\n\n        if (getPosition() != mResolvedPosition) {\n            mResolvedPosition = getPosition();\n            setOffsetPixels(mOffsetPixels * -1);\n        }\n\n        if (mSlideDrawable != null) mSlideDrawable.setIsRtl(layoutDirection == LAYOUT_DIRECTION_RTL);\n\n        requestLayout();\n        invalidate();\n    }\n\n    /**\n     * Sets the number of pixels the content should be offset.\n     *\n     * @param offsetPixels The number of pixels to offset the content by.\n     */\n    protected void setOffsetPixels(float offsetPixels) {\n        final int oldOffset = (int) mOffsetPixels;\n        final int newOffset = (int) offsetPixels;\n\n        mOffsetPixels = offsetPixels;\n\n        if (mSlideDrawable != null) {\n            final float offset = Math.abs(mOffsetPixels) / mMenuSize;\n            mSlideDrawable.setOffset(offset);\n            updateUpContentDescription();\n        }\n\n        if (newOffset != oldOffset) {\n            onOffsetPixelsChanged(newOffset);\n            mMenuVisible = newOffset != 0;\n\n            // Notify any attached listeners of the current open ratio\n            final float openRatio = ((float) Math.abs(newOffset)) / mMenuSize;\n            dispatchOnDrawerSlide(openRatio, newOffset);\n        }\n    }\n\n    /**\n     * Called when the number of pixels the content should be offset by has changed.\n     *\n     * @param offsetPixels The number of pixels to offset the content by.\n     */\n    protected abstract void onOffsetPixelsChanged(int offsetPixels);\n\n    /**\n     * Toggles the menu open and close with animation.\n     */\n    public void toggleMenu() {\n        toggleMenu(true);\n    }\n\n    /**\n     * Toggles the menu open and close.\n     *\n     * @param animate Whether open/close should be animated.\n     */\n    public abstract void toggleMenu(boolean animate);\n\n    /**\n     * Animates the menu open.\n     */\n    public void openMenu() {\n        openMenu(true);\n    }\n\n    /**\n     * Opens the menu.\n     *\n     * @param animate Whether open/close should be animated.\n     */\n    public abstract void openMenu(boolean animate);\n\n    /**\n     * Animates the menu closed.\n     */\n    public void closeMenu() {\n        closeMenu(true);\n    }\n\n    /**\n     * Closes the menu.\n     *\n     * @param animate Whether open/close should be animated.\n     */\n    public abstract void closeMenu(boolean animate);\n\n    /**\n     * Indicates whether the menu is currently visible.\n     *\n     * @return True if the menu is open, false otherwise.\n     */\n    public abstract boolean isMenuVisible();\n\n    /**\n     * Set the size of the menu drawer when open.\n     *\n     * @param size The size of the menu.\n     */\n    public abstract void setMenuSize(int size);\n\n    /**\n     * Returns the size of the menu.\n     *\n     * @return The size of the menu.\n     */\n    public int getMenuSize() {\n        return mMenuSize;\n    }\n\n    /**\n     * Set the active view.\n     * If the mdActiveIndicator attribute is set, this View will have the indicator drawn next to it.\n     *\n     * @param v The active view.\n     */\n    public void setActiveView(View v) {\n        setActiveView(v, 0);\n    }\n\n    /**\n     * Set the active view.\n     * If the mdActiveIndicator attribute is set, this View will have the indicator drawn next to it.\n     *\n     * @param v        The active view.\n     * @param position Optional position, usually used with ListView. v.setTag(R.id.mdActiveViewPosition, position)\n     *                 must be called first.\n     */\n    public void setActiveView(View v, int position) {\n        final View oldView = mActiveView;\n        mActiveView = v;\n        mActivePosition = position;\n\n        if (mAllowIndicatorAnimation && oldView != null) {\n            startAnimatingIndicator();\n        }\n\n        invalidate();\n    }\n\n    /**\n     * Sets whether the indicator should be animated between active views.\n     *\n     * @param animate Whether the indicator should be animated between active views.\n     */\n    public void setAllowIndicatorAnimation(boolean animate) {\n        if (animate != mAllowIndicatorAnimation) {\n            mAllowIndicatorAnimation = animate;\n            completeAnimatingIndicator();\n        }\n    }\n\n    /**\n     * Indicates whether the indicator should be animated between active views.\n     *\n     * @return Whether the indicator should be animated between active views.\n     */\n    public boolean getAllowIndicatorAnimation() {\n        return mAllowIndicatorAnimation;\n    }\n\n    /**\n     * Scroll listener that checks whether the active view has moved before the drawer is invalidated.\n     */\n    private ViewTreeObserver.OnScrollChangedListener mScrollListener = new ViewTreeObserver.OnScrollChangedListener() {\n        @Override\n        public void onScrollChanged() {\n            if (mActiveView != null && isViewDescendant(mActiveView)) {\n                mActiveView.getDrawingRect(mTempRect);\n                offsetDescendantRectToMyCoords(mActiveView, mTempRect);\n                if (mTempRect.left != mActiveRect.left || mTempRect.top != mActiveRect.top\n                        || mTempRect.right != mActiveRect.right || mTempRect.bottom != mActiveRect.bottom) {\n                    invalidate();\n                }\n            }\n        }\n    };\n\n    /**\n     * Starts animating the indicator to a new position.\n     */\n    private void startAnimatingIndicator() {\n        mIndicatorStartPos = getIndicatorStartPos();\n        mIndicatorAnimating = true;\n        mIndicatorScroller.startScroll(0.0f, 1.0f, INDICATOR_ANIM_DURATION);\n\n        animateIndicatorInvalidate();\n    }\n\n    /**\n     * Returns the start position of the indicator.\n     *\n     * @return The start position of the indicator.\n     */\n    private int getIndicatorStartPos() {\n        switch (getPosition()) {\n            case TOP:\n                return mIndicatorClipRect.left;\n            case RIGHT:\n                return mIndicatorClipRect.top;\n            case BOTTOM:\n                return mIndicatorClipRect.left;\n            default:\n                return mIndicatorClipRect.top;\n        }\n    }\n\n    /**\n     * Compute the touch area based on the touch mode.\n     */\n    protected void updateTouchAreaSize() {\n        if (mTouchMode == TOUCH_MODE_BEZEL) {\n            mTouchSize = mTouchBezelSize;\n        } else if (mTouchMode == TOUCH_MODE_FULLSCREEN) {\n            mTouchSize = getMeasuredWidth();\n        } else {\n            mTouchSize = 0;\n        }\n    }\n\n    /**\n     * Callback when each frame in the indicator animation should be drawn.\n     */\n    private void animateIndicatorInvalidate() {\n        if (mIndicatorScroller.computeScrollOffset()) {\n            mIndicatorOffset = mIndicatorScroller.getCurr();\n            invalidate();\n\n            if (!mIndicatorScroller.isFinished()) {\n                postOnAnimation(mIndicatorRunnable);\n                return;\n            }\n        }\n\n        completeAnimatingIndicator();\n    }\n\n    /**\n     * Called when the indicator animation has completed.\n     */\n    private void completeAnimatingIndicator() {\n        mIndicatorOffset = 1.0f;\n        mIndicatorAnimating = false;\n        invalidate();\n    }\n\n    /**\n     * Enables or disables offsetting the menu when dragging the drawer.\n     *\n     * @param offsetMenu True to offset the menu, false otherwise.\n     */\n    public abstract void setOffsetMenuEnabled(boolean offsetMenu);\n\n    /**\n     * Indicates whether the menu is being offset when dragging the drawer.\n     *\n     * @return True if the menu is being offset, false otherwise.\n     */\n    public abstract boolean getOffsetMenuEnabled();\n\n    /**\n     * Get the current state of the drawer.\n     *\n     * @return The state of the drawer.\n     */\n    public int getDrawerState() {\n        return mDrawerState;\n    }\n\n    /**\n     * Register a callback to be invoked when the drawer state changes.\n     *\n     * @param listener The callback that will run.\n     */\n    public void setOnDrawerStateChangeListener(OnDrawerStateChangeListener listener) {\n        mOnDrawerStateChangeListener = listener;\n    }\n\n    /**\n     * Register a callback that will be invoked when the drawer is about to intercept touch events.\n     *\n     * @param listener The callback that will be invoked.\n     */\n    public void setOnInterceptMoveEventListener(OnInterceptMoveEventListener listener) {\n        mOnInterceptMoveEventListener = listener;\n    }\n\n    /**\n     * Defines whether the drop shadow is enabled.\n     *\n     * @param enabled Whether the drop shadow is enabled.\n     */\n    public void setDropShadowEnabled(boolean enabled) {\n        mDropShadowEnabled = enabled;\n        invalidate();\n    }\n\n    protected GradientDrawable.Orientation getDropShadowOrientation() {\n        // Gets the orientation for the static and sliding drawer. The overlay drawer provides its own implementation.\n        switch (getPosition()) {\n            case TOP:\n                return GradientDrawable.Orientation.BOTTOM_TOP;\n\n            case RIGHT:\n                return GradientDrawable.Orientation.LEFT_RIGHT;\n\n            case BOTTOM:\n                return GradientDrawable.Orientation.TOP_BOTTOM;\n\n            default:\n                return GradientDrawable.Orientation.RIGHT_LEFT;\n        }\n    }\n\n    /**\n     * Sets the color of the drop shadow.\n     *\n     * @param color The color of the drop shadow.\n     */\n    public void setDropShadowColor(int color) {\n        GradientDrawable.Orientation orientation = getDropShadowOrientation();\n\n        final int endColor = color & 0x00FFFFFF;\n        mDropShadowDrawable = new GradientDrawable(orientation,\n                new int[] {\n                        color,\n                        endColor,\n                });\n        invalidate();\n    }\n\n    /**\n     * Sets the drawable of the drop shadow.\n     *\n     * @param drawable The drawable of the drop shadow.\n     */\n    public void setDropShadow(Drawable drawable) {\n        mDropShadowDrawable = drawable;\n        mCustomDropShadow = drawable != null;\n        invalidate();\n    }\n\n    /**\n     * Sets the drawable of the drop shadow.\n     *\n     * @param resId The resource identifier of the the drawable.\n     */\n    public void setDropShadow(int resId) {\n        setDropShadow(getResources().getDrawable(resId));\n    }\n\n    /**\n     * Returns the drawable of the drop shadow.\n     */\n    public Drawable getDropShadow() {\n        return mDropShadowDrawable;\n    }\n\n    /**\n     * Sets the size of the drop shadow.\n     *\n     * @param size The size of the drop shadow in px.\n     */\n    public void setDropShadowSize(int size) {\n        mDropShadowSize = size;\n        invalidate();\n    }\n\n    /**\n     * Animates the drawer slightly open until the user opens the drawer.\n     */\n    public abstract void peekDrawer();\n\n    /**\n     * Animates the drawer slightly open. If delay is larger than 0, this happens until the user opens the drawer.\n     *\n     * @param delay The delay (in milliseconds) between each run of the animation. If 0, this animation is only run\n     *              once.\n     */\n    public abstract void peekDrawer(long delay);\n\n    /**\n     * Animates the drawer slightly open. If delay is larger than 0, this happens until the user opens the drawer.\n     *\n     * @param startDelay The delay (in milliseconds) until the animation is first run.\n     * @param delay      The delay (in milliseconds) between each run of the animation. If 0, this animation is only run\n     *                   once.\n     */\n    public abstract void peekDrawer(long startDelay, long delay);\n\n    /**\n     * Enables or disables the user of {@link View#LAYER_TYPE_HARDWARE} when animations views.\n     *\n     * @param enabled Whether hardware layers are enabled.\n     */\n    public abstract void setHardwareLayerEnabled(boolean enabled);\n\n    /**\n     * Sets the maximum duration of open/close animations.\n     *\n     * @param duration The maximum duration in milliseconds.\n     */\n    public void setMaxAnimationDuration(int duration) {\n        mMaxAnimationDuration = duration;\n    }\n\n    /**\n     * Sets whether an overlay should be drawn when sliding the drawer.\n     *\n     * @param drawOverlay Whether an overlay should be drawn when sliding the drawer.\n     */\n    public void setDrawOverlay(boolean drawOverlay) {\n        mDrawOverlay = drawOverlay;\n    }\n\n    /**\n     * Gets whether an overlay is drawn when sliding the drawer.\n     *\n     * @return Whether an overlay is drawn when sliding the drawer.\n     */\n    public boolean getDrawOverlay() {\n        return mDrawOverlay;\n    }\n\n    protected void updateUpContentDescription() {\n        final int upContentDesc = isMenuVisible() ? mDrawerOpenContentDesc : mDrawerClosedContentDesc;\n        if (mDrawerIndicatorEnabled && mActionBarHelper != null && upContentDesc != mCurrentUpContentDesc) {\n            mCurrentUpContentDesc = upContentDesc;\n            mActionBarHelper.setActionBarDescription(upContentDesc);\n        }\n    }\n\n    /**\n     * Sets the drawable used as the drawer indicator.\n     *\n     * @param drawable The drawable used as the drawer indicator.\n     */\n    public void setSlideDrawable(int drawableRes) {\n        setSlideDrawable(getResources().getDrawable(drawableRes));\n    }\n\n    /**\n     * Sets the drawable used as the drawer indicator.\n     *\n     * @param drawable The drawable used as the drawer indicator.\n     */\n    public void setSlideDrawable(Drawable drawable) {\n        mSlideDrawable = new SlideDrawable(drawable);\n        mSlideDrawable.setIsRtl(ViewHelper.getLayoutDirection(this) == LAYOUT_DIRECTION_RTL);\n\n        if (mActionBarHelper != null) {\n            mActionBarHelper.setDisplayShowHomeAsUpEnabled(true);\n\n            if (mDrawerIndicatorEnabled) {\n                mActionBarHelper.setActionBarUpIndicator(mSlideDrawable,\n                        isMenuVisible() ? mDrawerOpenContentDesc : mDrawerClosedContentDesc);\n            }\n        }\n    }\n\n    /**\n     * Sets up the drawer indicator. It cna then be shown with {@link #setDrawerIndicatorEnabled(boolean)}.\n     *\n     * @param activity The activity the drawer is attached to.\n     */\n    public void setupUpIndicator(Activity activity) {\n        if (mActionBarHelper == null) {\n            mActionBarHelper = new ActionBarHelper(activity);\n            mThemeUpIndicator = mActionBarHelper.getThemeUpIndicator();\n\n            if (mDrawerIndicatorEnabled) {\n                mActionBarHelper.setActionBarUpIndicator(mSlideDrawable,\n                        isMenuVisible() ? mDrawerOpenContentDesc : mDrawerClosedContentDesc);\n            }\n        }\n    }\n\n    /**\n     * Sets whether the drawer indicator should be enabled. {@link #setupUpIndicator(android.app.Activity)} must be\n     * called first.\n     *\n     * @param enabled Whether the drawer indicator should enabled.\n     */\n    public void setDrawerIndicatorEnabled(boolean enabled) {\n        if (mActionBarHelper == null) {\n            throw new IllegalStateException(\"setupUpIndicator(Activity) has not been called\");\n        }\n\n        mDrawerIndicatorEnabled = enabled;\n        if (enabled) {\n            mActionBarHelper.setActionBarUpIndicator(mSlideDrawable,\n                    isMenuVisible() ? mDrawerOpenContentDesc : mDrawerClosedContentDesc);\n        } else {\n            mActionBarHelper.setActionBarUpIndicator(mThemeUpIndicator, 0);\n        }\n    }\n\n    /**\n     * Indicates whether the drawer indicator is currently enabled.\n     *\n     * @return Whether the drawer indicator is enabled.\n     */\n    public boolean isDrawerIndicatorEnabled() {\n        return mDrawerIndicatorEnabled;\n    }\n\n    /**\n     * Returns the ViewGroup used as a parent for the menu view.\n     *\n     * @return The menu view's parent.\n     */\n    public ViewGroup getMenuContainer() {\n        return mMenuContainer;\n    }\n\n    /**\n     * Returns the ViewGroup used as a parent for the content view.\n     *\n     * @return The content view's parent.\n     */\n    public ViewGroup getContentContainer() {\n        if (mDragMode == MENU_DRAG_CONTENT) {\n            return mContentContainer;\n        } else {\n            return (ViewGroup) findViewById(android.R.id.content);\n        }\n    }\n\n    /**\n     * Set the menu view from a layout resource.\n     *\n     * @param layoutResId Resource ID to be inflated.\n     */\n    public void setMenuView(int layoutResId) {\n        mMenuContainer.removeAllViews();\n        mMenuView = LayoutInflater.from(getContext()).inflate(layoutResId, mMenuContainer, false);\n        mMenuContainer.addView(mMenuView);\n    }\n\n    /**\n     * Set the menu view to an explicit view.\n     *\n     * @param view The menu view.\n     */\n    public void setMenuView(View view) {\n        setMenuView(view, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));\n    }\n\n    /**\n     * Set the menu view to an explicit view.\n     *\n     * @param view   The menu view.\n     * @param params Layout parameters for the view.\n     */\n    public void setMenuView(View view, LayoutParams params) {\n        mMenuView = view;\n        mMenuContainer.removeAllViews();\n        mMenuContainer.addView(view, params);\n    }\n\n    /**\n     * Returns the menu view.\n     *\n     * @return The menu view.\n     */\n    public View getMenuView() {\n        return mMenuView;\n    }\n\n    /**\n     * Set the content from a layout resource.\n     *\n     * @param layoutResId Resource ID to be inflated.\n     */\n    public void setContentView(int layoutResId) {\n        switch (mDragMode) {\n            case MenuDrawer.MENU_DRAG_CONTENT:\n                mContentContainer.removeAllViews();\n                LayoutInflater.from(getContext()).inflate(layoutResId, mContentContainer, true);\n                break;\n\n            case MenuDrawer.MENU_DRAG_WINDOW:\n                mActivity.setContentView(layoutResId);\n                break;\n        }\n    }\n\n    /**\n     * Set the content to an explicit view.\n     *\n     * @param view The desired content to display.\n     */\n    public void setContentView(View view) {\n        setContentView(view, new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));\n    }\n\n    /**\n     * Set the content to an explicit view.\n     *\n     * @param view   The desired content to display.\n     * @param params Layout parameters for the view.\n     */\n    public void setContentView(View view, LayoutParams params) {\n        switch (mDragMode) {\n            case MenuDrawer.MENU_DRAG_CONTENT:\n                mContentContainer.removeAllViews();\n                mContentContainer.addView(view, params);\n                break;\n\n            case MenuDrawer.MENU_DRAG_WINDOW:\n                mActivity.setContentView(view, params);\n                break;\n        }\n    }\n\n    protected void setDrawerState(int state) {\n        if (state != mDrawerState) {\n            final int oldState = mDrawerState;\n            mDrawerState = state;\n            if (mOnDrawerStateChangeListener != null) mOnDrawerStateChangeListener.onDrawerStateChange(oldState, state);\n            if (DEBUG) logDrawerState(state);\n        }\n    }\n\n    protected void logDrawerState(int state) {\n        switch (state) {\n            case STATE_CLOSED:\n                Log.d(TAG, \"[DrawerState] STATE_CLOSED\");\n                break;\n\n            case STATE_CLOSING:\n                Log.d(TAG, \"[DrawerState] STATE_CLOSING\");\n                break;\n\n            case STATE_DRAGGING:\n                Log.d(TAG, \"[DrawerState] STATE_DRAGGING\");\n                break;\n\n            case STATE_OPENING:\n                Log.d(TAG, \"[DrawerState] STATE_OPENING\");\n                break;\n\n            case STATE_OPEN:\n                Log.d(TAG, \"[DrawerState] STATE_OPEN\");\n                break;\n\n            default:\n                Log.d(TAG, \"[DrawerState] Unknown: \" + state);\n        }\n    }\n\n    /**\n     * Returns the touch mode.\n     */\n    public abstract int getTouchMode();\n\n    /**\n     * Sets the drawer touch mode. Possible values are {@link #TOUCH_MODE_NONE}, {@link #TOUCH_MODE_BEZEL} or\n     * {@link #TOUCH_MODE_FULLSCREEN}.\n     *\n     * @param mode The touch mode.\n     */\n    public abstract void setTouchMode(int mode);\n\n    /**\n     * Sets the size of the touch bezel.\n     *\n     * @param size The touch bezel size in px.\n     */\n    public abstract void setTouchBezelSize(int size);\n\n    /**\n     * Returns the size of the touch bezel in px.\n     */\n    public abstract int getTouchBezelSize();\n\n    @Override\n    public void postOnAnimation(Runnable action) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n            super.postOnAnimation(action);\n        } else {\n            postDelayed(action, ANIMATION_DELAY);\n        }\n    }\n\n    @Override\n    protected boolean fitSystemWindows(Rect insets) {\n        if (mDragMode == MENU_DRAG_WINDOW && mPosition != Position.BOTTOM) {\n            mMenuContainer.setPadding(0, insets.top, 0, 0);\n        }\n        return super.fitSystemWindows(insets);\n    }\n\n    protected void dispatchOnDrawerSlide(float openRatio, int offsetPixels) {\n        if (mOnDrawerStateChangeListener != null) {\n            mOnDrawerStateChangeListener.onDrawerSlide(openRatio, offsetPixels);\n        }\n    }\n\n    /**\n     * Saves the state of the drawer.\n     *\n     * @return Returns a Parcelable containing the drawer state.\n     */\n    public final Parcelable saveState() {\n        if (mState == null) mState = new Bundle();\n        saveState(mState);\n        return mState;\n    }\n\n    void saveState(Bundle state) {\n        // State saving isn't required for subclasses.\n    }\n\n    /**\n     * Restores the state of the drawer.\n     *\n     * @param in A parcelable containing the drawer state.\n     */\n    public void restoreState(Parcelable in) {\n        mState = (Bundle) in;\n    }\n\n    @Override\n    protected Parcelable onSaveInstanceState() {\n        Parcelable superState = super.onSaveInstanceState();\n        SavedState state = new SavedState(superState);\n\n        if (mState == null) mState = new Bundle();\n        saveState(mState);\n\n        state.mState = mState;\n        return state;\n    }\n\n    @Override\n    protected void onRestoreInstanceState(Parcelable state) {\n        SavedState savedState = (SavedState) state;\n        super.onRestoreInstanceState(savedState.getSuperState());\n\n        restoreState(savedState.mState);\n    }\n\n    static class SavedState extends BaseSavedState {\n\n        Bundle mState;\n\n        public SavedState(Parcelable superState) {\n            super(superState);\n        }\n\n        public SavedState(Parcel in) {\n            super(in);\n            mState = in.readBundle();\n        }\n\n        @Override\n        public void writeToParcel(Parcel dest, int flags) {\n            super.writeToParcel(dest, flags);\n            dest.writeBundle(mState);\n        }\n\n        @SuppressWarnings(\"UnusedDeclaration\")\n        public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {\n            @Override\n            public SavedState createFromParcel(Parcel in) {\n                return new SavedState(in);\n            }\n\n            @Override\n            public SavedState[] newArray(int size) {\n                return new SavedState[size];\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/net/simonvt/menudrawer/NoClickThroughFrameLayout.java",
    "content": "package net.simonvt.menudrawer;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\n\n/**\n * FrameLayout which doesn't let touch events propagate to views positioned behind it in the view hierarchy.\n */\nclass NoClickThroughFrameLayout extends BuildLayerFrameLayout {\n\n    public NoClickThroughFrameLayout(Context context) {\n        super(context);\n    }\n\n    public NoClickThroughFrameLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public NoClickThroughFrameLayout(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        return true;\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/net/simonvt/menudrawer/OverlayDrawer.java",
    "content": "package net.simonvt.menudrawer;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.drawable.GradientDrawable;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.VelocityTracker;\nimport android.view.View;\n\npublic class OverlayDrawer extends DraggableDrawer {\n\n    private static final String TAG = \"OverlayDrawer\";\n\n    private int mPeekSize;\n\n    private Runnable mRevealRunnable = new Runnable() {\n        @Override\n        public void run() {\n            cancelContentTouch();\n            int animateTo = 0;\n            switch (getPosition()) {\n                case RIGHT:\n                case BOTTOM:\n                    animateTo = -mPeekSize;\n                    break;\n\n                default:\n                    animateTo = mPeekSize;\n                    break;\n            }\n            animateOffsetTo(animateTo, 250);\n        }\n    };\n\n    OverlayDrawer(Activity activity, int dragMode) {\n        super(activity, dragMode);\n    }\n\n    public OverlayDrawer(Context context) {\n        super(context);\n    }\n\n    public OverlayDrawer(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public OverlayDrawer(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n    }\n\n    @Override\n    protected void initDrawer(Context context, AttributeSet attrs, int defStyle) {\n        super.initDrawer(context, attrs, defStyle);\n        super.addView(mContentContainer, -1, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));\n        if (USE_TRANSLATIONS) {\n            mContentContainer.setLayerType(View.LAYER_TYPE_NONE, null);\n        }\n        mContentContainer.setHardwareLayersEnabled(false);\n        super.addView(mMenuContainer, -1, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));\n        mPeekSize = dpToPx(20);\n    }\n\n    @Override\n    protected void drawOverlay(Canvas canvas) {\n        final int width = getWidth();\n        final int height = getHeight();\n        final int offsetPixels = (int) mOffsetPixels;\n        final float openRatio = Math.abs(mOffsetPixels) / mMenuSize;\n\n        switch (getPosition()) {\n            case LEFT:\n                mMenuOverlay.setBounds(offsetPixels, 0, width, height);\n                break;\n\n            case RIGHT:\n                mMenuOverlay.setBounds(0, 0, width + offsetPixels, height);\n                break;\n\n            case TOP:\n                mMenuOverlay.setBounds(0, offsetPixels, width, height);\n                break;\n\n            case BOTTOM:\n                mMenuOverlay.setBounds(0, 0, width, height + offsetPixels);\n                break;\n        }\n\n        mMenuOverlay.setAlpha((int) (MAX_MENU_OVERLAY_ALPHA * openRatio));\n        mMenuOverlay.draw(canvas);\n    }\n\n    @Override\n    public void openMenu(boolean animate) {\n        int animateTo = 0;\n        switch (getPosition()) {\n            case LEFT:\n            case TOP:\n                animateTo = mMenuSize;\n                break;\n\n            case RIGHT:\n            case BOTTOM:\n                animateTo = -mMenuSize;\n                break;\n        }\n\n        animateOffsetTo(animateTo, 0, animate);\n    }\n\n    @Override\n    public void closeMenu(boolean animate) {\n        animateOffsetTo(0, 0, animate);\n    }\n\n    @Override\n    protected void onOffsetPixelsChanged(int offsetPixels) {\n        if (USE_TRANSLATIONS) {\n            switch (getPosition()) {\n                case LEFT:\n                    mMenuContainer.setTranslationX(offsetPixels - mMenuSize);\n                    break;\n\n                case TOP:\n                    mMenuContainer.setTranslationY(offsetPixels - mMenuSize);\n                    break;\n\n                case RIGHT:\n                    mMenuContainer.setTranslationX(offsetPixels + mMenuSize);\n                    break;\n\n                case BOTTOM:\n                    mMenuContainer.setTranslationY(offsetPixels + mMenuSize);\n                    break;\n            }\n        } else {\n            switch (getPosition()) {\n                case TOP:\n                    mMenuContainer.offsetTopAndBottom(offsetPixels - mMenuContainer.getBottom());\n                    break;\n\n                case BOTTOM:\n                    mMenuContainer.offsetTopAndBottom(offsetPixels - (mMenuContainer.getTop() - getHeight()));\n                    break;\n\n                case LEFT:\n                    mMenuContainer.offsetLeftAndRight(offsetPixels - mMenuContainer.getRight());\n                    break;\n\n                case RIGHT:\n                    mMenuContainer.offsetLeftAndRight(offsetPixels - (mMenuContainer.getLeft() - getWidth()));\n                    break;\n            }\n        }\n\n        invalidate();\n    }\n\n    @Override\n    protected void initPeekScroller() {\n        switch (getPosition()) {\n            case RIGHT:\n            case BOTTOM: {\n                final int dx = -mPeekSize;\n                mPeekScroller.startScroll(0, 0, dx, 0, PEEK_DURATION);\n                break;\n            }\n\n            default: {\n                final int dx = mPeekSize;\n                mPeekScroller.startScroll(0, 0, dx, 0, PEEK_DURATION);\n                break;\n            }\n        }\n    }\n\n    @Override\n    protected void onSizeChanged(int w, int h, int oldw, int oldh) {\n        super.onSizeChanged(w, h, oldw, oldh);\n        onOffsetPixelsChanged((int) mOffsetPixels);\n    }\n\n    @Override\n    protected GradientDrawable.Orientation getDropShadowOrientation() {\n        switch (getPosition()) {\n            case TOP:\n                return GradientDrawable.Orientation.TOP_BOTTOM;\n\n            case RIGHT:\n                return GradientDrawable.Orientation.RIGHT_LEFT;\n\n            case BOTTOM:\n                return GradientDrawable.Orientation.BOTTOM_TOP;\n\n            default:\n                return GradientDrawable.Orientation.LEFT_RIGHT;\n        }\n    }\n\n    @Override\n    protected void updateDropShadowRect() {\n        final float openRatio = Math.abs(mOffsetPixels) / mMenuSize;\n        final int dropShadowSize = (int) (mDropShadowSize * openRatio);\n\n        switch (getPosition()) {\n            case LEFT:\n                mDropShadowRect.top = 0;\n                mDropShadowRect.bottom = getHeight();\n                mDropShadowRect.left = ViewHelper.getRight(mMenuContainer);\n                mDropShadowRect.right = mDropShadowRect.left + dropShadowSize;\n                break;\n\n            case TOP:\n                mDropShadowRect.left = 0;\n                mDropShadowRect.right = getWidth();\n                mDropShadowRect.top = ViewHelper.getBottom(mMenuContainer);\n                mDropShadowRect.bottom = mDropShadowRect.top + dropShadowSize;\n                break;\n\n            case RIGHT:\n                mDropShadowRect.top = 0;\n                mDropShadowRect.bottom = getHeight();\n                mDropShadowRect.right = ViewHelper.getLeft(mMenuContainer);\n                mDropShadowRect.left = mDropShadowRect.right - dropShadowSize;\n                break;\n\n            case BOTTOM:\n                mDropShadowRect.left = 0;\n                mDropShadowRect.right = getWidth();\n                mDropShadowRect.bottom = ViewHelper.getTop(mMenuContainer);\n                mDropShadowRect.top = mDropShadowRect.bottom - dropShadowSize;\n                break;\n        }\n    }\n\n    @Override\n    protected void startLayerTranslation() {\n        if (USE_TRANSLATIONS && mHardwareLayersEnabled && !mLayerTypeHardware) {\n            mLayerTypeHardware = true;\n            mMenuContainer.setLayerType(View.LAYER_TYPE_HARDWARE, null);\n        }\n    }\n\n    @Override\n    protected void stopLayerTranslation() {\n        if (mLayerTypeHardware) {\n            mLayerTypeHardware = false;\n            mMenuContainer.setLayerType(View.LAYER_TYPE_NONE, null);\n        }\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int l, int t, int r, int b) {\n        final int width = r - l;\n        final int height = b - t;\n\n        mContentContainer.layout(0, 0, width, height);\n\n        if (USE_TRANSLATIONS) {\n            switch (getPosition()) {\n                case LEFT:\n                    mMenuContainer.layout(0, 0, mMenuSize, height);\n                    break;\n\n                case RIGHT:\n                    mMenuContainer.layout(width - mMenuSize, 0, width, height);\n                    break;\n\n                case TOP:\n                    mMenuContainer.layout(0, 0, width, mMenuSize);\n                    break;\n\n                case BOTTOM:\n                    mMenuContainer.layout(0, height - mMenuSize, width, height);\n                    break;\n            }\n\n        } else {\n            final int offsetPixels = (int) mOffsetPixels;\n            final int menuSize = mMenuSize;\n\n            switch (getPosition()) {\n                case LEFT:\n                    mMenuContainer.layout(-menuSize + offsetPixels, 0, offsetPixels, height);\n                    break;\n\n                case RIGHT:\n                    mMenuContainer.layout(width + offsetPixels, 0, width + menuSize + offsetPixels, height);\n                    break;\n\n                case TOP:\n                    mMenuContainer.layout(0, -menuSize + offsetPixels, width, offsetPixels);\n                    break;\n\n                case BOTTOM:\n                    mMenuContainer.layout(0, height + offsetPixels, width, height + menuSize + offsetPixels);\n                    break;\n            }\n        }\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);\n        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);\n\n        if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) {\n            throw new IllegalStateException(\"Must measure with an exact size\");\n        }\n\n        final int width = MeasureSpec.getSize(widthMeasureSpec);\n        final int height = MeasureSpec.getSize(heightMeasureSpec);\n\n        if (mOffsetPixels == -1) openMenu(false);\n\n        int menuWidthMeasureSpec;\n        int menuHeightMeasureSpec;\n        switch (getPosition()) {\n            case TOP:\n            case BOTTOM:\n                menuWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, width);\n                menuHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 0, mMenuSize);\n                break;\n\n            default:\n                // LEFT/RIGHT\n                menuWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, mMenuSize);\n                menuHeightMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, height);\n        }\n        mMenuContainer.measure(menuWidthMeasureSpec, menuHeightMeasureSpec);\n\n        final int contentWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, width);\n        final int contentHeightMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, height);\n        mContentContainer.measure(contentWidthMeasureSpec, contentHeightMeasureSpec);\n\n        setMeasuredDimension(width, height);\n\n        updateTouchAreaSize();\n    }\n\n    private boolean isContentTouch(int x, int y) {\n        boolean contentTouch = false;\n\n        switch (getPosition()) {\n            case LEFT:\n                contentTouch = ViewHelper.getRight(mMenuContainer) < x;\n                break;\n\n            case RIGHT:\n                contentTouch = ViewHelper.getLeft(mMenuContainer) > x;\n                break;\n\n            case TOP:\n                contentTouch = ViewHelper.getBottom(mMenuContainer) < y;\n                break;\n\n            case BOTTOM:\n                contentTouch = ViewHelper.getTop(mMenuContainer) > y;\n                break;\n        }\n\n        return contentTouch;\n    }\n\n    protected boolean onDownAllowDrag(int x, int y) {\n        switch (getPosition()) {\n            case LEFT:\n                return (!mMenuVisible && mInitialMotionX <= mTouchSize)\n                        || (mMenuVisible && mInitialMotionX <= mOffsetPixels);\n\n            case RIGHT:\n                final int width = getWidth();\n                final int initialMotionX = (int) mInitialMotionX;\n\n                return (!mMenuVisible && initialMotionX >= width - mTouchSize)\n                        || (mMenuVisible && initialMotionX >= width + mOffsetPixels);\n\n            case TOP:\n                return (!mMenuVisible && mInitialMotionY <= mTouchSize)\n                        || (mMenuVisible && mInitialMotionY <= mOffsetPixels);\n\n            case BOTTOM:\n                final int height = getHeight();\n                return (!mMenuVisible && mInitialMotionY >= height - mTouchSize)\n                        || (mMenuVisible && mInitialMotionY >= height + mOffsetPixels);\n        }\n\n        return false;\n    }\n\n    protected boolean onMoveAllowDrag(int x, int y, float dx, float dy) {\n        if (mMenuVisible && mTouchMode == TOUCH_MODE_FULLSCREEN) {\n            return true;\n        }\n\n        switch (getPosition()) {\n            case LEFT:\n                return (!mMenuVisible && mInitialMotionX <= mTouchSize && (dx > 0)) // Drawer closed\n                        || (mMenuVisible && x <= mOffsetPixels) // Drawer open\n                        || (Math.abs(mOffsetPixels) <= mPeekSize && mMenuVisible); // Drawer revealed\n\n            case RIGHT:\n                final int width = getWidth();\n                return (!mMenuVisible && mInitialMotionX >= width - mTouchSize && (dx < 0))\n                        || (mMenuVisible && x >= width - mOffsetPixels)\n                        || (Math.abs(mOffsetPixels) <= mPeekSize && mMenuVisible);\n\n            case TOP:\n                return (!mMenuVisible && mInitialMotionY <= mTouchSize && (dy > 0))\n                        || (mMenuVisible && x <= mOffsetPixels)\n                        || (Math.abs(mOffsetPixels) <= mPeekSize && mMenuVisible);\n\n            case BOTTOM:\n                final int height = getHeight();\n                return (!mMenuVisible && mInitialMotionY >= height - mTouchSize && (dy < 0))\n                        || (mMenuVisible && x >= height - mOffsetPixels)\n                        || (Math.abs(mOffsetPixels) <= mPeekSize && mMenuVisible);\n        }\n\n        return false;\n    }\n\n    protected void onMoveEvent(float dx, float dy) {\n        switch (getPosition()) {\n            case LEFT:\n                setOffsetPixels(Math.min(Math.max(mOffsetPixels + dx, 0), mMenuSize));\n                break;\n\n            case RIGHT:\n                setOffsetPixels(Math.max(Math.min(mOffsetPixels + dx, 0), -mMenuSize));\n                break;\n\n            case TOP:\n                setOffsetPixels(Math.min(Math.max(mOffsetPixels + dy, 0), mMenuSize));\n                break;\n\n            case BOTTOM:\n                setOffsetPixels(Math.max(Math.min(mOffsetPixels + dy, 0), -mMenuSize));\n                break;\n        }\n    }\n\n    protected void onUpEvent(int x, int y) {\n        final int offsetPixels = (int) mOffsetPixels;\n\n        switch (getPosition()) {\n            case LEFT: {\n                if (mIsDragging) {\n                    mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);\n                    final int initialVelocity = (int) getXVelocity(mVelocityTracker);\n                    mLastMotionX = x;\n                    animateOffsetTo(initialVelocity > 0 ? mMenuSize : 0, initialVelocity, true);\n\n                    // Close the menu when content is clicked while the menu is visible.\n                } else if (mMenuVisible) {\n                    closeMenu();\n                }\n                break;\n            }\n\n            case TOP: {\n                if (mIsDragging) {\n                    mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);\n                    final int initialVelocity = (int) getYVelocity(mVelocityTracker);\n                    mLastMotionY = y;\n                    animateOffsetTo(initialVelocity > 0 ? mMenuSize : 0, initialVelocity, true);\n\n                    // Close the menu when content is clicked while the menu is visible.\n                } else if (mMenuVisible) {\n                    closeMenu();\n                }\n                break;\n            }\n\n            case RIGHT: {\n                final int width = getWidth();\n\n                if (mIsDragging) {\n                    mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);\n                    final int initialVelocity = (int) getXVelocity(mVelocityTracker);\n                    mLastMotionX = x;\n                    animateOffsetTo(initialVelocity > 0 ? 0 : -mMenuSize, initialVelocity, true);\n\n                    // Close the menu when content is clicked while the menu is visible.\n                } else if (mMenuVisible) {\n                    closeMenu();\n                }\n                break;\n            }\n\n            case BOTTOM: {\n                if (mIsDragging) {\n                    mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);\n                    final int initialVelocity = (int) getYVelocity(mVelocityTracker);\n                    mLastMotionY = y;\n                    animateOffsetTo(initialVelocity < 0 ? -mMenuSize : 0, initialVelocity, true);\n\n                    // Close the menu when content is clicked while the menu is visible.\n                } else if (mMenuVisible) {\n                    closeMenu();\n                }\n                break;\n            }\n        }\n    }\n\n    protected boolean checkTouchSlop(float dx, float dy) {\n        switch (getPosition()) {\n            case TOP:\n            case BOTTOM:\n                return Math.abs(dy) > mTouchSlop && Math.abs(dy) > Math.abs(dx);\n\n            default:\n                return Math.abs(dx) > mTouchSlop && Math.abs(dx) > Math.abs(dy);\n        }\n    }\n\n    @Override\n    protected void stopAnimation() {\n        super.stopAnimation();\n        removeCallbacks(mRevealRunnable);\n    }\n\n    @Override\n    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {\n        super.requestDisallowInterceptTouchEvent(disallowIntercept);\n        removeCallbacks(mRevealRunnable);\n        if (mIsPeeking) {\n            endPeek();\n            animateOffsetTo(0, PEEK_DURATION);\n        }\n    }\n\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        final int action = ev.getAction() & MotionEvent.ACTION_MASK;\n\n        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {\n            removeCallbacks(mRevealRunnable);\n            mActivePointerId = INVALID_POINTER;\n            mIsDragging = false;\n            if (mVelocityTracker != null) {\n                mVelocityTracker.recycle();\n                mVelocityTracker = null;\n            }\n\n            if (Math.abs(mOffsetPixels) > mMenuSize / 2) {\n                openMenu();\n            } else {\n                closeMenu();\n            }\n\n            return false;\n        }\n\n        if (action == MotionEvent.ACTION_DOWN && mMenuVisible && isCloseEnough()) {\n            setOffsetPixels(0);\n            stopAnimation();\n            endPeek();\n            setDrawerState(STATE_CLOSED);\n            mIsDragging = false;\n        }\n\n        // Always intercept events over the content while menu is visible.\n        if (mMenuVisible) {\n            int index = 0;\n            if (mActivePointerId != INVALID_POINTER) {\n                index = ev.findPointerIndex(mActivePointerId);\n                index = index == -1 ? 0 : index;\n            }\n\n            final int x = (int) ev.getX(index);\n            final int y = (int) ev.getY(index);\n            if (isContentTouch(x, y)) {\n                return true;\n            }\n        }\n\n        if (!mMenuVisible && !mIsDragging && mTouchMode == TOUCH_MODE_NONE) {\n            return false;\n        }\n\n        if (action != MotionEvent.ACTION_DOWN && mIsDragging) {\n            return true;\n        }\n\n        switch (action) {\n            case MotionEvent.ACTION_DOWN: {\n                mLastMotionX = mInitialMotionX = ev.getX();\n                mLastMotionY = mInitialMotionY = ev.getY();\n                final boolean allowDrag = onDownAllowDrag((int) mLastMotionX, (int) mLastMotionY);\n                mActivePointerId = ev.getPointerId(0);\n\n                if (allowDrag) {\n                    setDrawerState(mMenuVisible ? STATE_OPEN : STATE_CLOSED);\n                    stopAnimation();\n                    endPeek();\n\n                    if (!mMenuVisible && mInitialMotionX <= mPeekSize) {\n                        postDelayed(mRevealRunnable, 160);\n                    }\n\n                    mIsDragging = false;\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_MOVE: {\n                final int activePointerId = mActivePointerId;\n                if (activePointerId == INVALID_POINTER) {\n                    // If we don't have a valid id, the touch down wasn't on content.\n                    break;\n                }\n\n                final int pointerIndex = ev.findPointerIndex(activePointerId);\n                if (pointerIndex == -1) {\n                    mIsDragging = false;\n                    mActivePointerId = INVALID_POINTER;\n                    endDrag();\n                    closeMenu(true);\n                    return false;\n                }\n\n                final float x = ev.getX(pointerIndex);\n                final float dx = x - mLastMotionX;\n                final float y = ev.getY(pointerIndex);\n                final float dy = y - mLastMotionY;\n\n                if (Math.abs(dx) >= mTouchSlop || Math.abs(dy) >= mTouchSlop) {\n                    removeCallbacks(mRevealRunnable);\n                    endPeek();\n                }\n\n                if (checkTouchSlop(dx, dy)) {\n                    if (mOnInterceptMoveEventListener != null && (mTouchMode == TOUCH_MODE_FULLSCREEN || mMenuVisible)\n                            && canChildrenScroll((int) dx, (int) dy, (int) x, (int) y)) {\n                        endDrag(); // Release the velocity tracker\n                        requestDisallowInterceptTouchEvent(true);\n                        return false;\n                    }\n\n                    final boolean allowDrag = onMoveAllowDrag((int) x, (int) y, dx, dy);\n\n                    if (allowDrag) {\n                        endPeek();\n                        stopAnimation();\n                        setDrawerState(STATE_DRAGGING);\n                        mIsDragging = true;\n                        mLastMotionX = x;\n                        mLastMotionY = y;\n                    }\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_POINTER_UP:\n                onPointerUp(ev);\n                mLastMotionX = ev.getX(ev.findPointerIndex(mActivePointerId));\n                mLastMotionY = ev.getY(ev.findPointerIndex(mActivePointerId));\n                break;\n        }\n\n        if (mVelocityTracker == null) mVelocityTracker = VelocityTracker.obtain();\n        mVelocityTracker.addMovement(ev);\n\n        return mIsDragging;\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent ev) {\n        if (!mMenuVisible && !mIsDragging && mTouchMode == TOUCH_MODE_NONE) {\n            return false;\n        }\n        final int action = ev.getAction() & MotionEvent.ACTION_MASK;\n\n        if (mVelocityTracker == null) mVelocityTracker = VelocityTracker.obtain();\n        mVelocityTracker.addMovement(ev);\n\n        switch (action) {\n            case MotionEvent.ACTION_DOWN: {\n                mLastMotionX = mInitialMotionX = ev.getX();\n                mLastMotionY = mInitialMotionY = ev.getY();\n                final boolean allowDrag = onDownAllowDrag((int) mLastMotionX, (int) mLastMotionY);\n\n                mActivePointerId = ev.getPointerId(0);\n\n                if (allowDrag) {\n                    stopAnimation();\n                    endPeek();\n\n                    if (!mMenuVisible && mLastMotionX <= mPeekSize) {\n                        postDelayed(mRevealRunnable, 160);\n                    }\n\n                    startLayerTranslation();\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_MOVE: {\n                final int pointerIndex = ev.findPointerIndex(mActivePointerId);\n                if (pointerIndex == -1) {\n                    mIsDragging = false;\n                    mActivePointerId = INVALID_POINTER;\n                    endDrag();\n                    closeMenu(true);\n                    return false;\n                }\n\n                if (!mIsDragging) {\n                    final float x = ev.getX(pointerIndex);\n                    final float dx = x - mLastMotionX;\n                    final float y = ev.getY(pointerIndex);\n                    final float dy = y - mLastMotionY;\n\n                    if (checkTouchSlop(dx, dy)) {\n                        final boolean allowDrag = onMoveAllowDrag((int) x, (int) y, dx, dy);\n\n                        if (allowDrag) {\n                            endPeek();\n                            stopAnimation();\n                            setDrawerState(STATE_DRAGGING);\n                            mIsDragging = true;\n                            mLastMotionX = x;\n                            mLastMotionY = y;\n                        } else {\n                            mInitialMotionX = x;\n                            mInitialMotionY = y;\n                        }\n                    }\n                }\n\n                if (mIsDragging) {\n                    startLayerTranslation();\n\n                    final float x = ev.getX(pointerIndex);\n                    final float dx = x - mLastMotionX;\n                    final float y = ev.getY(pointerIndex);\n                    final float dy = y - mLastMotionY;\n\n                    mLastMotionX = x;\n                    mLastMotionY = y;\n                    onMoveEvent(dx, dy);\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_CANCEL:\n            case MotionEvent.ACTION_UP: {\n                removeCallbacks(mRevealRunnable);\n                int index = ev.findPointerIndex(mActivePointerId);\n                index = index == -1 ? 0 : index;\n                final int x = (int) ev.getX(index);\n                final int y = (int) ev.getY(index);\n                onUpEvent(x, y);\n                mActivePointerId = INVALID_POINTER;\n                mIsDragging = false;\n                break;\n            }\n\n            case MotionEvent.ACTION_POINTER_DOWN:\n                final int index = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)\n                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;\n                mLastMotionX = ev.getX(index);\n                mLastMotionY = ev.getY(index);\n                mActivePointerId = ev.getPointerId(index);\n                break;\n\n            case MotionEvent.ACTION_POINTER_UP:\n                onPointerUp(ev);\n                mLastMotionX = ev.getX(ev.findPointerIndex(mActivePointerId));\n                mLastMotionY = ev.getY(ev.findPointerIndex(mActivePointerId));\n                break;\n        }\n\n        return true;\n    }\n\n    private void onPointerUp(MotionEvent ev) {\n        final int pointerIndex = ev.getActionIndex();\n        final int pointerId = ev.getPointerId(pointerIndex);\n        if (pointerId == mActivePointerId) {\n            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;\n            mLastMotionX = ev.getX(newPointerIndex);\n            mActivePointerId = ev.getPointerId(newPointerIndex);\n            if (mVelocityTracker != null) {\n                mVelocityTracker.clear();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/net/simonvt/menudrawer/PeekInterpolator.java",
    "content": "package net.simonvt.menudrawer;\n\nimport android.view.animation.Interpolator;\n\nclass PeekInterpolator implements Interpolator {\n\n    private static final String TAG = \"PeekInterpolator\";\n\n    private static final SinusoidalInterpolator SINUSOIDAL_INTERPOLATOR = new SinusoidalInterpolator();\n\n    @Override\n    public float getInterpolation(float input) {\n        float result;\n\n        if (input < 1.f / 3.f) {\n            result = SINUSOIDAL_INTERPOLATOR.getInterpolation(input * 3);\n\n        } else if (input > 2.f / 3.f) {\n            final float val = ((input + 1.f / 3.f) - 1.f) * 3.f;\n            result = 1.f - SINUSOIDAL_INTERPOLATOR.getInterpolation(val);\n\n        } else {\n            result = 1.f;\n        }\n\n        return result;\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/net/simonvt/menudrawer/Position.java",
    "content": "package net.simonvt.menudrawer;\n\nimport android.util.SparseArray;\n\n/**\n * Enums used for positioning the drawer.\n */\npublic enum Position {\n    // Positions the drawer to the left of the content.\n    LEFT(0),\n\n    // Positions the drawer above the content.\n    TOP(1),\n\n    // Positions the drawer to the right of the content.\n    RIGHT(2),\n\n    // Positions the drawer below the content.\n    BOTTOM(3),\n\n    /**\n     * Position the drawer at the start edge. This will position the drawer to the {@link #LEFT} with LTR languages and\n     * {@link #RIGHT} with RTL languages.\n     */\n    START(4),\n\n    /**\n     * Position the drawer at the end edge. This will position the drawer to the {@link #RIGHT} with LTR languages and\n     * {@link #LEFT} with RTL languages.\n     */\n    END(5);\n\n    final int mValue;\n\n    Position(int value) {\n        mValue = value;\n    }\n\n    private static final SparseArray<Position> STRING_MAPPING = new SparseArray<Position>();\n\n    static {\n        for (Position via : Position.values()) {\n            STRING_MAPPING.put(via.mValue, via);\n        }\n    }\n\n    public static Position fromValue(int value) {\n        return STRING_MAPPING.get(value);\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/net/simonvt/menudrawer/Scroller.java",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage net.simonvt.menudrawer;\n\nimport android.content.Context;\nimport android.hardware.SensorManager;\nimport android.os.Build;\nimport android.util.FloatMath;\nimport android.view.ViewConfiguration;\nimport android.view.animation.AnimationUtils;\nimport android.view.animation.Interpolator;\n\n\n/**\n * This class encapsulates scrolling.  The duration of the scroll\n * can be passed in the constructor and specifies the maximum time that\n * the scrolling animation should take.  Past this time, the scrolling is\n * automatically moved to its final stage and computeScrollOffset()\n * will always return false to indicate that scrolling is over.\n */\nclass Scroller  {\n    private int mMode;\n\n    private int mStartX;\n    private int mStartY;\n    private int mFinalX;\n    private int mFinalY;\n\n    private int mMinX;\n    private int mMaxX;\n    private int mMinY;\n    private int mMaxY;\n\n    private int mCurrX;\n    private int mCurrY;\n    private long mStartTime;\n    private int mDuration;\n    private float mDurationReciprocal;\n    private float mDeltaX;\n    private float mDeltaY;\n    private boolean mFinished;\n    private Interpolator mInterpolator;\n    private boolean mFlywheel;\n\n    private float mVelocity;\n\n    private static final int DEFAULT_DURATION = 250;\n    private static final int SCROLL_MODE = 0;\n    private static final int FLING_MODE = 1;\n\n    private static final float DECELERATION_RATE = (float) (Math.log(0.75) / Math.log(0.9));\n    private static final float ALPHA = 800; // pixels / seconds\n    private static final float START_TENSION = 0.4f; // Tension at start: (0.4 * total T, 1.0 * Distance)\n    private static final float END_TENSION = 1.0f - START_TENSION;\n    private static final int NB_SAMPLES = 100;\n    private static final float[] SPLINE = new float[NB_SAMPLES + 1];\n\n    private float mDeceleration;\n    private final float mPpi;\n\n    static {\n        float xMin = 0.0f;\n        for (int i = 0; i <= NB_SAMPLES; i++) {\n            final float t = (float) i / NB_SAMPLES;\n            float xMax = 1.0f;\n            float x, tx, coef;\n            while (true) {\n                x = xMin + (xMax - xMin) / 2.0f;\n                coef = 3.0f * x * (1.0f - x);\n                tx = coef * ((1.0f - x) * START_TENSION + x * END_TENSION) + x * x * x;\n                if (Math.abs(tx - t) < 1E-5) break;\n                if (tx > t) xMax = x;\n                else xMin = x;\n            }\n            final float d = coef + x * x * x;\n            SPLINE[i] = d;\n        }\n        SPLINE[NB_SAMPLES] = 1.0f;\n\n        // This controls the viscous fluid effect (how much of it)\n        sViscousFluidScale = 8.0f;\n        // must be set to 1.0 (used in viscousFluid())\n        sViscousFluidNormalize = 1.0f;\n        sViscousFluidNormalize = 1.0f / viscousFluid(1.0f);\n    }\n\n    private static float sViscousFluidScale;\n    private static float sViscousFluidNormalize;\n\n    /**\n     * Create a Scroller with the default duration and interpolator.\n     */\n    public Scroller(Context context) {\n        this(context, null);\n    }\n\n    /**\n     * Create a Scroller with the specified interpolator. If the interpolator is\n     * null, the default (viscous) interpolator will be used. \"Flywheel\" behavior will\n     * be in effect for apps targeting Honeycomb or newer.\n     */\n    public Scroller(Context context, Interpolator interpolator) {\n        this(context, interpolator,\n                context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB);\n    }\n\n    /**\n     * Create a Scroller with the specified interpolator. If the interpolator is\n     * null, the default (viscous) interpolator will be used. Specify whether or\n     * not to support progressive \"flywheel\" behavior in flinging.\n     */\n    public Scroller(Context context, Interpolator interpolator, boolean flywheel) {\n        mFinished = true;\n        mInterpolator = interpolator;\n        mPpi = context.getResources().getDisplayMetrics().density * 160.0f;\n        mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());\n        mFlywheel = flywheel;\n    }\n\n    /**\n     * The amount of friction applied to flings. The default value\n     * is {@link android.view.ViewConfiguration#getScrollFriction}.\n     *\n     * @param friction A scalar dimension-less value representing the coefficient of\n     *         friction.\n     */\n    public final void setFriction(float friction) {\n        mDeceleration = computeDeceleration(friction);\n    }\n\n    private float computeDeceleration(float friction) {\n        return SensorManager.GRAVITY_EARTH   // g (m/s^2)\n                      * 39.37f               // inch/meter\n                      * mPpi                 // pixels per inch\n                      * friction;\n    }\n\n    /**\n     *\n     * Returns whether the scroller has finished scrolling.\n     *\n     * @return True if the scroller has finished scrolling, false otherwise.\n     */\n    public final boolean isFinished() {\n        return mFinished;\n    }\n\n    /**\n     * Force the finished field to a particular value.\n     *\n     * @param finished The new finished value.\n     */\n    public final void forceFinished(boolean finished) {\n        mFinished = finished;\n    }\n\n    /**\n     * Returns how long the scroll event will take, in milliseconds.\n     *\n     * @return The duration of the scroll in milliseconds.\n     */\n    public final int getDuration() {\n        return mDuration;\n    }\n\n    /**\n     * Returns the current X offset in the scroll.\n     *\n     * @return The new X offset as an absolute distance from the origin.\n     */\n    public final int getCurrX() {\n        return mCurrX;\n    }\n\n    /**\n     * Returns the current Y offset in the scroll.\n     *\n     * @return The new Y offset as an absolute distance from the origin.\n     */\n    public final int getCurrY() {\n        return mCurrY;\n    }\n\n    /**\n     * Returns the current velocity.\n     *\n     * @return The original velocity less the deceleration. Result may be\n     * negative.\n     */\n    public float getCurrVelocity() {\n        return mVelocity - mDeceleration * timePassed() / 2000.0f;\n    }\n\n    /**\n     * Returns the start X offset in the scroll.\n     *\n     * @return The start X offset as an absolute distance from the origin.\n     */\n    public final int getStartX() {\n        return mStartX;\n    }\n\n    /**\n     * Returns the start Y offset in the scroll.\n     *\n     * @return The start Y offset as an absolute distance from the origin.\n     */\n    public final int getStartY() {\n        return mStartY;\n    }\n\n    /**\n     * Returns where the scroll will end. Valid only for \"fling\" scrolls.\n     *\n     * @return The final X offset as an absolute distance from the origin.\n     */\n    public final int getFinalX() {\n        return mFinalX;\n    }\n\n    /**\n     * Returns where the scroll will end. Valid only for \"fling\" scrolls.\n     *\n     * @return The final Y offset as an absolute distance from the origin.\n     */\n    public final int getFinalY() {\n        return mFinalY;\n    }\n\n    /**\n     * Call this when you want to know the new location.  If it returns true,\n     * the animation is not yet finished.  loc will be altered to provide the\n     * new location.\n     */\n    public boolean computeScrollOffset() {\n        if (mFinished) {\n            return false;\n        }\n\n        int timePassed = (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime);\n\n        if (timePassed < mDuration) {\n            switch (mMode) {\n            case SCROLL_MODE:\n                float x = timePassed * mDurationReciprocal;\n\n                if (mInterpolator == null)\n                    x = viscousFluid(x);\n                else\n                    x = mInterpolator.getInterpolation(x);\n\n                mCurrX = mStartX + Math.round(x * mDeltaX);\n                mCurrY = mStartY + Math.round(x * mDeltaY);\n                break;\n            case FLING_MODE:\n                final float t = (float) timePassed / mDuration;\n                final int index = (int) (NB_SAMPLES * t);\n                final float tInf = (float) index / NB_SAMPLES;\n                final float tSup = (float) (index + 1) / NB_SAMPLES;\n                final float dInf = SPLINE[index];\n                final float dSup = SPLINE[index + 1];\n                final float distanceCoef = dInf + (t - tInf) / (tSup - tInf) * (dSup - dInf);\n\n                mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));\n                // Pin to mMinX <= mCurrX <= mMaxX\n                mCurrX = Math.min(mCurrX, mMaxX);\n                mCurrX = Math.max(mCurrX, mMinX);\n\n                mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));\n                // Pin to mMinY <= mCurrY <= mMaxY\n                mCurrY = Math.min(mCurrY, mMaxY);\n                mCurrY = Math.max(mCurrY, mMinY);\n\n                if (mCurrX == mFinalX && mCurrY == mFinalY) {\n                    mFinished = true;\n                }\n\n                break;\n            }\n        } else {\n            mCurrX = mFinalX;\n            mCurrY = mFinalY;\n            mFinished = true;\n        }\n        return true;\n    }\n\n    /**\n     * Start scrolling by providing a starting point and the distance to travel.\n     * The scroll will use the default value of 250 milliseconds for the\n     * duration.\n     *\n     * @param startX Starting horizontal scroll offset in pixels. Positive\n     *        numbers will scroll the content to the left.\n     * @param startY Starting vertical scroll offset in pixels. Positive numbers\n     *        will scroll the content up.\n     * @param dx Horizontal distance to travel. Positive numbers will scroll the\n     *        content to the left.\n     * @param dy Vertical distance to travel. Positive numbers will scroll the\n     *        content up.\n     */\n    public void startScroll(int startX, int startY, int dx, int dy) {\n        startScroll(startX, startY, dx, dy, DEFAULT_DURATION);\n    }\n\n    /**\n     * Start scrolling by providing a starting point and the distance to travel.\n     *\n     * @param startX Starting horizontal scroll offset in pixels. Positive\n     *        numbers will scroll the content to the left.\n     * @param startY Starting vertical scroll offset in pixels. Positive numbers\n     *        will scroll the content up.\n     * @param dx Horizontal distance to travel. Positive numbers will scroll the\n     *        content to the left.\n     * @param dy Vertical distance to travel. Positive numbers will scroll the\n     *        content up.\n     * @param duration Duration of the scroll in milliseconds.\n     */\n    public void startScroll(int startX, int startY, int dx, int dy, int duration) {\n        mMode = SCROLL_MODE;\n        mFinished = false;\n        mDuration = duration;\n        mStartTime = AnimationUtils.currentAnimationTimeMillis();\n        mStartX = startX;\n        mStartY = startY;\n        mFinalX = startX + dx;\n        mFinalY = startY + dy;\n        mDeltaX = dx;\n        mDeltaY = dy;\n        mDurationReciprocal = 1.0f / (float) mDuration;\n    }\n\n    /**\n     * Start scrolling based on a fling gesture. The distance travelled will\n     * depend on the initial velocity of the fling.\n     *\n     * @param startX Starting point of the scroll (X)\n     * @param startY Starting point of the scroll (Y)\n     * @param velocityX Initial velocity of the fling (X) measured in pixels per\n     *        second.\n     * @param velocityY Initial velocity of the fling (Y) measured in pixels per\n     *        second\n     * @param minX Minimum X value. The scroller will not scroll past this\n     *        point.\n     * @param maxX Maximum X value. The scroller will not scroll past this\n     *        point.\n     * @param minY Minimum Y value. The scroller will not scroll past this\n     *        point.\n     * @param maxY Maximum Y value. The scroller will not scroll past this\n     *        point.\n     */\n    public void fling(int startX, int startY, int velocityX, int velocityY,\n            int minX, int maxX, int minY, int maxY) {\n        // Continue a scroll or fling in progress\n        if (mFlywheel && !mFinished) {\n            float oldVel = getCurrVelocity();\n\n            float dx = (float) (mFinalX - mStartX);\n            float dy = (float) (mFinalY - mStartY);\n            float hyp = FloatMath.sqrt(dx * dx + dy * dy);\n\n            float ndx = dx / hyp;\n            float ndy = dy / hyp;\n\n            float oldVelocityX = ndx * oldVel;\n            float oldVelocityY = ndy * oldVel;\n            if (Math.signum(velocityX) == Math.signum(oldVelocityX)\n                    && Math.signum(velocityY) == Math.signum(oldVelocityY)) {\n                velocityX += oldVelocityX;\n                velocityY += oldVelocityY;\n            }\n        }\n\n        mMode = FLING_MODE;\n        mFinished = false;\n\n        float velocity = FloatMath.sqrt(velocityX * velocityX + velocityY * velocityY);\n\n        mVelocity = velocity;\n        final double l = Math.log(START_TENSION * velocity / ALPHA);\n        mDuration = (int) (1000.0 * Math.exp(l / (DECELERATION_RATE - 1.0)));\n        mStartTime = AnimationUtils.currentAnimationTimeMillis();\n        mStartX = startX;\n        mStartY = startY;\n\n        float coeffX = velocity == 0 ? 1.0f : velocityX / velocity;\n        float coeffY = velocity == 0 ? 1.0f : velocityY / velocity;\n\n        int totalDistance =\n                (int) (ALPHA * Math.exp(DECELERATION_RATE / (DECELERATION_RATE - 1.0) * l));\n\n        mMinX = minX;\n        mMaxX = maxX;\n        mMinY = minY;\n        mMaxY = maxY;\n\n        mFinalX = startX + Math.round(totalDistance * coeffX);\n        // Pin to mMinX <= mFinalX <= mMaxX\n        mFinalX = Math.min(mFinalX, mMaxX);\n        mFinalX = Math.max(mFinalX, mMinX);\n\n        mFinalY = startY + Math.round(totalDistance * coeffY);\n        // Pin to mMinY <= mFinalY <= mMaxY\n        mFinalY = Math.min(mFinalY, mMaxY);\n        mFinalY = Math.max(mFinalY, mMinY);\n    }\n\n    static float viscousFluid(float x) {\n        x *= sViscousFluidScale;\n        if (x < 1.0f) {\n            x -= (1.0f - (float) Math.exp(-x));\n        } else {\n            float start = 0.36787944117f;   // 1/e == exp(-1)\n            x = 1.0f - (float) Math.exp(1.0f - x);\n            x = start + x * (1.0f - start);\n        }\n        x *= sViscousFluidNormalize;\n        return x;\n    }\n\n    /**\n     * Stops the animation. Contrary to {@link #forceFinished(boolean)},\n     * aborting the animating cause the scroller to move to the final x and y\n     * position\n     *\n     * @see #forceFinished(boolean)\n     */\n    public void abortAnimation() {\n        mCurrX = mFinalX;\n        mCurrY = mFinalY;\n        mFinished = true;\n    }\n\n    /**\n     * Extend the scroll animation. This allows a running animation to scroll\n     * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.\n     *\n     * @param extend Additional time to scroll in milliseconds.\n     * @see #setFinalX(int)\n     * @see #setFinalY(int)\n     */\n    public void extendDuration(int extend) {\n        int passed = timePassed();\n        mDuration = passed + extend;\n        mDurationReciprocal = 1.0f / mDuration;\n        mFinished = false;\n    }\n\n    /**\n     * Returns the time elapsed since the beginning of the scrolling.\n     *\n     * @return The elapsed time in milliseconds.\n     */\n    public int timePassed() {\n        return (int) (AnimationUtils.currentAnimationTimeMillis() - mStartTime);\n    }\n\n    /**\n     * Sets the final position (X) for this scroller.\n     *\n     * @param newX The new X offset as an absolute distance from the origin.\n     * @see #extendDuration(int)\n     * @see #setFinalY(int)\n     */\n    public void setFinalX(int newX) {\n        mFinalX = newX;\n        mDeltaX = mFinalX - mStartX;\n        mFinished = false;\n    }\n\n    /**\n     * Sets the final position (Y) for this scroller.\n     *\n     * @param newY The new Y offset as an absolute distance from the origin.\n     * @see #extendDuration(int)\n     * @see #setFinalX(int)\n     */\n    public void setFinalY(int newY) {\n        mFinalY = newY;\n        mDeltaY = mFinalY - mStartY;\n        mFinished = false;\n    }\n\n    /**\n     * @hide\n     */\n    public boolean isScrollingInDirection(float xvel, float yvel) {\n        return !mFinished && Math.signum(xvel) == Math.signum(mFinalX - mStartX)\n                && Math.signum(yvel) == Math.signum(mFinalY - mStartY);\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/net/simonvt/menudrawer/SinusoidalInterpolator.java",
    "content": "package net.simonvt.menudrawer;\n\nimport android.view.animation.Interpolator;\n\n/**\n * Interpolator which, when drawn from 0 to 1, looks like half a sine-wave. Used for smoother opening/closing when\n * peeking at the drawer.\n */\nclass SinusoidalInterpolator implements Interpolator {\n\n    @Override\n    public float getInterpolation(float input) {\n        return (float) (0.5f + 0.5f * Math.sin(input * Math.PI - Math.PI / 2.f));\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/net/simonvt/menudrawer/SlideDrawable.java",
    "content": "package net.simonvt.menudrawer;\n\nimport android.graphics.Canvas;\nimport android.graphics.ColorFilter;\nimport android.graphics.PorterDuff;\nimport android.graphics.Rect;\nimport android.graphics.Region;\nimport android.graphics.drawable.Drawable;\n\npublic class SlideDrawable extends Drawable implements Drawable.Callback {\n\n    private Drawable mWrapped;\n    private float mOffset;\n\n    private final Rect mTmpRect = new Rect();\n\n    private boolean mIsRtl;\n\n    public SlideDrawable(Drawable wrapped) {\n        mWrapped = wrapped;\n    }\n\n    public void setOffset(float offset) {\n        mOffset = offset;\n        invalidateSelf();\n    }\n\n    public float getOffset() {\n        return mOffset;\n    }\n\n    void setIsRtl(boolean isRtl) {\n        mIsRtl = isRtl;\n        invalidateSelf();\n    }\n\n    @Override\n    public void draw(Canvas canvas) {\n        mWrapped.copyBounds(mTmpRect);\n        canvas.save();\n        if (mIsRtl) {\n            canvas.translate(1.f / 3 * mTmpRect.width() * mOffset, 0);\n        } else {\n            canvas.translate(1.f / 3 * mTmpRect.width() * -mOffset, 0);\n        }\n        mWrapped.draw(canvas);\n        canvas.restore();\n    }\n\n    @Override\n    public void setChangingConfigurations(int configs) {\n        mWrapped.setChangingConfigurations(configs);\n    }\n\n    @Override\n    public int getChangingConfigurations() {\n        return mWrapped.getChangingConfigurations();\n    }\n\n    @Override\n    public void setDither(boolean dither) {\n        mWrapped.setDither(dither);\n    }\n\n    @Override\n    public void setFilterBitmap(boolean filter) {\n        mWrapped.setFilterBitmap(filter);\n    }\n\n    @Override\n    public void setAlpha(int alpha) {\n        mWrapped.setAlpha(alpha);\n    }\n\n    @Override\n    public void setColorFilter(ColorFilter cf) {\n        mWrapped.setColorFilter(cf);\n    }\n\n    @Override\n    public void setColorFilter(int color, PorterDuff.Mode mode) {\n        mWrapped.setColorFilter(color, mode);\n    }\n\n    @Override\n    public void clearColorFilter() {\n        mWrapped.clearColorFilter();\n    }\n\n    @Override\n    public boolean isStateful() {\n        return mWrapped.isStateful();\n    }\n\n    @Override\n    public boolean setState(int[] stateSet) {\n        return mWrapped.setState(stateSet);\n    }\n\n    @Override\n    public int[] getState() {\n        return mWrapped.getState();\n    }\n\n    @Override\n    public Drawable getCurrent() {\n        return mWrapped.getCurrent();\n    }\n\n    @Override\n    public boolean setVisible(boolean visible, boolean restart) {\n        return super.setVisible(visible, restart);\n    }\n\n    @Override\n    public int getOpacity() {\n        return mWrapped.getOpacity();\n    }\n\n    @Override\n    public Region getTransparentRegion() {\n        return mWrapped.getTransparentRegion();\n    }\n\n    @Override\n    protected boolean onStateChange(int[] state) {\n        mWrapped.setState(state);\n        return super.onStateChange(state);\n    }\n\n    @Override\n    protected void onBoundsChange(Rect bounds) {\n        super.onBoundsChange(bounds);\n        mWrapped.setBounds(bounds);\n    }\n\n    @Override\n    public int getIntrinsicWidth() {\n        return mWrapped.getIntrinsicWidth();\n    }\n\n    @Override\n    public int getIntrinsicHeight() {\n        return mWrapped.getIntrinsicHeight();\n    }\n\n    @Override\n    public int getMinimumWidth() {\n        return mWrapped.getMinimumWidth();\n    }\n\n    @Override\n    public int getMinimumHeight() {\n        return mWrapped.getMinimumHeight();\n    }\n\n    @Override\n    public boolean getPadding(Rect padding) {\n        return mWrapped.getPadding(padding);\n    }\n\n    @Override\n    public ConstantState getConstantState() {\n        return super.getConstantState();\n    }\n\n    @Override\n    public void invalidateDrawable(Drawable who) {\n        if (who == mWrapped) {\n            invalidateSelf();\n        }\n    }\n\n    @Override\n    public void scheduleDrawable(Drawable who, Runnable what, long when) {\n        if (who == mWrapped) {\n            scheduleSelf(what, when);\n        }\n    }\n\n    @Override\n    public void unscheduleDrawable(Drawable who, Runnable what) {\n        if (who == mWrapped) {\n            unscheduleSelf(what);\n        }\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/net/simonvt/menudrawer/SlidingDrawer.java",
    "content": "package net.simonvt.menudrawer;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.VelocityTracker;\n\npublic class SlidingDrawer extends DraggableDrawer {\n\n    private static final String TAG = \"OverlayDrawer\";\n\n    SlidingDrawer(Activity activity, int dragMode) {\n        super(activity, dragMode);\n    }\n\n    public SlidingDrawer(Context context) {\n        super(context);\n    }\n\n    public SlidingDrawer(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public SlidingDrawer(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n    }\n\n    @Override\n    protected void initDrawer(Context context, AttributeSet attrs, int defStyle) {\n        super.initDrawer(context, attrs, defStyle);\n        super.addView(mMenuContainer, -1, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));\n        super.addView(mContentContainer, -1, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));\n    }\n\n    @Override\n    public void openMenu(boolean animate) {\n        int animateTo = 0;\n        switch (getPosition()) {\n            case LEFT:\n            case TOP:\n                animateTo = mMenuSize;\n                break;\n\n            case RIGHT:\n            case BOTTOM:\n                animateTo = -mMenuSize;\n                break;\n        }\n\n        animateOffsetTo(animateTo, 0, animate);\n    }\n\n    @Override\n    public void closeMenu(boolean animate) {\n        animateOffsetTo(0, 0, animate);\n    }\n\n    @Override\n    protected void onOffsetPixelsChanged(int offsetPixels) {\n        if (USE_TRANSLATIONS) {\n            switch (getPosition()) {\n                case TOP:\n                case BOTTOM:\n                    mContentContainer.setTranslationY(offsetPixels);\n                    break;\n\n                default:\n                    mContentContainer.setTranslationX(offsetPixels);\n                    break;\n            }\n        } else {\n            switch (getPosition()) {\n                case TOP:\n                case BOTTOM:\n                    mContentContainer.offsetTopAndBottom(offsetPixels - mContentContainer.getTop());\n                    break;\n\n                default:\n                    mContentContainer.offsetLeftAndRight(offsetPixels - mContentContainer.getLeft());\n                    break;\n            }\n        }\n\n        offsetMenu(offsetPixels);\n        invalidate();\n    }\n\n    @Override\n    protected void initPeekScroller() {\n        switch (getPosition()) {\n            case RIGHT:\n            case BOTTOM: {\n                final int dx = -mMenuSize / 3;\n                mPeekScroller.startScroll(0, 0, dx, 0, PEEK_DURATION);\n                break;\n            }\n\n            default: {\n                final int dx = mMenuSize / 3;\n                mPeekScroller.startScroll(0, 0, dx, 0, PEEK_DURATION);\n                break;\n            }\n        }\n    }\n\n    @Override\n    protected void onSizeChanged(int w, int h, int oldw, int oldh) {\n        super.onSizeChanged(w, h, oldw, oldh);\n        onOffsetPixelsChanged((int) mOffsetPixels);\n    }\n\n    @Override\n    protected void drawOverlay(Canvas canvas) {\n        final int width = getWidth();\n        final int height = getHeight();\n        final int offsetPixels = (int) mOffsetPixels;\n        final float openRatio = Math.abs(mOffsetPixels) / mMenuSize;\n\n        switch (getPosition()) {\n            case LEFT:\n                mMenuOverlay.setBounds(0, 0, offsetPixels, height);\n                break;\n\n            case RIGHT:\n                mMenuOverlay.setBounds(width + offsetPixels, 0, width, height);\n                break;\n\n            case TOP:\n                mMenuOverlay.setBounds(0, 0, width, offsetPixels);\n                break;\n\n            case BOTTOM:\n                mMenuOverlay.setBounds(0, height + offsetPixels, width, height);\n                break;\n        }\n\n        mMenuOverlay.setAlpha((int) (MAX_MENU_OVERLAY_ALPHA * (1.f - openRatio)));\n        mMenuOverlay.draw(canvas);\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int l, int t, int r, int b) {\n        final int width = r - l;\n        final int height = b - t;\n\n        if (USE_TRANSLATIONS) {\n            mContentContainer.layout(0, 0, width, height);\n        } else {\n            final int offsetPixels = (int) mOffsetPixels;\n            if (getPosition() == Position.LEFT || getPosition() == Position.RIGHT) {\n                mContentContainer.layout(offsetPixels, 0, width + offsetPixels, height);\n            } else {\n                mContentContainer.layout(0, offsetPixels, width, height + offsetPixels);\n            }\n        }\n\n        switch (getPosition()) {\n            case LEFT:\n                mMenuContainer.layout(0, 0, mMenuSize, height);\n                break;\n\n            case RIGHT:\n                mMenuContainer.layout(width - mMenuSize, 0, width, height);\n                break;\n\n            case TOP:\n                mMenuContainer.layout(0, 0, width, mMenuSize);\n                break;\n\n            case BOTTOM:\n                mMenuContainer.layout(0, height - mMenuSize, width, height);\n                break;\n        }\n    }\n\n    /**\n     * Offsets the menu relative to its original position based on the position of the content.\n     *\n     * @param offsetPixels The number of pixels the content if offset.\n     */\n    private void offsetMenu(int offsetPixels) {\n        if (!mOffsetMenu || mMenuSize == 0) {\n            return;\n        }\n\n        final int width = getWidth();\n        final int height = getHeight();\n\n        final int menuSize = mMenuSize;\n        final int sign = (int) (mOffsetPixels / Math.abs(mOffsetPixels));\n        final float openRatio = Math.abs(mOffsetPixels) / menuSize;\n        final int offset = (int) (-0.25f * ((1.0f - openRatio) * menuSize) * sign);\n\n        switch (getPosition()) {\n            case LEFT: {\n                if (USE_TRANSLATIONS) {\n                    if (offsetPixels > 0) {\n                        mMenuContainer.setTranslationX(offset);\n                    } else {\n                        mMenuContainer.setTranslationX(-menuSize);\n                    }\n\n                } else {\n                    mMenuContainer.offsetLeftAndRight(offset - mMenuContainer.getLeft());\n                    mMenuContainer.setVisibility(offsetPixels == 0 ? INVISIBLE : VISIBLE);\n                }\n                break;\n            }\n\n            case RIGHT: {\n                if (USE_TRANSLATIONS) {\n                    if (offsetPixels != 0) {\n                        mMenuContainer.setTranslationX(offset);\n                    } else {\n                        mMenuContainer.setTranslationX(menuSize);\n                    }\n\n                } else {\n                    final int oldOffset = mMenuContainer.getRight() - width;\n                    final int offsetBy = offset - oldOffset;\n                    mMenuContainer.offsetLeftAndRight(offsetBy);\n                    mMenuContainer.setVisibility(offsetPixels == 0 ? INVISIBLE : VISIBLE);\n                }\n                break;\n            }\n\n            case TOP: {\n                if (USE_TRANSLATIONS) {\n                    if (offsetPixels > 0) {\n                        mMenuContainer.setTranslationY(offset);\n                    } else {\n                        mMenuContainer.setTranslationY(-menuSize);\n                    }\n\n                } else {\n                    mMenuContainer.offsetTopAndBottom(offset - mMenuContainer.getTop());\n                    mMenuContainer.setVisibility(offsetPixels == 0 ? INVISIBLE : VISIBLE);\n                }\n                break;\n            }\n\n            case BOTTOM: {\n                if (USE_TRANSLATIONS) {\n                    if (offsetPixels != 0) {\n                        mMenuContainer.setTranslationY(offset);\n                    } else {\n                        mMenuContainer.setTranslationY(menuSize);\n                    }\n\n                } else {\n                    final int oldOffset = mMenuContainer.getBottom() - height;\n                    final int offsetBy = offset - oldOffset;\n                    mMenuContainer.offsetTopAndBottom(offsetBy);\n                    mMenuContainer.setVisibility(offsetPixels == 0 ? INVISIBLE : VISIBLE);\n                }\n                break;\n            }\n        }\n\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);\n        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);\n\n        if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) {\n            throw new IllegalStateException(\"Must measure with an exact size\");\n        }\n\n        final int width = MeasureSpec.getSize(widthMeasureSpec);\n        final int height = MeasureSpec.getSize(heightMeasureSpec);\n\n        if (mOffsetPixels == -1) openMenu(false);\n\n        int menuWidthMeasureSpec;\n        int menuHeightMeasureSpec;\n        switch (getPosition()) {\n            case TOP:\n            case BOTTOM:\n                menuWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, width);\n                menuHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 0, mMenuSize);\n                break;\n\n            default:\n                // LEFT/RIGHT\n                menuWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, mMenuSize);\n                menuHeightMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, height);\n        }\n        mMenuContainer.measure(menuWidthMeasureSpec, menuHeightMeasureSpec);\n\n        final int contentWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, width);\n        final int contentHeightMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, height);\n        mContentContainer.measure(contentWidthMeasureSpec, contentHeightMeasureSpec);\n\n        setMeasuredDimension(width, height);\n\n        updateTouchAreaSize();\n    }\n\n    private boolean isContentTouch(int x, int y) {\n        boolean contentTouch = false;\n\n        switch (getPosition()) {\n            case LEFT:\n                contentTouch = ViewHelper.getLeft(mContentContainer) < x;\n                break;\n\n            case RIGHT:\n                contentTouch = ViewHelper.getRight(mContentContainer) > x;\n                break;\n\n            case TOP:\n                contentTouch = ViewHelper.getTop(mContentContainer) < y;\n                break;\n\n            case BOTTOM:\n                contentTouch = ViewHelper.getBottom(mContentContainer) > y;\n                break;\n        }\n\n        return contentTouch;\n    }\n\n    protected boolean onDownAllowDrag(int x, int y) {\n        switch (getPosition()) {\n            case LEFT:\n                return (!mMenuVisible && mInitialMotionX <= mTouchSize)\n                        || (mMenuVisible && mInitialMotionX >= mOffsetPixels);\n\n            case RIGHT:\n                final int width = getWidth();\n                final int initialMotionX = (int) mInitialMotionX;\n\n                return (!mMenuVisible && initialMotionX >= width - mTouchSize)\n                        || (mMenuVisible && initialMotionX <= width + mOffsetPixels);\n\n            case TOP:\n                return (!mMenuVisible && mInitialMotionY <= mTouchSize)\n                        || (mMenuVisible && mInitialMotionY >= mOffsetPixels);\n\n            case BOTTOM:\n                final int height = getHeight();\n                return (!mMenuVisible && mInitialMotionY >= height - mTouchSize)\n                        || (mMenuVisible && mInitialMotionY <= height + mOffsetPixels);\n        }\n\n        return false;\n    }\n\n    protected boolean onMoveAllowDrag(int x, int y, float dx, float dy) {\n        switch (getPosition()) {\n            case LEFT:\n                return (!mMenuVisible && mInitialMotionX <= mTouchSize && (dx > 0))\n                        || (mMenuVisible && x >= mOffsetPixels);\n\n            case RIGHT:\n                final int width = getWidth();\n                return (!mMenuVisible && mInitialMotionX >= width - mTouchSize && (dx < 0))\n                        || (mMenuVisible && x <= width + mOffsetPixels);\n\n            case TOP:\n                return (!mMenuVisible && mInitialMotionY <= mTouchSize && (dy > 0))\n                        || (mMenuVisible && y >= mOffsetPixels);\n\n            case BOTTOM:\n                final int height = getHeight();\n                return (!mMenuVisible && mInitialMotionY >= height - mTouchSize && (dy < 0))\n                        || (mMenuVisible && y <= height + mOffsetPixels);\n        }\n\n        return false;\n    }\n\n    protected void onMoveEvent(float dx, float dy) {\n        switch (getPosition()) {\n            case LEFT:\n                setOffsetPixels(Math.min(Math.max(mOffsetPixels + dx, 0), mMenuSize));\n                break;\n\n            case RIGHT:\n                setOffsetPixels(Math.max(Math.min(mOffsetPixels + dx, 0), -mMenuSize));\n                break;\n\n            case TOP:\n                setOffsetPixels(Math.min(Math.max(mOffsetPixels + dy, 0), mMenuSize));\n                break;\n\n            case BOTTOM:\n                setOffsetPixels(Math.max(Math.min(mOffsetPixels + dy, 0), -mMenuSize));\n                break;\n        }\n    }\n\n    protected void onUpEvent(int x, int y) {\n        final int offsetPixels = (int) mOffsetPixels;\n\n        switch (getPosition()) {\n            case LEFT: {\n                if (mIsDragging) {\n                    mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);\n                    final int initialVelocity = (int) getXVelocity(mVelocityTracker);\n                    mLastMotionX = x;\n                    animateOffsetTo(initialVelocity > 0 ? mMenuSize : 0, initialVelocity, true);\n\n                    // Close the menu when content is clicked while the menu is visible.\n                } else if (mMenuVisible && x > offsetPixels) {\n                    closeMenu();\n                }\n                break;\n            }\n\n            case TOP: {\n                if (mIsDragging) {\n                    mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);\n                    final int initialVelocity = (int) getYVelocity(mVelocityTracker);\n                    mLastMotionY = y;\n                    animateOffsetTo(initialVelocity > 0 ? mMenuSize : 0, initialVelocity, true);\n\n                    // Close the menu when content is clicked while the menu is visible.\n                } else if (mMenuVisible && y > offsetPixels) {\n                    closeMenu();\n                }\n                break;\n            }\n\n            case RIGHT: {\n                final int width = getWidth();\n\n                if (mIsDragging) {\n                    mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);\n                    final int initialVelocity = (int) getXVelocity(mVelocityTracker);\n                    mLastMotionX = x;\n                    animateOffsetTo(initialVelocity > 0 ? 0 : -mMenuSize, initialVelocity, true);\n\n                    // Close the menu when content is clicked while the menu is visible.\n                } else if (mMenuVisible && x < width + offsetPixels) {\n                    closeMenu();\n                }\n                break;\n            }\n\n            case BOTTOM: {\n                if (mIsDragging) {\n                    mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);\n                    final int initialVelocity = (int) getYVelocity(mVelocityTracker);\n                    mLastMotionY = y;\n                    animateOffsetTo(initialVelocity < 0 ? -mMenuSize : 0, initialVelocity, true);\n\n                    // Close the menu when content is clicked while the menu is visible.\n                } else if (mMenuVisible && y < getHeight() + offsetPixels) {\n                    closeMenu();\n                }\n                break;\n            }\n        }\n    }\n\n    protected boolean checkTouchSlop(float dx, float dy) {\n        switch (getPosition()) {\n            case TOP:\n            case BOTTOM:\n                return Math.abs(dy) > mTouchSlop && Math.abs(dy) > Math.abs(dx);\n\n            default:\n                return Math.abs(dx) > mTouchSlop && Math.abs(dx) > Math.abs(dy);\n        }\n    }\n\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        final int action = ev.getAction() & MotionEvent.ACTION_MASK;\n\n        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {\n            mActivePointerId = INVALID_POINTER;\n            mIsDragging = false;\n            if (mVelocityTracker != null) {\n                mVelocityTracker.recycle();\n                mVelocityTracker = null;\n            }\n\n            if (Math.abs(mOffsetPixels) > mMenuSize / 2) {\n                openMenu();\n            } else {\n                closeMenu();\n            }\n\n            return false;\n        }\n\n        if (action == MotionEvent.ACTION_DOWN && mMenuVisible && isCloseEnough()) {\n            setOffsetPixels(0);\n            stopAnimation();\n            endPeek();\n            setDrawerState(STATE_CLOSED);\n            mIsDragging = false;\n        }\n\n        // Always intercept events over the content while menu is visible.\n        if (mMenuVisible) {\n            int index = 0;\n            if (mActivePointerId != INVALID_POINTER) {\n                index = ev.findPointerIndex(mActivePointerId);\n                index = index == -1 ? 0 : index;\n            }\n\n            final int x = (int) ev.getX(index);\n            final int y = (int) ev.getY(index);\n            if (isContentTouch(x, y)) {\n                return true;\n            }\n        }\n\n        if (!mMenuVisible && !mIsDragging && mTouchMode == TOUCH_MODE_NONE) {\n            return false;\n        }\n\n        if (action != MotionEvent.ACTION_DOWN && mIsDragging) {\n            return true;\n        }\n\n        switch (action) {\n            case MotionEvent.ACTION_DOWN: {\n                mLastMotionX = mInitialMotionX = ev.getX();\n                mLastMotionY = mInitialMotionY = ev.getY();\n                final boolean allowDrag = onDownAllowDrag((int) mLastMotionX, (int) mLastMotionY);\n                mActivePointerId = ev.getPointerId(0);\n\n                if (allowDrag) {\n                    setDrawerState(mMenuVisible ? STATE_OPEN : STATE_CLOSED);\n                    stopAnimation();\n                    endPeek();\n                    mIsDragging = false;\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_MOVE: {\n                final int activePointerId = mActivePointerId;\n                if (activePointerId == INVALID_POINTER) {\n                    // If we don't have a valid id, the touch down wasn't on content.\n                    break;\n                }\n\n                final int pointerIndex = ev.findPointerIndex(activePointerId);\n                if (pointerIndex == -1) {\n                    mIsDragging = false;\n                    mActivePointerId = INVALID_POINTER;\n                    endDrag();\n                    closeMenu(true);\n                    return false;\n                }\n\n                final float x = ev.getX(pointerIndex);\n                final float dx = x - mLastMotionX;\n                final float y = ev.getY(pointerIndex);\n                final float dy = y - mLastMotionY;\n\n                if (checkTouchSlop(dx, dy)) {\n                    if (mOnInterceptMoveEventListener != null && (mTouchMode == TOUCH_MODE_FULLSCREEN || mMenuVisible)\n                            && canChildrenScroll((int) dx, (int) dy, (int) x, (int) y)) {\n                        endDrag(); // Release the velocity tracker\n                        requestDisallowInterceptTouchEvent(true);\n                        return false;\n                    }\n\n                    final boolean allowDrag = onMoveAllowDrag((int) x, (int) y, dx, dy);\n\n                    if (allowDrag) {\n                        setDrawerState(STATE_DRAGGING);\n                        mIsDragging = true;\n                        mLastMotionX = x;\n                        mLastMotionY = y;\n                    }\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_POINTER_UP:\n                onPointerUp(ev);\n                mLastMotionX = ev.getX(ev.findPointerIndex(mActivePointerId));\n                mLastMotionY = ev.getY(ev.findPointerIndex(mActivePointerId));\n                break;\n        }\n\n        if (mVelocityTracker == null) mVelocityTracker = VelocityTracker.obtain();\n        mVelocityTracker.addMovement(ev);\n\n        return mIsDragging;\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent ev) {\n        if (!mMenuVisible && !mIsDragging && mTouchMode == TOUCH_MODE_NONE) {\n            return false;\n        }\n        final int action = ev.getAction() & MotionEvent.ACTION_MASK;\n\n        if (mVelocityTracker == null) mVelocityTracker = VelocityTracker.obtain();\n        mVelocityTracker.addMovement(ev);\n\n        switch (action) {\n            case MotionEvent.ACTION_DOWN: {\n                mLastMotionX = mInitialMotionX = ev.getX();\n                mLastMotionY = mInitialMotionY = ev.getY();\n                final boolean allowDrag = onDownAllowDrag((int) mLastMotionX, (int) mLastMotionY);\n\n                mActivePointerId = ev.getPointerId(0);\n\n                if (allowDrag) {\n                    stopAnimation();\n                    endPeek();\n                    startLayerTranslation();\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_MOVE: {\n                final int pointerIndex = ev.findPointerIndex(mActivePointerId);\n                if (pointerIndex == -1) {\n                    mIsDragging = false;\n                    mActivePointerId = INVALID_POINTER;\n                    endDrag();\n                    closeMenu(true);\n                    return false;\n                }\n\n                if (!mIsDragging) {\n                    final float x = ev.getX(pointerIndex);\n                    final float dx = x - mLastMotionX;\n                    final float y = ev.getY(pointerIndex);\n                    final float dy = y - mLastMotionY;\n\n                    if (checkTouchSlop(dx, dy)) {\n                        final boolean allowDrag = onMoveAllowDrag((int) x, (int) y, dx, dy);\n\n                        if (allowDrag) {\n                            setDrawerState(STATE_DRAGGING);\n                            mIsDragging = true;\n                            mLastMotionX = x;\n                            mLastMotionY = y;\n                        } else {\n                            mInitialMotionX = x;\n                            mInitialMotionY = y;\n                        }\n                    }\n                }\n\n                if (mIsDragging) {\n                    startLayerTranslation();\n\n                    final float x = ev.getX(pointerIndex);\n                    final float dx = x - mLastMotionX;\n                    final float y = ev.getY(pointerIndex);\n                    final float dy = y - mLastMotionY;\n\n                    mLastMotionX = x;\n                    mLastMotionY = y;\n                    onMoveEvent(dx, dy);\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_CANCEL:\n            case MotionEvent.ACTION_UP: {\n                int index = ev.findPointerIndex(mActivePointerId);\n                index = index == -1 ? 0 : index;\n                final int x = (int) ev.getX(index);\n                final int y = (int) ev.getY(index);\n                onUpEvent(x, y);\n                mActivePointerId = INVALID_POINTER;\n                mIsDragging = false;\n                break;\n            }\n\n            case MotionEvent.ACTION_POINTER_DOWN:\n                final int index = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)\n                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;\n                mLastMotionX = ev.getX(index);\n                mLastMotionY = ev.getY(index);\n                mActivePointerId = ev.getPointerId(index);\n                break;\n\n            case MotionEvent.ACTION_POINTER_UP:\n                onPointerUp(ev);\n                mLastMotionX = ev.getX(ev.findPointerIndex(mActivePointerId));\n                mLastMotionY = ev.getY(ev.findPointerIndex(mActivePointerId));\n                break;\n        }\n\n        return true;\n    }\n\n    private void onPointerUp(MotionEvent ev) {\n        final int pointerIndex = ev.getActionIndex();\n        final int pointerId = ev.getPointerId(pointerIndex);\n        if (pointerId == mActivePointerId) {\n            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;\n            mLastMotionX = ev.getX(newPointerIndex);\n            mActivePointerId = ev.getPointerId(newPointerIndex);\n            if (mVelocityTracker != null) {\n                mVelocityTracker.clear();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/net/simonvt/menudrawer/SmoothInterpolator.java",
    "content": "package net.simonvt.menudrawer;\n\nimport android.view.animation.Interpolator;\n\nclass SmoothInterpolator implements Interpolator {\n\n    @Override\n    public float getInterpolation(float t) {\n        t -= 1.0f;\n        return t * t * t * t * t + 1.0f;\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/net/simonvt/menudrawer/StaticDrawer.java",
    "content": "package net.simonvt.menudrawer;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.drawable.Drawable;\nimport android.util.AttributeSet;\n\npublic class StaticDrawer extends MenuDrawer {\n\n    public StaticDrawer(Context context) {\n        super(context);\n    }\n\n    public StaticDrawer(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public StaticDrawer(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n    }\n\n    @Override\n    protected void initDrawer(Context context, AttributeSet attrs, int defStyle) {\n        super.initDrawer(context, attrs, defStyle);\n        super.addView(mMenuContainer, -1, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));\n        super.addView(mContentContainer, -1, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));\n\n        mIsStatic = true;\n    }\n\n    @Override\n    protected void drawOverlay(Canvas canvas) {\n        // NO-OP\n    }\n\n    @Override\n    protected void onOffsetPixelsChanged(int offsetPixels) {\n        // NO-OP\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int l, int t, int r, int b) {\n        final int width = r - l;\n        final int height = b - t;\n\n        switch (getPosition()) {\n            case LEFT:\n                mMenuContainer.layout(0, 0, mMenuSize, height);\n                mContentContainer.layout(mMenuSize, 0, width, height);\n                break;\n\n            case RIGHT:\n                mMenuContainer.layout(width - mMenuSize, 0, width, height);\n                mContentContainer.layout(0, 0, width - mMenuSize, height);\n                break;\n\n            case TOP:\n                mMenuContainer.layout(0, 0, width, mMenuSize);\n                mContentContainer.layout(0, mMenuSize, width, height);\n                break;\n\n            case BOTTOM:\n                mMenuContainer.layout(0, height - mMenuSize, width, height);\n                mContentContainer.layout(0, 0, width, height - mMenuSize);\n                break;\n        }\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);\n        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);\n\n        if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) {\n            throw new IllegalStateException(\"Must measure with an exact size\");\n        }\n\n        final int width = MeasureSpec.getSize(widthMeasureSpec);\n        final int height = MeasureSpec.getSize(heightMeasureSpec);\n\n        switch (getPosition()) {\n            case LEFT:\n            case RIGHT: {\n                final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);\n\n                final int menuWidth = mMenuSize;\n                final int menuWidthMeasureSpec = MeasureSpec.makeMeasureSpec(menuWidth, MeasureSpec.EXACTLY);\n\n                final int contentWidth = width - menuWidth;\n                final int contentWidthMeasureSpec = MeasureSpec.makeMeasureSpec(contentWidth, MeasureSpec.EXACTLY);\n\n                mContentContainer.measure(contentWidthMeasureSpec, childHeightMeasureSpec);\n                mMenuContainer.measure(menuWidthMeasureSpec, childHeightMeasureSpec);\n                break;\n            }\n\n            case TOP:\n            case BOTTOM: {\n                final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);\n\n                final int menuHeight = mMenuSize;\n                final int menuHeightMeasureSpec = MeasureSpec.makeMeasureSpec(menuHeight, MeasureSpec.EXACTLY);\n\n                final int contentHeight = height - menuHeight;\n                final int contentHeightMeasureSpec = MeasureSpec.makeMeasureSpec(contentHeight, MeasureSpec.EXACTLY);\n\n                mContentContainer.measure(childWidthMeasureSpec, contentHeightMeasureSpec);\n                mMenuContainer.measure(childWidthMeasureSpec, menuHeightMeasureSpec);\n                break;\n            }\n        }\n\n        setMeasuredDimension(width, height);\n    }\n\n    @Override\n    public void toggleMenu(boolean animate) {\n        // NO-OP\n    }\n\n    @Override\n    public void openMenu(boolean animate) {\n        // NO-OP\n    }\n\n    @Override\n    public void closeMenu(boolean animate) {\n        // NO-OP\n    }\n\n    @Override\n    public boolean isMenuVisible() {\n        return true;\n    }\n\n    @Override\n    public void setMenuSize(int size) {\n        mMenuSize = size;\n        requestLayout();\n        invalidate();\n    }\n\n    @Override\n    public void setOffsetMenuEnabled(boolean offsetMenu) {\n        // NO-OP\n    }\n\n    @Override\n    public boolean getOffsetMenuEnabled() {\n        return false;\n    }\n\n    @Override\n    public void peekDrawer() {\n        // NO-OP\n    }\n\n    @Override\n    public void peekDrawer(long delay) {\n        // NO-OP\n    }\n\n    @Override\n    public void peekDrawer(long startDelay, long delay) {\n        // NO-OP\n    }\n\n    @Override\n    public void setHardwareLayerEnabled(boolean enabled) {\n        // NO-OP\n    }\n\n    @Override\n    public int getTouchMode() {\n        return TOUCH_MODE_NONE;\n    }\n\n    @Override\n    public void setTouchMode(int mode) {\n        // NO-OP\n    }\n\n    @Override\n    public void setTouchBezelSize(int size) {\n        // NO-OP\n    }\n\n    @Override\n    public int getTouchBezelSize() {\n        return 0;\n    }\n\n    @Override\n    public void setSlideDrawable(int drawableRes) {\n        // NO-OP\n    }\n\n    @Override\n    public void setSlideDrawable(Drawable drawable) {\n        // NO-OP\n    }\n\n    @Override\n    public void setupUpIndicator(Activity activity) {\n        // NO-OP\n    }\n\n    @Override\n    public void setDrawerIndicatorEnabled(boolean enabled) {\n        // NO-OP\n    }\n\n    @Override\n    public boolean isDrawerIndicatorEnabled() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/net/simonvt/menudrawer/ViewHelper.java",
    "content": "package net.simonvt.menudrawer;\n\nimport android.os.Build;\nimport android.view.View;\n\nfinal class ViewHelper {\n\n    private ViewHelper() {\n    }\n\n    public static int getLeft(View v) {\n        if (MenuDrawer.USE_TRANSLATIONS) {\n            return (int) (v.getLeft() + v.getTranslationX());\n        }\n\n        return v.getLeft();\n    }\n\n    public static int getTop(View v) {\n        if (MenuDrawer.USE_TRANSLATIONS) {\n            return (int) (v.getTop() + v.getTranslationY());\n        }\n\n        return v.getTop();\n    }\n\n    public static int getRight(View v) {\n        if (MenuDrawer.USE_TRANSLATIONS) {\n            return (int) (v.getRight() + v.getTranslationX());\n        }\n\n        return v.getRight();\n    }\n\n    public static int getBottom(View v) {\n        if (MenuDrawer.USE_TRANSLATIONS) {\n            return (int) (v.getBottom() + v.getTranslationY());\n        }\n\n        return v.getBottom();\n    }\n\n    public static int getLayoutDirection(View v) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            return v.getLayoutDirection();\n        }\n\n        return View.LAYOUT_DIRECTION_LTR;\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/net/simonvt/menudrawer/compat/ActionBarHelper.java",
    "content": "package net.simonvt.menudrawer.compat;\n\nimport java.lang.reflect.Method;\n\nimport android.app.Activity;\nimport android.graphics.drawable.Drawable;\nimport android.os.Build;\nimport android.util.Log;\n\npublic final class ActionBarHelper {\n\n    private static final String TAG = \"ActionBarHelper\";\n\n    static final boolean DEBUG = false;\n\n    private Activity mActivity;\n\n    private Object mIndicatorInfo;\n\n    private boolean mUsesCompat;\n\n    public ActionBarHelper(Activity activity) {\n        mActivity = activity;\n\n        try {\n            Class clazz = activity.getClass();\n            Method m = clazz.getMethod(\"getSupportActionBar\");\n            mUsesCompat = true;\n        } catch (NoSuchMethodException e) {\n            if (DEBUG) {\n                Log.e(TAG,\n                        \"Activity \" + activity.getClass().getSimpleName() + \" does not use a compatibility action bar\",\n                        e);\n            }\n        }\n\n        mIndicatorInfo = getIndicatorInfo();\n    }\n\n    private Object getIndicatorInfo() {\n        if (mUsesCompat && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {\n            return ActionBarHelperCompat.getIndicatorInfo(mActivity);\n        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {\n            return ActionBarHelperNative.getIndicatorInfo(mActivity);\n        }\n\n        return null;\n    }\n\n    public void setActionBarUpIndicator(Drawable drawable, int contentDesc) {\n        if (mUsesCompat && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {\n            ActionBarHelperCompat.setActionBarUpIndicator(mIndicatorInfo, mActivity, drawable, contentDesc);\n        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {\n            ActionBarHelperNative.setActionBarUpIndicator(mIndicatorInfo, mActivity, drawable, contentDesc);\n        }\n    }\n\n    public void setActionBarDescription(int contentDesc) {\n        if (mUsesCompat && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {\n            ActionBarHelperCompat.setActionBarDescription(mIndicatorInfo, mActivity, contentDesc);\n        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {\n            ActionBarHelperNative.setActionBarDescription(mIndicatorInfo, mActivity, contentDesc);\n        }\n    }\n\n    public Drawable getThemeUpIndicator() {\n        if (mUsesCompat && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {\n            return ActionBarHelperCompat.getThemeUpIndicator(mIndicatorInfo);\n        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {\n            return ActionBarHelperNative.getThemeUpIndicator(mIndicatorInfo, mActivity);\n        }\n\n        return null;\n    }\n\n    public void setDisplayShowHomeAsUpEnabled(boolean enabled) {\n        if (mUsesCompat && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {\n            ActionBarHelperCompat.setDisplayHomeAsUpEnabled(mIndicatorInfo, enabled);\n        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {\n            ActionBarHelperNative.setDisplayHomeAsUpEnabled(mActivity, enabled);\n        }\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/net/simonvt/menudrawer/compat/ActionBarHelperCompat.java",
    "content": "package net.simonvt.menudrawer.compat;\n\nimport java.lang.reflect.Method;\n\nimport android.app.Activity;\nimport android.graphics.drawable.Drawable;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\n\nfinal class ActionBarHelperCompat {\n\n    private static final String TAG = \"ActionBarHelperCompat\";\n\n    private ActionBarHelperCompat() {\n    }\n\n    public static void setActionBarUpIndicator(Object info, Activity activity, Drawable drawable, int contentDescRes) {\n        final SetIndicatorInfo sii = (SetIndicatorInfo) info;\n        if (sii.mUpIndicatorView != null) {\n            sii.mUpIndicatorView.setImageDrawable(drawable);\n            final String contentDescription = contentDescRes == 0 ? null : activity.getString(contentDescRes);\n            sii.mUpIndicatorView.setContentDescription(contentDescription);\n        }\n    }\n\n    public static void setActionBarDescription(Object info, Activity activity, int contentDescRes) {\n        final SetIndicatorInfo sii = (SetIndicatorInfo) info;\n        if (sii.mUpIndicatorView != null) {\n            final String contentDescription = contentDescRes == 0 ? null : activity.getString(contentDescRes);\n            sii.mUpIndicatorView.setContentDescription(contentDescription);\n        }\n    }\n\n    public static Drawable getThemeUpIndicator(Object info) {\n        final SetIndicatorInfo sii = (SetIndicatorInfo) info;\n        if (sii.mUpIndicatorView != null) {\n            return sii.mUpIndicatorView.getDrawable();\n        }\n        return null;\n    }\n\n    public static Object getIndicatorInfo(Activity activity) {\n        return new SetIndicatorInfo(activity);\n    }\n\n    public static void setDisplayHomeAsUpEnabled(Object info, boolean enabled) {\n        final SetIndicatorInfo sii = (SetIndicatorInfo) info;\n        if (sii.mHomeAsUpEnabled != null) {\n            try {\n                sii.mHomeAsUpEnabled.invoke(sii.mActionBar, enabled);\n            } catch (Throwable t) {\n                if (ActionBarHelper.DEBUG) {\n                    Log.e(TAG, \"Unable to call setHomeAsUpEnabled\", t);\n                }\n            }\n        }\n    }\n\n    private static class SetIndicatorInfo {\n\n        public ImageView mUpIndicatorView;\n        public Object mActionBar;\n        public Method mHomeAsUpEnabled;\n\n        SetIndicatorInfo(Activity activity) {\n            try {\n                String appPackage = activity.getPackageName();\n\n                try {\n                    // Attempt to find ActionBarSherlock up indicator\n                    final int homeId = activity.getResources().getIdentifier(\"abs__home\", \"id\", appPackage);\n                    View v = activity.findViewById(homeId);\n                    ViewGroup parent = (ViewGroup) v.getParent();\n                    final int upId = activity.getResources().getIdentifier(\"abs__up\", \"id\", appPackage);\n                    mUpIndicatorView = (ImageView) parent.findViewById(upId);\n                } catch (Throwable t) {\n                    if (ActionBarHelper.DEBUG) {\n                        Log.e(TAG, \"ABS action bar not found\", t);\n                    }\n                }\n\n                if (mUpIndicatorView == null) {\n                    // Attempt to find AppCompat up indicator\n                    final int homeId = activity.getResources().getIdentifier(\"home\", \"id\", appPackage);\n                    View v = activity.findViewById(homeId);\n                    ViewGroup parent = (ViewGroup) v.getParent();\n                    final int upId = activity.getResources().getIdentifier(\"up\", \"id\", appPackage);\n                    mUpIndicatorView = (ImageView) parent.findViewById(upId);\n                }\n\n                Class supportActivity = activity.getClass();\n                Method getActionBar = supportActivity.getMethod(\"getSupportActionBar\");\n\n                mActionBar = getActionBar.invoke(activity, null);\n                Class supportActionBar = mActionBar.getClass();\n                mHomeAsUpEnabled = supportActionBar.getMethod(\"setDisplayHomeAsUpEnabled\", Boolean.TYPE);\n\n            } catch (Throwable t) {\n                if (ActionBarHelper.DEBUG) {\n                    Log.e(TAG, \"Unable to init SetIndicatorInfo for ABS\", t);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "WayHoo/src/net/simonvt/menudrawer/compat/ActionBarHelperNative.java",
    "content": "package net.simonvt.menudrawer.compat;\n\nimport java.lang.reflect.Method;\n\nimport android.annotation.SuppressLint;\nimport android.app.ActionBar;\nimport android.app.Activity;\nimport android.content.res.TypedArray;\nimport android.graphics.drawable.Drawable;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\n\n@SuppressLint(\"NewApi\")\nfinal class ActionBarHelperNative {\n\n    private static final String TAG = \"ActionBarHelperNative\";\n\n    private ActionBarHelperNative() {\n    }\n\n    private static final int[] THEME_ATTRS = new int[] {\n            android.R.attr.homeAsUpIndicator\n    };\n\n    public static void setActionBarUpIndicator(Object info, Activity activity, Drawable drawable, int contentDescRes) {\n\n        final SetIndicatorInfo sii = (SetIndicatorInfo) info;\n        if (sii.setHomeAsUpIndicator != null) {\n            try {\n                final ActionBar actionBar = activity.getActionBar();\n                sii.setHomeAsUpIndicator.invoke(actionBar, drawable);\n                sii.setHomeActionContentDescription.invoke(actionBar, contentDescRes);\n            } catch (Throwable t) {\n                if (ActionBarHelper.DEBUG) Log.e(TAG, \"Couldn't set home-as-up indicator via JB-MR2 API\", t);\n            }\n        } else if (sii.upIndicatorView != null) {\n            sii.upIndicatorView.setImageDrawable(drawable);\n        } else {\n            if (ActionBarHelper.DEBUG) Log.e(TAG, \"Couldn't set home-as-up indicator\");\n        }\n    }\n\n    public static void setActionBarDescription(Object info, Activity activity, int contentDescRes) {\n        final SetIndicatorInfo sii = (SetIndicatorInfo) info;\n        if (sii.setHomeAsUpIndicator != null) {\n            try {\n                final ActionBar actionBar = activity.getActionBar();\n                sii.setHomeActionContentDescription.invoke(actionBar, contentDescRes);\n            } catch (Throwable t) {\n                if (ActionBarHelper.DEBUG) Log.e(TAG, \"Couldn't set content description via JB-MR2 API\", t);\n            }\n        }\n    }\n\n    public static Drawable getThemeUpIndicator(Object info, Activity activity) {\n        final TypedArray a = activity.obtainStyledAttributes(THEME_ATTRS);\n        final Drawable result = a.getDrawable(0);\n        a.recycle();\n        return result;\n    }\n\n    public static Object getIndicatorInfo(Activity activity) {\n        return new SetIndicatorInfo(activity);\n    }\n\n    public static void setDisplayHomeAsUpEnabled(Activity activity, boolean b) {\n        ActionBar actionBar = activity.getActionBar();\n        if (actionBar != null) {\n            actionBar.setDisplayHomeAsUpEnabled(b);\n        }\n    }\n\n    private static class SetIndicatorInfo {\n\n        public Method setHomeAsUpIndicator;\n        public Method setHomeActionContentDescription;\n        public ImageView upIndicatorView;\n\n        SetIndicatorInfo(Activity activity) {\n            try {\n                setHomeAsUpIndicator = ActionBar.class.getDeclaredMethod(\"setHomeAsUpIndicator\", Drawable.class);\n                setHomeActionContentDescription = ActionBar.class.getDeclaredMethod(\n                        \"setHomeActionContentDescription\", Integer.TYPE);\n\n                // If we got the method we won't need the stuff below.\n                return;\n            } catch (Throwable t) {\n                // Oh well. We'll use the other mechanism below instead.\n            }\n\n            final View home = activity.findViewById(android.R.id.home);\n            if (home == null) {\n                // Action bar doesn't have a known configuration, an OEM messed with things.\n                return;\n            }\n\n            final ViewGroup parent = (ViewGroup) home.getParent();\n            final int childCount = parent.getChildCount();\n            if (childCount != 2) {\n                // No idea which one will be the right one, an OEM messed with things.\n                return;\n            }\n\n            final View first = parent.getChildAt(0);\n            final View second = parent.getChildAt(1);\n            final View up = first.getId() == android.R.id.home ? second : first;\n\n            if (up instanceof ImageView) {\n                // Jackpot! (Probably...)\n                upIndicatorView = (ImageView) up;\n            }\n        }\n    }\n}\n"
  }
]