[
  {
    "path": ".classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"con\" path=\"com.android.ide.eclipse.adt.ANDROID_FRAMEWORK\"/>\n\t<classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.LIBRARIES\"/>\n\t<classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.DEPENDENCIES\"/>\n\t<classpathentry kind=\"src\" path=\"src\"/>\n\t<classpathentry kind=\"src\" path=\"gen\"/>\n\t<classpathentry kind=\"con\" path=\"org.eclipse.andmore.ANDROID_FRAMEWORK\"/>\n\t<classpathentry exported=\"true\" kind=\"con\" path=\"org.eclipse.andmore.LIBRARIES\"/>\n\t<classpathentry exported=\"true\" kind=\"con\" path=\"org.eclipse.andmore.DEPENDENCIES\"/>\n\t<classpathentry kind=\"output\" path=\"bin/classes\"/>\n</classpath>\n"
  },
  {
    "path": ".externalToolBuilders/Set Build Info.launch",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<launchConfiguration type=\"org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType\">\n<booleanAttribute key=\"org.eclipse.debug.core.capture_output\" value=\"false\"/>\n<booleanAttribute key=\"org.eclipse.debug.ui.ATTR_CONSOLE_OUTPUT_ON\" value=\"false\"/>\n<booleanAttribute key=\"org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND\" value=\"false\"/>\n<stringAttribute key=\"org.eclipse.ui.externaltools.ATTR_LOCATION\" value=\"${workspace_loc:/satstat/setbuild.sh}\"/>\n<stringAttribute key=\"org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS\" value=\"full,incremental,auto,\"/>\n<booleanAttribute key=\"org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED\" value=\"true\"/>\n<stringAttribute key=\"org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY\" value=\"${workspace_loc:/satstat}\"/>\n</launchConfiguration>\n"
  },
  {
    "path": ".gitignore",
    "content": ".gradle\nbin\nbuild\ngen\nres/raw/build.txt\n"
  },
  {
    "path": ".project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>satstat</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>\n\t\t\t<triggers>auto,full,incremental,</triggers>\n\t\t\t<arguments>\n\t\t\t\t<dictionary>\n\t\t\t\t\t<key>LaunchConfigHandle</key>\n\t\t\t\t\t<value>&lt;project&gt;/.externalToolBuilders/Set Build Info.launch</value>\n\t\t\t\t</dictionary>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.andmore.ResourceManagerBuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.andmore.PreCompilerBuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.andmore.ApkBuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>org.eclipse.andmore.AndroidNature</nature>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": ".settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6\norg.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve\norg.eclipse.jdt.core.compiler.compliance=1.6\norg.eclipse.jdt.core.compiler.debug.lineNumber=generate\norg.eclipse.jdt.core.compiler.debug.localVariable=generate\norg.eclipse.jdt.core.compiler.debug.sourceFile=generate\norg.eclipse.jdt.core.compiler.problem.assertIdentifier=error\norg.eclipse.jdt.core.compiler.problem.enumIdentifier=error\norg.eclipse.jdt.core.compiler.source=1.6\n"
  },
  {
    "path": "AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.vonglasow.michael.satstat\"\n    android:versionCode=\"30400\"\n    android:versionName=\"3.4\" >\n\n    <uses-sdk\n        android:minSdkVersion=\"11\"\n        android:targetSdkVersion=\"23\" />\n\n    <uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />\n    <uses-permission android:name=\"android.permission.READ_PHONE_STATE\" />\n    <uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\" />\n    <uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\" />\n    <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.ACCESS_LOCATION_EXTRA_COMMANDS\" />\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n\n    <application\n        android:name=\".SatStatApplication\"\n        android:allowBackup=\"true\"\n        android:icon=\"@drawable/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme\" >\n        <activity\n            android:name=\".ui.MainActivity\"\n            android:label=\"@string/app_name\" >\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=\".ui.AboutActivity\"\n            android:label=\"@string/title_activity_about\"\n            android:parentActivityName=\".ui.MainActivity\" >\n            <meta-data\n                android:name=\"android.support.PARENT_ACTIVITY\"\n                android:value=\".ui.MainActivity\" />\n        </activity>\n        <activity\n            android:name=\".ui.SettingsActivity\"\n            android:label=\"@string/title_activity_settings\"\n            android:parentActivityName=\".ui.MainActivity\" >\n            <meta-data\n                android:name=\"android.support.PARENT_ACTIVITY\"\n                android:value=\".ui.MainActivity\" />\n        </activity>\n\n        <receiver android:name=\"GpsEventReceiver\" >\n            <intent-filter>\n                <action android:name=\"android.location.GPS_ENABLED_CHANGE\" />\n                <action android:name=\"android.location.GPS_FIX_CHANGE\" />\n                <action android:name=\"android.net.conn.CONNECTIVITY_CHANGE\" />\n                <action android:name=\"android.net.wifi.STATE_CHANGE\" />\n                <action android:name=\"com.vonglasow.michael.satstat.AGPS_DATA_EXPIRED\" />\n            </intent-filter>\n        </receiver>\n\n        <service\n            android:name=\"PasvLocListenerService\"\n            android:exported=\"false\"\n            android:stopWithTask=\"false\" >\n        </service>\n\n        <activity\n            android:name=\".ui.LegendActivity\"\n            android:label=\"@string/title_activity_legend\"\n            android:parentActivityName=\".ui.MainActivity\" >\n            <meta-data\n                android:name=\"android.support.PARENT_ACTIVITY\"\n                android:value=\".ui.MainActivity\" />\n        </activity>\n        <activity\n            android:name=\".ui.MapDownloadActivity\"\n            android:label=\"@string/title_activity_map_download\"\n            android:parentActivityName=\".ui.SettingsActivity\" >\n            <meta-data\n                android:name=\"android.support.PARENT_ACTIVITY\"\n                android:value=\".ui.SettingsActivity\" />\n        </activity>\n        <activity\n            android:name=\".utils.PermissionHelper$PermissionRequestActivity\" >\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "LICENSE",
    "content": "GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. {http://fsf.org/}\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    {one line to give the program's name and a brief idea of what it does.}\n    Copyright (C) {year}  {name of author}\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see {http://www.gnu.org/licenses/}.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    satstat  Copyright (C) 2013  mvglasow\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n{http://www.gnu.org/licenses/}.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n{http://www.gnu.org/philosophy/why-not-lgpl.html}.\n"
  },
  {
    "path": "README.md",
    "content": "SatStat\n=======\n\nAndroid Location, Sensor and Radio Network Status\n\n<a href=\"https://f-droid.org/packages/com.vonglasow.michael.satstat/\" target=\"_blank\">\n<img src=\"https://f-droid.org/badge/get-it-on.png\" alt=\"Get it on F-Droid\" height=\"80\"/></a>\n\n---\n\n<img alt=\"GPS view in SatStat\" src=\"https://mvglasow.github.io/satstat/screenshots/satstat-gps.png\" width=\"240px\" />&nbsp;<img alt=\"Sensor view in SatStat\" src=\"https://mvglasow.github.io/satstat/screenshots/satstat-sensors.png\" width=\"240px\" />&nbsp;<img alt=\"Radio view in SatStat\" src=\"https://mvglasow.github.io/satstat/screenshots/satstat-radio.png\" width=\"240px\" />&nbsp;<img alt=\"Map view in SatStat with local tiles\" src=\"https://mvglasow.github.io/satstat/screenshots/satstat-map.png\" width=\"240px\" />&nbsp;<img alt=\"Map view in SatStat with OSM tiles\" src=\"https://mvglasow.github.io/satstat/screenshots/satstat-map2.png\" width=\"240px\" />&nbsp;<img alt=\"GPS notification in SatStat without fix\" src=\"https://mvglasow.github.io/satstat/screenshots/satstat-search.png\" width=\"240px\" />&nbsp;<img alt=\"GPS notification in SatStat with fix\" src=\"https://mvglasow.github.io/satstat/screenshots/satstat-fix.png\" width=\"240px\" />\n"
  },
  {
    "path": "build.gradle",
    "content": "buildscript {\n    repositories {\n        mavenCentral()\n    }\n\n    dependencies {\n        classpath 'com.android.tools.build:gradle:2.1.2'\n    }\n}\n\napply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion '25.0.0'\n\n    defaultConfig {\n        minSdkVersion 11\n        targetSdkVersion 23\n    }\n\n    packagingOptions {\n        exclude 'META-INF/LICENSE.txt'\n        exclude 'META-INF/NOTICE.txt'\n    }\n    \n    sourceSets {\n        main {\n            manifest.srcFile 'AndroidManifest.xml'\n            java.srcDirs = ['src']\n            resources.srcDirs = ['src']\n            aidl.srcDirs = ['src']\n            renderscript.srcDirs = ['src']\n            res.srcDirs = ['res']\n            assets.srcDirs = ['assets']\n        }\n    }\n    \n    lintOptions {\n        abortOnError false\n    }\n\n    repositories {\n        mavenCentral()\n        mavenLocal()\n    }\n\n    dependencies {\n        compile 'com.android.support:support-v4:23.2.1'\n        compile 'com.android.support:appcompat-v7:23.2.1'\n        compile 'com.android.support:design:23.2.1'\n        \n        compile 'commons-net:commons-net:3.5'\n\n        compile 'org.mapsforge:mapsforge-core:0.9.1'\n        compile 'org.mapsforge:mapsforge-map:0.9.1'\n        compile 'org.mapsforge:mapsforge-map-reader:0.9.1'\n        compile 'org.mapsforge:mapsforge-themes:0.9.1'\n        compile 'org.mapsforge:mapsforge-map-android:0.9.1'\n        compile 'com.caverock:androidsvg:1.2.2-beta-1'\n        compile 'net.sf.kxml:kxml2:2.3.0'\n    }\n}\n"
  },
  {
    "path": "metadata/en-US/changelogs/10.txt",
    "content": "Support for legacy (3.0+) Android versions (#1)\nWiFi now displays channels (#2)\nSky plot now aligned correctly when display is rotated (#4)\n"
  },
  {
    "path": "metadata/en-US/changelogs/20.txt",
    "content": "AGPS data download\nSupport for 5 GHz WiFI frequencies (#9)\nTab navigation in action bar in addition to swiping\nPrevent NPE when returning to main activity\nImproved sizing for sky plot to avoid pushing vies off the screen (#6)\n"
  },
  {
    "path": "metadata/en-US/changelogs/2000.txt",
    "content": "Map view: new view to visualize reported locations\nGPS view: display SNR bars for all known satellites (including Beidou and various SBAS systems)\nRadio cell view: add support for LTE on Android versions that support it\nRadio cell view: display neighboring cells correctly\nRadio cell view: use CellInfo on Android versions that support it\nRadio cell view: eliminate duplicate entries\nRadio cell view: show PSC for UMTS cells\nRadio cell view: display dashes instead of bogus values when data is missing\nNotifications: animate icon while searching for location\nNotifications: show correct TTFF\nNotifications: update content after losing fix\nAbout screen: show version\nWhen crashing, write crash log file\nFix cases in which automatic AGPS download would fail silently (#38)\nUpdate mobile network type even when no data connection is active (#30)\nFix screen orientation lock issue on landscape-default devices (#32)\nDo not crash upon receiving location updates with NaN coordinates (#35)\nDo not crash when radio is not connected\n"
  },
  {
    "path": "metadata/en-US/changelogs/2010.txt",
    "content": "Add option for metric vs. Imperial measurement units\nAdd option for UTC vs. local time for GPS timestamps\nAdd option to split UMTS/LTE cell IDs\nAdd options for various coordinate formats\nAdd Hungarian translations, thanks to kadarivan\nAdd legend for colors and symbols used\nSize graphics depending on display density\nIncrease expiration delay for location providers\nTarget API 19\nIntroduce Material LAF\nInclude notice in AGPS message that success cannot be reported\nRIL: use all available cell information sources\nPreserve state of main activity when returning from another\nRun media scanner on crash log files\nUse friendly date and time for crash log files\nRIL: display neighboring cells correctly\nRIL: show correct signal strength\nRIL: correctly separate LTE cells from GSM/UMTS cells\nFix Mapsforge crash\nFix color issue in Sensor view on Marshmallow\n"
  },
  {
    "path": "metadata/en-US/changelogs/2020.txt",
    "content": "Fix crashes when permissions are denied\nFix memory leak when stopping or destroying main Activity\n"
  },
  {
    "path": "metadata/en-US/changelogs/30.txt",
    "content": "Fix regression from 60b8bec pushing sky plot off screen in landscape\nLog satellites with near-zero elevation/azimuth\n"
  },
  {
    "path": "metadata/en-US/changelogs/3000.txt",
    "content": "Support for offline maps, which can be shared with other Mapsforge apps (#65)\nAdd option to show both UMTS/LTE cell ID representations (#77)\nOptionally show speed in knots (#74)\nLabel GNSS systems in satellite SNR bar graph (#64)\nDifferent options to sort WiFis (#44)\nAllow manual purging of map tile cache\nSwitch map tile provider from (discontinued) Mapquest to OSM (#86)\nFully support Android's new runtime permission model (#67)\nUpdate to Mapsforge 0.6.1, now with proper persistent caching\nLook and feel improvements\nFix faulty mph conversion in satellite view\nFix memory leak when rotating or leaving and re-entering map view\n"
  },
  {
    "path": "metadata/en-US/changelogs/3010.txt",
    "content": "Fix crash in Settings on API levels prior to 19 (#89)\nUpdate French translation (thanks eliovir)\nUse correct unit for speed in notification\n"
  },
  {
    "path": "metadata/en-US/changelogs/3020.txt",
    "content": "Update Portuguese translation\nFix crash in CDMA cell view\nFix nautic coordinate display\n"
  },
  {
    "path": "metadata/en-US/changelogs/3030.txt",
    "content": "Sky plot: make compass orientation work on devices with compass but no orientation sensor\nSky plot: add support for Galileo\nSky plot and notification: add support for UTM coordinates\nPreferences: include a hint on map download source\nMap download: keep notification after map download finishes\nMap download: adapt to new directory layout on Mapsforge mirror server\nMetadata: add feature graphics, icon and screenshots for F-Droid\nTranslations: updates for French and Hungarian\n\nBug fixes:\n\nSky plot: use Altitude consistently\nSky plot and notification: display negative coordinates correctly in non-decimal formats\nMap view: fix crash caused by a rare race condition\nMap download: do not crash if retrieving the map directory fails\nMap download: correctly display map sizes above 2 GiB\nMap download: display list correctly even after screen rotation\nAbout screen: make links clickable\n"
  },
  {
    "path": "metadata/en-US/changelogs/40.txt",
    "content": "GPS status notification whenever an app accesses GPS (configurable)\nAdd signal-to-noise graph for GPS/GLONASS satellites\nTranslations: Catalan, German, Spanish, French, Italian, Lithuanian\nAdd NESW marks to sky plot\nLock display rotation when device is in near-horizontal position\nUse sensor resolution to determine number of decimals displayed\nShow build number in About screen\nFix dormant sizing bug in sky plot\n"
  },
  {
    "path": "metadata/en-US/changelogs/50.txt",
    "content": "Update AGPS data when connecting to a WiFi network (configurable)\nPrevent crash when sensors report zero resolution\n"
  },
  {
    "path": "metadata/en-US/changelogs/60.txt",
    "content": "Prevent crash on AGPS update\n"
  },
  {
    "path": "metadata/en-US/changelogs/70.txt",
    "content": "Show cellular network type (#21)\nShow type of WiFi encryption (#20)\nAuto-refresh WiFi network list every second (#26)\nTouch WiFi network to pin it to the top of the list (#26)\nHide empty cell list in network view\nChoose from all available networks for AGPS auto-update (#18)\nCheck for captive portal before AGPS update attempt (#18)\nRepeat AGPS auto-update each time the interval expires if a selected network is available (#18)\nFix minor screen real estate issue in German translation (#23)\nPrevent crash upon switching to other activities (#27)\n"
  },
  {
    "path": "metadata/en-US/changelogs/80.txt",
    "content": "Fix time conversion error that could cause an endless AGPS update loop (#31)\nTypo in French translation\nShow TTFF in notification (experimental)\n"
  },
  {
    "path": "mkpng",
    "content": "#!/bin/bash\n\n## Generates PNG drawables from all SVG files.\n\nSVG_PATH=./extra\n\nLDPI_PATH=./res/drawable-ldpi\nMDPI_PATH=./res/drawable-mdpi\nHDPI_PATH=./res/drawable-hdpi\nXHDPI_PATH=./res/drawable-xhdpi\nXXHDPI_PATH=./res/drawable-xxhdpi\n\n# Android density bins are:\n# LDPI:   120 dpi\n# MDPI:   160 dpi\n# HDPI:   240 dpi\n# XHDPI:  320 dpi\n# XXHDPI: 480 dpi\n\n# Default for icons is 1 SVG user unit (\"px\") = 1 dp.\n# However, both these units have their own conflicting definitions:\n# - 1 SVG user units = 1 pixel at 90 dpi.\n# - 1 dp = 1 pixel at MDPI (160 dpi).\n# Therefore, SVG export needs to use the following dpi values:\n\nLDPI_RES=67.5\nMDPI_RES=90\nHDPI_RES=135\nXHDPI_RES=180\nXXHDPI_RES=270\n\n\n## Creates a single PNG file from a given input SVG file at the given DPI resolution.\n##\n## If the output file exists and is newer than the input file, no action is taken.\n##\n## @param $1 The input SVG filename (full path)\n## @param $2 The output PNG filename (full path)\n## @param $3 The resolution in DPI\nfunction make_png_dpi {\n\tif [[ -e $2 && $2 -nt $1 ]] ; then\n\t\techo Skipping $1 because $2 exists and is newer\n\telse\n\t\techo inkscape --without-gui --export-area-page --export-dpi=$3 --export-png=$2 $1\n\t\tinkscape --without-gui --export-area-page --export-dpi=$3 --export-png=$2 $1\n\tfi\n}\n\n\n## Creates a single PNG file from a given input SVG file at the given width.\n##\n## If the output file exists and is newer than the input file, no action is taken.\n##\n## @param $1 The input SVG filename (full path)\n## @param $2 The output PNG filename (full path)\n## @param $3 The width in pixels\nfunction make_png_width {\n\tif [[ -e $2 && $2 -nt $1 ]] ; then\n\t\techo Skipping $1 because $2 exists and is newer\n\telse\n\t\techo inkscape --without-gui --export-area-page --export-width=$3 --export-png=$2 $1\n\t\tinkscape --without-gui --export-area-page --export-width=$3 --export-png=$2 $1\n\tfi\n}\n\n\n## Converts a SVG icon to PNG drawables using the default of 1 px = 1 dip.\n##\n## @param $1 The input SVG filename (path stripped)\n## @param $2 The output PNG filename (path stripped)\nfunction convert_by_dpi {\n\tbaresvgfile=$1\n\tbarepngfile=$2\n\tmake_png_dpi $SVG_PATH/$baresvgfile $LDPI_PATH/$barepngfile $LDPI_RES\n\tmake_png_dpi $SVG_PATH/$baresvgfile $MDPI_PATH/$barepngfile $MDPI_RES\n\tmake_png_dpi $SVG_PATH/$baresvgfile $HDPI_PATH/$barepngfile $HDPI_RES\n\tmake_png_dpi $SVG_PATH/$baresvgfile $XHDPI_PATH/$barepngfile $XHDPI_RES\n\tmake_png_dpi $SVG_PATH/$baresvgfile $XXHDPI_PATH/$barepngfile $XXHDPI_RES\n}\n\n\n## Converts a SVG icon to PNG drawables using a specified width.\n##\n## @param $1 The input SVG filename (path stripped)\n## @param $2 The output PNG filename (path stripped)\n## @param $3 The width for the converted PNG files, in dip\nfunction convert_by_width {\n\tbaresvgfile=$1\n\tbarepngfile=$2\n    width_mdpi=$3\n\t# Derive other sizes from MDPI\n\t# Ordered to do lossy operations (integer division) last\n\twidth_xhdpi=$(($width_mdpi * 2))\n\twidth_xxhdpi=$(($width_mdpi * 3))\n\twidth_hdpi=$(($width_xxhdpi / 2))\n\twidth_ldpi=$(($width_hdpi / 2))\n\tmake_png_width $SVG_PATH/$baresvgfile $LDPI_PATH/$barepngfile $width_ldpi\n\tmake_png_width $SVG_PATH/$baresvgfile $MDPI_PATH/$barepngfile $width_mdpi\n\tmake_png_width $SVG_PATH/$baresvgfile $HDPI_PATH/$barepngfile $width_hdpi\n\tmake_png_width $SVG_PATH/$baresvgfile $XHDPI_PATH/$barepngfile $width_xhdpi\n\tmake_png_width $SVG_PATH/$baresvgfile $XXHDPI_PATH/$barepngfile $width_xxhdpi\n}\n\n\nfor svgfile in $SVG_PATH/*.svg\ndo\n\t# strip path from file name\n\tbaresvgfile=${svgfile#$SVG_PATH/}\n\tbarepngfile=${baresvgfile%.svg}.png\n\tcase $baresvgfile in\n\t\tic_context_marker_*.svg)\n\t\t\tconvert_by_width $baresvgfile $barepngfile 32\n\t\t\t;;\n\t\tic_launcher.svg|ic_launcher_oi_filemanager.svg)\n\t\t\tconvert_by_width $baresvgfile $barepngfile 48\n\t\t\t;;\n\t\t*)\n\t\t\tconvert_by_dpi $baresvgfile $barepngfile\n\tesac\ndone\n\n"
  },
  {
    "path": "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": "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-23\nandroid.library=false\nandroid.library.reference.1=../../workspaces/satstat/libprojects/appcompat\nandroid.library.reference.2=../../workspaces/satstat/libprojects/design\n"
  },
  {
    "path": "res/drawable/divider.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ImageView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  android:src=\"@android:drawable/divider_horizontal_dark\"\n  android:layout_width=\"fill_parent\" android:layout_height=\"wrap_content\"\n  android:scaleType=\"fitXY\" android:paddingLeft=\"5sp\"\n  android:paddingRight=\"5sp\" android:paddingBottom=\"2sp\" />\n"
  },
  {
    "path": "res/drawable/ic_stat_notify_nolocation.xml",
    "content": "<animation-list xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:oneshot=\"false\">\n    <item android:drawable=\"@drawable/ic_stat_notify_nolocation_m\" android:duration=\"160\" />\n    <item android:drawable=\"@drawable/ic_stat_notify_nolocation_lm\" android:duration=\"240\" />\n    <item android:drawable=\"@drawable/ic_stat_notify_nolocation_l\" android:duration=\"320\" />\n    <item android:drawable=\"@drawable/ic_stat_notify_nolocation_lm\" android:duration=\"240\" />\n    <item android:drawable=\"@drawable/ic_stat_notify_nolocation_m\" android:duration=\"160\" />\n    <item android:drawable=\"@drawable/ic_stat_notify_nolocation_mr\" android:duration=\"240\" />\n    <item android:drawable=\"@drawable/ic_stat_notify_nolocation_r\" android:duration=\"320\" />\n    <item android:drawable=\"@drawable/ic_stat_notify_nolocation_mr\" android:duration=\"240\" />\n</animation-list>\n"
  },
  {
    "path": "res/drawable/list_selector_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2008 The Android Open Source Project Licensed under the \n\tApache License, Version 2.0 (the \"License\"); you may not use this file except \n\tin compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 \n\tUnless required by applicable law or agreed to in writing, software distributed \n\tunder the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES \n\tOR CONDITIONS OF ANY KIND, either express or implied. See the License for \n\tthe specific language governing permissions and limitations under the License. -->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\t<item android:state_window_focused=\"false\" android:drawable=\"@android:color/transparent\" />\n\n\t<!-- Even though these two point to the same resource, have two states so \n\t\tthe drawable will invalidate itself when coming out of pressed state. -->\n\t<item android:state_focused=\"true\" android:state_enabled=\"false\"\n\t\tandroid:state_pressed=\"true\" android:drawable=\"@android:color/transparent\" />\n\t<item android:state_focused=\"true\" android:state_enabled=\"false\"\n\t\tandroid:drawable=\"@android:color/transparent\" />\n\n\t<item android:state_focused=\"true\" android:state_pressed=\"true\"\n\t\tandroid:drawable=\"@color/ripple_material_dark\" />\n\t<item android:state_focused=\"false\" android:state_pressed=\"true\"\n\t\tandroid:drawable=\"@color/ripple_material_dark\" />\n\n\t<item android:state_focused=\"true\"\n\t\tandroid:drawable=\"@color/ripple_material_dark\" />\n\n\t<!-- !!hack!! to fill StateListDrawable.mStateListState with exactly 10 \n\t\titems otherwise it will throw NPE on Android 1.6 -->\n\n\t<item android:animationCache=\"true\"\n\t\tandroid:drawable=\"@android:color/transparent\" />\n\n\t<item android:animationCache=\"false\"\n\t\tandroid:drawable=\"@android:color/transparent\" />\n\n\t<item android:alwaysDrawnWithCache=\"false\"\n\t\tandroid:drawable=\"@android:color/transparent\" />\n\n\t<item android:alwaysDrawnWithCache=\"true\"\n\t\tandroid:drawable=\"@android:color/transparent\" />\n\t\n\t\n</selector>"
  },
  {
    "path": "res/drawable/list_selector_background_transition.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<transition xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@color/ripple_material_dark\"  />\n    <item android:drawable=\"@color/ripple_material_dark\"  />\n</transition>\n"
  },
  {
    "path": "res/drawable/zoom_control_in.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/zoom_control_in_disabled\" android:state_enabled=\"false\" />\n    <item android:drawable=\"@drawable/zoom_control_in_pressed\" android:state_pressed=\"true\" />\n    <item android:drawable=\"@drawable/zoom_control_in_normal\" />\n\n</selector>"
  },
  {
    "path": "res/drawable/zoom_control_out.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/zoom_control_out_disabled\" android:state_enabled=\"false\" />\n    <item android:drawable=\"@drawable/zoom_control_out_pressed\" android:state_pressed=\"true\" />\n    <item android:drawable=\"@drawable/zoom_control_out_normal\" />\n\n</selector>"
  },
  {
    "path": "res/layout/activity_about.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".AboutActivity\" >\n\n    <ScrollView\n        android:id=\"@+id/scrollView1\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\" >\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:orientation=\"vertical\"\n            android:paddingBottom=\"@dimen/activity_vertical_margin\"\n            android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n            android:paddingRight=\"@dimen/activity_horizontal_margin\"\n            android:paddingTop=\"@dimen/activity_vertical_margin\" >\n\n            <TextView\n                android:id=\"@+id/aboutCaption\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/about_caption\" />\n\n            <TextView\n                android:id=\"@+id/aboutBuild\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:paddingBottom=\"@dimen/activity_vertical_margin\" />\n\n            <TextView\n                android:id=\"@+id/aboutText\"\n                android:autoLink=\"email|web\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\" />\n\n            </LinearLayout>\n    </ScrollView>\n\n</RelativeLayout>\n"
  },
  {
    "path": "res/layout/activity_legend.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".LegendActivity\" >\n\n    <ScrollView\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\" >\n\n        <LinearLayout\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\"\n            android:paddingBottom=\"@dimen/activity_vertical_margin\"\n            android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n            android:paddingRight=\"@dimen/activity_horizontal_margin\" >\n\n            <TextView\n                style=\"?android:attr/listSeparatorTextViewStyle\"\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_legend_gps\" />\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"vertical\" >\n\n                <!-- GPS legend items go here -->\n                <!-- TODO dividers between child layouts -->\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/legend_rowheight\"\n                    android:measureWithLargestChild=\"false\"\n                    android:orientation=\"horizontal\"\n                    android:weightSum=\"22\" >\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical|center\"\n                        android:layout_weight=\"3\"\n                        android:gravity=\"center_vertical|center\"\n                        android:text=\"@string/dot\"\n                        android:textAppearance=\"?android:attr/textAppearanceLarge\"\n                        android:textColor=\"@color/gpsUsed\" />\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical\"\n                        android:layout_weight=\"19\"\n                        android:gravity=\"center_vertical\"\n                        android:text=\"@string/title_legend_gps_used\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/legend_rowheight\"\n                    android:measureWithLargestChild=\"false\"\n                    android:orientation=\"horizontal\"\n                    android:weightSum=\"22\" >\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical|center\"\n                        android:layout_weight=\"3\"\n                        android:gravity=\"center_vertical|center\"\n                        android:text=\"@string/dot\"\n                        android:textAppearance=\"?android:attr/textAppearanceLarge\"\n                        android:textColor=\"@color/gpsUnused\" />\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical\"\n                        android:layout_weight=\"19\"\n                        android:gravity=\"center_vertical\"\n                        android:text=\"@string/title_legend_gps_unused\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n            </LinearLayout>\n\n            <TextView\n                style=\"?android:attr/listSeparatorTextViewStyle\"\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_legend_sensors\" />\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"vertical\" >\n\n                <!-- Sensor legend items go here -->\n                <!-- TODO dividers between child layouts -->\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/legend_rowheight\"\n                    android:measureWithLargestChild=\"false\"\n                    android:orientation=\"horizontal\"\n                    android:weightSum=\"22\" >\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical|center\"\n                        android:layout_weight=\"3\"\n                        android:gravity=\"center_vertical|center\"\n                        android:text=\"@string/dot\"\n                        android:textAppearance=\"?android:attr/textAppearanceLarge\"\n                        android:textColor=\"@color/accHigh\" />\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical\"\n                        android:layout_weight=\"19\"\n                        android:gravity=\"center_vertical\"\n                        android:text=\"@string/title_legend_sensors_high\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/legend_rowheight\"\n                    android:measureWithLargestChild=\"false\"\n                    android:orientation=\"horizontal\"\n                    android:weightSum=\"22\" >\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical|center\"\n                        android:layout_weight=\"3\"\n                        android:gravity=\"center_vertical|center\"\n                        android:text=\"@string/dot\"\n                        android:textAppearance=\"?android:attr/textAppearanceLarge\"\n                        android:textColor=\"@color/accMedium\" />\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical\"\n                        android:layout_weight=\"19\"\n                        android:gravity=\"center_vertical\"\n                        android:text=\"@string/title_legend_sensors_medium\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/legend_rowheight\"\n                    android:measureWithLargestChild=\"false\"\n                    android:orientation=\"horizontal\"\n                    android:weightSum=\"22\" >\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical|center\"\n                        android:layout_weight=\"3\"\n                        android:gravity=\"center_vertical|center\"\n                        android:text=\"@string/dot\"\n                        android:textAppearance=\"?android:attr/textAppearanceLarge\"\n                        android:textColor=\"@color/accLow\" />\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical\"\n                        android:layout_weight=\"19\"\n                        android:gravity=\"center_vertical\"\n                        android:text=\"@string/title_legend_sensors_low\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/legend_rowheight\"\n                    android:measureWithLargestChild=\"false\"\n                    android:orientation=\"horizontal\"\n                    android:weightSum=\"22\" >\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical|center\"\n                        android:layout_weight=\"3\"\n                        android:gravity=\"center_vertical|center\"\n                        android:text=\"@string/dot\"\n                        android:textAppearance=\"?android:attr/textAppearanceLarge\"\n                        android:textColor=\"@color/accUnreliable\" />\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical\"\n                        android:layout_weight=\"19\"\n                        android:gravity=\"center_vertical\"\n                        android:text=\"@string/title_legend_sensors_unreliable\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n            </LinearLayout>\n\n            <TextView\n                style=\"?android:attr/listSeparatorTextViewStyle\"\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_legend_radio\" />\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"vertical\" >\n\n                <!-- Radio legend items go here -->\n                <!-- TODO dividers between child layouts -->\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/legend_rowheight\"\n                    android:measureWithLargestChild=\"false\"\n                    android:orientation=\"horizontal\"\n                    android:weightSum=\"22\" >\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical|center\"\n                        android:layout_weight=\"3\"\n                        android:gravity=\"center_vertical|center\"\n                        android:text=\"@string/dot\"\n                        android:textAppearance=\"?android:attr/textAppearanceLarge\"\n                        android:textColor=\"@color/gen2\" />\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical\"\n                        android:layout_weight=\"19\"\n                        android:gravity=\"center_vertical\"\n                        android:text=\"@string/title_legend_radio_2g\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/legend_rowheight\"\n                    android:measureWithLargestChild=\"false\"\n                    android:orientation=\"horizontal\"\n                    android:weightSum=\"22\" >\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical|center\"\n                        android:layout_weight=\"3\"\n                        android:gravity=\"center_vertical|center\"\n                        android:text=\"@string/dot\"\n                        android:textAppearance=\"?android:attr/textAppearanceLarge\"\n                        android:textColor=\"@color/gen3\" />\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical\"\n                        android:layout_weight=\"19\"\n                        android:gravity=\"center_vertical\"\n                        android:text=\"@string/title_legend_radio_3g\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/legend_rowheight\"\n                    android:measureWithLargestChild=\"false\"\n                    android:orientation=\"horizontal\"\n                    android:weightSum=\"22\" >\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical|center\"\n                        android:layout_weight=\"3\"\n                        android:gravity=\"center_vertical|center\"\n                        android:text=\"@string/dot\"\n                        android:textAppearance=\"?android:attr/textAppearanceLarge\"\n                        android:textColor=\"@color/gen4\" />\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical\"\n                        android:layout_weight=\"19\"\n                        android:gravity=\"center_vertical\"\n                        android:text=\"@string/title_legend_radio_4g\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/legend_rowheight\"\n                    android:measureWithLargestChild=\"false\"\n                    android:orientation=\"horizontal\"\n                    android:weightSum=\"22\" >\n\n                    <ImageView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical|center\"\n                        android:layout_weight=\"3\"\n                        android:scaleType=\"center\"\n                        android:src=\"@drawable/ic_content_wifi_open\" />\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical\"\n                        android:layout_weight=\"19\"\n                        android:gravity=\"center_vertical\"\n                        android:text=\"@string/title_legend_radio_open\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/legend_rowheight\"\n                    android:measureWithLargestChild=\"false\"\n                    android:orientation=\"horizontal\"\n                    android:weightSum=\"22\" >\n\n                    <ImageView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical|center\"\n                        android:layout_weight=\"3\"\n                        android:scaleType=\"center\"\n                        android:src=\"@drawable/ic_content_wifi_wep\" />\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical\"\n                        android:layout_weight=\"19\"\n                        android:gravity=\"center_vertical\"\n                        android:text=\"@string/title_legend_radio_wep\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/legend_rowheight\"\n                    android:measureWithLargestChild=\"false\"\n                    android:orientation=\"horizontal\"\n                    android:weightSum=\"22\" >\n\n                    <ImageView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical|center\"\n                        android:layout_weight=\"3\"\n                        android:scaleType=\"center\"\n                        android:src=\"@drawable/ic_content_wifi_psk\" />\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical\"\n                        android:layout_weight=\"19\"\n                        android:gravity=\"center_vertical\"\n                        android:text=\"@string/title_legend_radio_psk\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/legend_rowheight\"\n                    android:measureWithLargestChild=\"false\"\n                    android:orientation=\"horizontal\"\n                    android:weightSum=\"22\" >\n\n                    <ImageView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical|center\"\n                        android:layout_weight=\"3\"\n                        android:scaleType=\"center\"\n                        android:src=\"@drawable/ic_content_wifi_eap\" />\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical\"\n                        android:layout_weight=\"19\"\n                        android:gravity=\"center_vertical\"\n                        android:text=\"@string/title_legend_radio_eap\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/legend_rowheight\"\n                    android:measureWithLargestChild=\"false\"\n                    android:orientation=\"horizontal\"\n                    android:weightSum=\"22\" >\n\n                    <ImageView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical|center\"\n                        android:layout_weight=\"3\"\n                        android:scaleType=\"center\"\n                        android:src=\"@drawable/ic_content_wifi_adhoc\" />\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical\"\n                        android:layout_weight=\"19\"\n                        android:gravity=\"center_vertical\"\n                        android:text=\"@string/title_legend_radio_adhoc\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"@dimen/legend_rowheight\"\n                    android:measureWithLargestChild=\"false\"\n                    android:orientation=\"horizontal\"\n                    android:weightSum=\"22\" >\n\n                    <ImageView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical|center\"\n                        android:layout_weight=\"3\"\n                        android:scaleType=\"center\"\n                        android:src=\"@drawable/ic_content_wifi_unknown\" />\n\n                    <TextView\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"match_parent\"\n                        android:layout_gravity=\"center_vertical\"\n                        android:layout_weight=\"19\"\n                        android:gravity=\"center_vertical\"\n                        android:text=\"@string/title_legend_radio_unknown\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n            </LinearLayout>\n\n            <TextView\n                style=\"?android:attr/listSeparatorTextViewStyle\"\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_legend_map\" />\n\n            <LinearLayout\n                android:id=\"@+id/legendMapContainer\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"vertical\" >\n\n                <!-- Map legend items go here (added dynamically) -->\n\n            </LinearLayout>\n        </LinearLayout>\n    </ScrollView>\n\n</RelativeLayout>"
  },
  {
    "path": "res/layout/activity_main.xml",
    "content": "<com.vonglasow.michael.satstat.widgets.MapViewPager xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/pager\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:keepScreenOn=\"true\"\n    tools:context=\".MainActivity\" >\n\n</com.vonglasow.michael.satstat.widgets.MapViewPager>\n"
  },
  {
    "path": "res/layout/activity_map_download.xml",
    "content": "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    tools:context=\"${relativePackage}.${activityClass}\" >\n\n    <ProgressBar\n        android:id=\"@+id/downloadProgress\"\n        style=\"?android:attr/progressBarStyleLarge\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical|center_horizontal\"\n        android:visibility=\"gone\" />\n\n    <LinearLayout\n        android:id=\"@+id/downloadErrorLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:orientation=\"vertical\" >\n\n        <TextView\n            android:id=\"@+id/downloadError\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center_horizontal\"\n            android:gravity=\"center_horizontal\"\n            android:text=\"@string/download_error\"\n            android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n        <Button\n            android:id=\"@+id/downloadRetry\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center_horizontal\"\n            android:text=\"@string/download_retry\" />\n\n    </LinearLayout>\n\n    <pl.polidea.treeview.TreeViewList\n        android:id=\"@+id/downloadList\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:divider=\"?android:attr/listDivider\"\n        android:dividerHeight=\"1dp\"\n        android:headerDividersEnabled=\"false\"\n        android:listSelector=\"@android:color/transparent\"\n        android:scrollbars=\"vertical\" />\n\n</FrameLayout>"
  },
  {
    "path": "res/layout/activity_settings.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\".SettingsActivity\" >\n\n</RelativeLayout>\n"
  },
  {
    "path": "res/layout/alert_map_path.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\" >\n    \n<LinearLayout\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:orientation=\"vertical\" >\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/map_path_title\"\n        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n    \n    <EditText\n        android:id=\"@+id/editPath\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:inputType=\"text\" >\n    </EditText>\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/map_path_fileman\"\n        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n    <LinearLayout\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal\"\n        android:gravity=\"center_horizontal\" >\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\" >\n\n            <ImageButton\n                android:id=\"@+id/btn_oi_filemanager\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"top|center_horizontal\"\n                android:src=\"@drawable/ic_launcher_oi_filemanager\" />\n\n            <TextView\n                android:id=\"@+id/textView2\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"top|center_horizontal\"\n                android:gravity=\"center_horizontal\"\n                android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n                android:paddingRight=\"@dimen/activity_horizontal_margin\"\n                android:text=\"@string/map_path_oi_filemanager\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\" >\n\n            <ImageButton\n                android:id=\"@+id/btn_cm_filemanager\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"top|center_horizontal\"\n                android:src=\"@drawable/ic_launcher_cmfilemanager\" />\n\n            <TextView\n                android:id=\"@+id/textView3\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"top|center_horizontal\"\n                android:gravity=\"center_horizontal\"\n                android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n                android:paddingRight=\"@dimen/activity_horizontal_margin\"\n                android:text=\"@string/map_path_cm_filemanager\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n        </LinearLayout>\n\n    </LinearLayout>\n\n</LinearLayout>\n</ScrollView>"
  },
  {
    "path": "res/layout/download_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=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"horizontal\" >\n\n    <LinearLayout\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:layout_weight=\"1\"\n        android:orientation=\"vertical\" >\n\n        <TextView\n            android:id=\"@+id/downloadListItem\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:text=\"TextView\"\n            android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:weightSum=\"15\" >\n\n            <TextView\n                android:id=\"@+id/downloadSize\"\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"6\"\n                android:text=\"Medium Text\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/downloadDate\"\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"9\"\n                android:text=\"Medium Text\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n        </LinearLayout>\n\n        <ProgressBar\n            android:id=\"@+id/downloadFileProgress\"\n            style=\"?android:attr/progressBarStyleHorizontal\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"top\"\n            android:max=\"10\"\n            android:maxHeight=\"8dp\"\n            android:minHeight=\"8dp\"\n            android:paddingBottom=\"0dp\"\n            android:paddingTop=\"0dp\"\n            android:progress=\"5\" />\n\n    </LinearLayout>\n\n    <ProgressBar\n        android:id=\"@+id/downloadDirProgress\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\" />\n\n    <ImageView\n        android:id=\"@+id/downloadIcon\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:layout_marginBottom=\"2dp\"\n        android:paddingBottom=\"12dp\"\n        android:paddingLeft=\"12dp\"\n        android:paddingRight=\"12dp\"\n        android:paddingTop=\"12dp\"\n        android:src=\"@drawable/ic_file_download\" />\n\n    <ImageButton\n        android:id=\"@+id/downloadCancel\"\n        android:layout_width=\"48dp\"\n        android:layout_height=\"48dp\"\n        android:layout_gravity=\"center_vertical\"\n        android:layout_marginBottom=\"2dp\"\n        android:src=\"@drawable/ic_cancel\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "res/layout/fragment_main_dummy.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\".MainActivity$DummySectionFragment\" >\n\n    <TextView\n        android:id=\"@+id/section_label\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\" />\n\n</RelativeLayout>\n"
  },
  {
    "path": "res/layout/fragment_main_gps.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/gpsRootLayout\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:orientation=\"vertical\" >\n\n    <LinearLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:baselineAligned=\"false\"\n        android:measureWithLargestChild=\"true\"\n        android:weightSum=\"24\" >\n\n        <LinearLayout\n            android:id=\"@+id/gpsLatLayout\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"9\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView2\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"bottom\"\n                android:text=\"@string/title_gps_lat\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/gpsLat\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:maxLines=\"1\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:id=\"@+id/gpsLonLayout\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"10\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView4\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"bottom\"\n                android:text=\"@string/title_gps_lon\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/gpsLon\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:maxLines=\"1\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:id=\"@+id/gpsCoordLayout\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"19\"\n            android:orientation=\"vertical\"\n            android:visibility=\"gone\" >\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"bottom\"\n                android:text=\"@string/title_gps_coord\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/gpsCoord\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:maxLines=\"1\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"5\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView8\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"bottom\"\n                android:maxLines=\"1\"\n                android:text=\"@string/title_gps_alt\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:weightSum=\"8\" >\n\n                <TextView\n                    android:id=\"@+id/gpsAlt\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:maxLines=\"1\"\n                    android:text=\"@string/value_none\"\n                    android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n                <TextView\n                    android:id=\"@+id/gpsAltUnit\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:gravity=\"bottom\"\n                    android:lines=\"1\"\n                    android:text=\"\"\n                    android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n            </LinearLayout>\n        </LinearLayout>\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:baselineAligned=\"true\"\n        android:measureWithLargestChild=\"true\"\n        android:weightSum=\"24\" >\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"9\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView12\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"bottom\"\n                android:lines=\"1\"\n                android:text=\"@string/title_gps_bear\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:weightSum=\"9\" >\n\n                <TextView\n                    android:id=\"@+id/gpsBearing\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_weight=\"4.5\"\n                    android:maxLines=\"1\"\n                    android:text=\"@string/value_none\"\n                    android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n                <TextView\n                    android:id=\"@+id/gpsOrientation\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_weight=\"4.5\"\n                    android:maxLines=\"1\"\n                    android:text=\"@string/value_none\"\n                    android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n            </LinearLayout>\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"6\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView6\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"bottom\"\n                android:lines=\"1\"\n                android:text=\"@string/title_gps_speed\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:weightSum=\"8\" >\n\n                <TextView\n                    android:id=\"@+id/gpsSpeed\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:maxLines=\"1\"\n                    android:text=\"@string/value_none\"\n                    android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n                <TextView\n                    android:id=\"@+id/gpsSpeedUnit\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:gravity=\"bottom\"\n                    android:lines=\"1\"\n                    android:text=\"\"\n                    android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n            </LinearLayout>\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"4\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView16\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"bottom\"\n                android:lines=\"1\"\n                android:text=\"@string/title_gps_acc\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:weightSum=\"8\" >\n\n                <TextView\n                    android:id=\"@+id/gpsAccuracy\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:maxLines=\"1\"\n                    android:text=\"@string/value_none\"\n                    android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n                <TextView\n                    android:id=\"@+id/gpsAccuracyUnit\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:gravity=\"bottom\"\n                    android:lines=\"1\"\n                    android:text=\"\"\n                    android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n            </LinearLayout>\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"5\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView17\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"bottom\"\n                android:lines=\"1\"\n                android:text=\"@string/title_gps_sats\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/gpsSats\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:maxLines=\"1\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n        </LinearLayout>\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:baselineAligned=\"true\"\n        android:measureWithLargestChild=\"true\"\n        android:weightSum=\"24\" >\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"4.5\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView32\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"bottom\"\n                android:lines=\"1\"\n                android:text=\"@string/title_gps_decl\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/orDeclination\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:maxLines=\"1\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"4.5\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView19\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"bottom\"\n                android:lines=\"1\"\n                android:text=\"@string/title_gps_ttff\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/gpsTtff\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:maxLines=\"1\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"15\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView10\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"bottom\"\n                android:lines=\"1\"\n                android:text=\"@string/title_gps_time\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/gpsTime\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:maxLines=\"1\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n        </LinearLayout>\n\n    </LinearLayout>\n\n    <com.vonglasow.michael.satstat.widgets.GpsSnrView\n        android:id=\"@+id/gpsSnrView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\" />\n\n</LinearLayout>"
  },
  {
    "path": "res/layout/fragment_main_map.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:layout_marginRight=\"12dp\"\n    android:layout_marginTop=\"12dp\"\n    android:orientation=\"vertical\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <android.support.design.widget.FloatingActionButton\n        android:id=\"@+id/mapReattach\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"top|right\"\n        android:src=\"@drawable/ic_btn_reattach\"\n        app:fabSize=\"normal\"\n        app:useCompatPadding=\"true\"\n        app:backgroundTint=\"@color/accent\" />\n\n    \t<!--\n        android:background=\"#AA000000\"\n        android:padding=\"3dp\"\n        -->\n\n    <TextView\n        android:id=\"@+id/mapAttribution\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"bottom|center\"\n        android:background=\"@color/halo\"\n        android:text=\"@string/osm_attribution\"\n        android:textAppearance=\"?android:attr/textAppearanceSmallInverse\"\n        android:textSize=\"10dp\" />\n    \n</FrameLayout>\n"
  },
  {
    "path": "res/layout/fragment_main_radio.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" >\n\n<LinearLayout\n    android:id=\"@+id/RootLayout\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\" >\n\n    <LinearLayout\n        android:id=\"@+id/rilLteLayout\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\" >\n\n        <TextView\n            android:id=\"@+id/TextView06\"\n            style=\"?android:attr/listSeparatorTextViewStyle\"\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/title_radio_lte\" />\n\n        <LinearLayout\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:baselineAligned=\"true\"\n            android:measureWithLargestChild=\"false\"\n            android:weightSum=\"29\" >\n\n            <LinearLayout\n                android:layout_width=\"0dp\"\n                android:layout_height=\"fill_parent\"\n                android:layout_weight=\"2\"\n                android:orientation=\"vertical\" >\n\n                <TextView\n                    android:id=\"@+id/TextView02\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"0dp\"\n                android:layout_height=\"fill_parent\"\n                android:layout_weight=\"3\"\n                android:orientation=\"vertical\" >\n\n                <TextView\n                    android:id=\"@+id/TextView04\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/title_radio_mcc\"\n                    android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"0dp\"\n                android:layout_height=\"fill_parent\"\n                android:layout_weight=\"3\"\n                android:orientation=\"vertical\"\n                android:weightSum=\"3\" >\n\n                <TextView\n                    android:id=\"@+id/TextView01\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/title_radio_mnc\"\n                    android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"0dp\"\n                android:layout_height=\"fill_parent\"\n                android:layout_weight=\"5\"\n                android:orientation=\"vertical\" >\n\n                <TextView\n                    android:id=\"@+id/TextView05\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/title_radio_tac\"\n                    android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"0dp\"\n                android:layout_height=\"fill_parent\"\n                android:layout_weight=\"9\"\n                android:orientation=\"vertical\" >\n\n                <TextView\n                    android:id=\"@+id/TextView03\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/title_radio_cellid\"\n                    android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"0dp\"\n                android:layout_height=\"fill_parent\"\n                android:layout_weight=\"3\"\n                android:orientation=\"vertical\" >\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/title_radio_pci\"\n                    android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"0dp\"\n                android:layout_height=\"fill_parent\"\n                android:layout_weight=\"4\"\n                android:orientation=\"vertical\" >\n\n                <TextView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/title_radio_level\"\n                    android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n            </LinearLayout>\n        </LinearLayout>\n\n        <TableLayout\n            android:id=\"@+id/rilLteCells\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:divider=\"?android:attr/listDivider\"\n            android:showDividers=\"beginning|middle\" >\n\n        </TableLayout>\n    </LinearLayout>\n\n\t<LinearLayout\n\t    android:id=\"@+id/rilGsmLayout\"\n\t    android:layout_width=\"fill_parent\"\n\t    android:layout_height=\"wrap_content\"\n\t    android:orientation=\"vertical\" >\n\t\n\t    <TextView\n\t        android:id=\"@+id/rotHeader\"\n\t        style=\"?android:attr/listSeparatorTextViewStyle\"\n\t        android:layout_width=\"fill_parent\"\n\t        android:layout_height=\"wrap_content\"\n\t        android:text=\"@string/title_radio_gsm\" />\n\t\n\t    <LinearLayout\n\t        android:layout_width=\"fill_parent\"\n\t        android:layout_height=\"wrap_content\"\n\t        android:baselineAligned=\"true\"\n\t        android:measureWithLargestChild=\"false\"\n\t        android:weightSum=\"29\" >\n\t\n\t        <LinearLayout\n\t            android:layout_width=\"0dp\"\n\t            android:layout_height=\"fill_parent\"\n\t            android:layout_weight=\"2\"\n\t            android:orientation=\"vertical\" >\n\t\n\t            <TextView\n\t                android:id=\"@+id/textView190\"\n\t                android:layout_width=\"wrap_content\"\n\t                android:layout_height=\"wrap_content\"\n\t                android:text=\"\"\n\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\n\t        </LinearLayout>\n\t\n\t        <LinearLayout\n\t            android:layout_width=\"0dp\"\n\t            android:layout_height=\"fill_parent\"\n\t            android:layout_weight=\"3\"\n\t            android:orientation=\"vertical\" >\n\t\n\t            <TextView\n\t                android:id=\"@+id/textView19\"\n\t                android:layout_width=\"wrap_content\"\n\t                android:layout_height=\"wrap_content\"\n\t                android:text=\"@string/title_radio_mcc\"\n\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\n\t        </LinearLayout>\n\t\n\t        <LinearLayout\n\t            android:layout_width=\"0dp\"\n\t            android:layout_height=\"fill_parent\"\n\t            android:layout_weight=\"3\"\n\t            android:orientation=\"vertical\"\n\t            android:weightSum=\"3\" >\n\t\n\t            <TextView\n\t                android:id=\"@+id/textView21\"\n\t                android:layout_width=\"wrap_content\"\n\t                android:layout_height=\"wrap_content\"\n\t                android:text=\"@string/title_radio_mnc\"\n\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\n\t        </LinearLayout>\n\t        \n\t        <LinearLayout\n\t            android:layout_width=\"0dp\"\n\t            android:layout_height=\"fill_parent\"\n\t            android:layout_weight=\"5\"\n\t            android:orientation=\"vertical\" >\n\t\n\t            <TextView\n\t                android:id=\"@+id/textView3\"\n\t                android:layout_width=\"wrap_content\"\n\t                android:layout_height=\"wrap_content\"\n\t                android:text=\"@string/title_radio_lac\"\n\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\n\t        </LinearLayout>\n\t        \n\t        <LinearLayout\n\t            android:layout_width=\"0dp\"\n\t            android:layout_height=\"fill_parent\"\n\t            android:layout_weight=\"9\"\n\t            android:orientation=\"vertical\" >\n\t\n\t            <TextView\n\t                android:id=\"@+id/textView23\"\n\t                android:layout_width=\"wrap_content\"\n\t                android:layout_height=\"wrap_content\"\n\t                android:text=\"@string/title_radio_cellid\"\n\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\n\t        </LinearLayout>\n\n\t        <LinearLayout\n\t            android:layout_width=\"0dp\"\n\t            android:layout_height=\"fill_parent\"\n\t            android:layout_weight=\"3\"\n\t            android:orientation=\"vertical\" >\n\n\t            <TextView\n\t                android:layout_width=\"wrap_content\"\n\t                android:layout_height=\"wrap_content\"\n\t                android:text=\"@string/title_radio_psc\"\n\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t        </LinearLayout>\n\t\n\t        <LinearLayout\n\t            android:layout_width=\"0dp\"\n\t            android:layout_height=\"fill_parent\"\n\t            android:layout_weight=\"4\"\n\t            android:orientation=\"vertical\" >\n\t\n\t            <TextView\n\t                android:layout_width=\"wrap_content\"\n\t                android:layout_height=\"wrap_content\"\n\t                android:text=\"@string/title_radio_level\"\n\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\n\t        </LinearLayout>\n\t\n\t        </LinearLayout>\n\t\n\t    <TableLayout\n\t        android:id=\"@+id/rilCells\"\n\t        android:layout_width=\"match_parent\"\n\t        android:layout_height=\"wrap_content\"\n\t        android:divider=\"?android:attr/listDivider\"\n\t        android:showDividers=\"beginning|middle\" >\n\t\n\t    </TableLayout>\n\t\n\t</LinearLayout>\n\n\t<LinearLayout\n\t    android:id=\"@+id/rilCdmaLayout\"\n\t    android:layout_width=\"fill_parent\"\n\t    android:layout_height=\"wrap_content\"\n\t    android:orientation=\"vertical\">\n\t    \n\t    <TextView\n\t        android:id=\"@+id/textView5\"\n\t        style=\"?android:attr/listSeparatorTextViewStyle\"\n\t        android:layout_width=\"fill_parent\"\n\t        android:layout_height=\"wrap_content\"\n\t        android:text=\"@string/title_radio_cdma\" />\n\t\n\t    <LinearLayout\n\t        android:layout_width=\"fill_parent\"\n\t        android:layout_height=\"wrap_content\"\n\t        android:baselineAligned=\"true\"\n\t        android:measureWithLargestChild=\"false\"\n\t        android:weightSum=\"26\" >\n\t\n\t        <LinearLayout\n\t            android:layout_width=\"0dp\"\n\t            android:layout_height=\"fill_parent\"\n\t            android:layout_weight=\"2\"\n\t            android:orientation=\"vertical\" >\n\t\n\t            <TextView\n\t                android:id=\"@+id/textView191\"\n\t                android:layout_width=\"wrap_content\"\n\t                android:layout_height=\"wrap_content\"\n\t                android:text=\"\"\n\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\n\t        </LinearLayout>\n\t\n\t        <LinearLayout\n\t            android:layout_width=\"0dp\"\n\t            android:layout_height=\"fill_parent\"\n\t            android:layout_weight=\"6\"\n\t            android:orientation=\"vertical\" >\n\t\n\t            <TextView\n\t                android:id=\"@+id/textView22\"\n\t                android:layout_width=\"wrap_content\"\n\t                android:layout_height=\"wrap_content\"\n\t                android:text=\"@string/title_radio_sid\"\n\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\n\t        </LinearLayout>\n\t\n\t        <LinearLayout\n\t            android:layout_width=\"0dp\"\n\t            android:layout_height=\"fill_parent\"\n\t            android:layout_weight=\"5\"\n\t            android:orientation=\"vertical\">\n\t\n\t            <TextView\n\t                android:id=\"@+id/textView25\"\n\t                android:layout_width=\"wrap_content\"\n\t                android:layout_height=\"wrap_content\"\n\t                android:text=\"@string/title_radio_nid\"\n\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\n\t        </LinearLayout>\n\t\n\t        <LinearLayout\n\t            android:layout_width=\"0dp\"\n\t            android:layout_height=\"fill_parent\"\n\t            android:layout_weight=\"9\"\n\t            android:orientation=\"vertical\" >\n\t\n\t            <TextView\n\t                android:id=\"@+id/textView24\"\n\t                android:layout_width=\"wrap_content\"\n\t                android:layout_height=\"wrap_content\"\n\t                android:text=\"@string/title_radio_bsid\"\n\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\n\t        </LinearLayout>\n\t\n\t        <LinearLayout\n\t            android:layout_width=\"0dp\"\n\t            android:layout_height=\"fill_parent\"\n\t            android:layout_weight=\"4\"\n\t            android:orientation=\"vertical\" >\n\t\n\t            <TextView\n\t                android:layout_width=\"wrap_content\"\n\t                android:layout_height=\"wrap_content\"\n\t                android:text=\"@string/title_radio_level\"\n\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\n\t        </LinearLayout>\n\t\n\t        </LinearLayout>\n\t\n\t    <TableLayout\n\t        android:id=\"@+id/rilCdmaCells\"\n\t        android:layout_width=\"match_parent\"\n\t        android:layout_height=\"wrap_content\"\n\t        android:divider=\"?android:attr/listDivider\"\n\t        android:showDividers=\"beginning|middle\" >\n\t\n\t    </TableLayout>\n\t</LinearLayout>\n    \n    <TextView\n        android:id=\"@+id/textView4\"\n        style=\"?android:attr/listSeparatorTextViewStyle\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/title_radio_wifi\" />\n\n    <LinearLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:baselineAligned=\"true\"\n        android:measureWithLargestChild=\"false\"\n        android:weightSum=\"22\" >\n\n        <TextView\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"3\"\n            android:text=\"\"\n            android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n        <TextView\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"14\"\n            android:text=\"@string/title_radio_mac\"\n            android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n        <TextView\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"2\"\n            android:text=\"@string/title_radio_channel\"\n            android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"3\"\n                android:text=\"@string/title_radio_level\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n        </LinearLayout>\n\n    <LinearLayout\n        android:id=\"@+id/wifiAps\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:divider=\"?android:attr/listDivider\"\n        android:orientation=\"vertical\"\n        android:showDividers=\"beginning|middle\" >\n\n    </LinearLayout>\n    \n</LinearLayout>\n</ScrollView>"
  },
  {
    "path": "res/layout/fragment_main_sensors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" >\n\n<LinearLayout\n    android:id=\"@+id/RootLayout\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\" >\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\" >\n\n        <TextView\n            android:id=\"@+id/accStatus\"\n            style=\"?android:attr/listSeparatorTextViewStyle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/dotSpace\"\n            android:textColor=\"@android:color/transparent\" />\n\n        <TextView\n            android:id=\"@+id/accHeader\"\n            style=\"?android:attr/listSeparatorTextViewStyle\"\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/title_sensor_acc\" />\n\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:baselineAligned=\"true\"\n        android:measureWithLargestChild=\"false\"\n        android:weightSum=\"4\" >\n\n        <LinearLayout\n            android:id=\"@+id/accAccuracyContainer\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"1\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView3\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_accTotal\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/accTotal\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:maxLines=\"1\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"1\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView19\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_accX\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/accX\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:maxLines=\"1\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"1\"\n            android:orientation=\"vertical\"\n            android:weightSum=\"3\" >\n\n            <TextView\n                android:id=\"@+id/textView21\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_accY\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/accY\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:maxLines=\"1\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"1\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView23\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_accZ\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/accZ\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:maxLines=\"1\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n        </LinearLayout>\n\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\" >\n\n        <TextView\n            android:id=\"@+id/rotStatus\"\n            style=\"?android:attr/listSeparatorTextViewStyle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/dotSpace\"\n            android:textColor=\"@android:color/transparent\" />\n\n        <TextView\n            android:id=\"@+id/rotHeader\"\n            style=\"?android:attr/listSeparatorTextViewStyle\"\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/title_sensor_rot\" />\n\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:baselineAligned=\"true\"\n        android:measureWithLargestChild=\"false\"\n        android:weightSum=\"4\" >\n\n        <LinearLayout\n            android:id=\"@+id/rotAccuracyContainer\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"1\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView36\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_rotTotal\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/rotTotal\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:maxLines=\"1\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"1\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView33\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_rotX\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/rotX\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:maxLines=\"1\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"1\"\n            android:orientation=\"vertical\"\n            android:weightSum=\"3\" >\n\n            <TextView\n                android:id=\"@+id/textView34\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_rotY\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/rotY\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:maxLines=\"1\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"1\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView35\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_rotZ\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/rotZ\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:maxLines=\"1\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n        </LinearLayout>\n\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\" >\n\n        <TextView\n            android:id=\"@+id/magStatus\"\n            style=\"?android:attr/listSeparatorTextViewStyle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/dotSpace\"\n            android:textColor=\"@android:color/transparent\" />\n\n        <TextView\n            android:id=\"@+id/magHeader\"\n            style=\"?android:attr/listSeparatorTextViewStyle\"\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/title_sensor_mag\" />\n\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:baselineAligned=\"true\"\n        android:measureWithLargestChild=\"false\"\n        android:weightSum=\"4\" >\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"1\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView24\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_magTotal\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/magTotal\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:maxLines=\"1\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"1\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView25\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_magX\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/magX\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:maxLines=\"1\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"1\"\n            android:orientation=\"vertical\"\n            android:weightSum=\"3\" >\n\n            <TextView\n                android:id=\"@+id/textView26\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_magY\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/magY\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:maxLines=\"1\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"1\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView27\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_magZ\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/magZ\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:maxLines=\"1\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n        </LinearLayout>\n\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\" >\n\n        <TextView\n            android:id=\"@+id/orStatus\"\n            style=\"?android:attr/listSeparatorTextViewStyle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/dotSpace\"\n            android:textColor=\"@android:color/transparent\" />\n\n        <TextView\n            android:id=\"@+id/orHeader\"\n            style=\"?android:attr/listSeparatorTextViewStyle\"\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/title_sensor_or\" />\n\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:baselineAligned=\"true\"\n        android:measureWithLargestChild=\"false\"\n        android:weightSum=\"4\" >\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"2\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView28\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_orAzimuth\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:weightSum=\"2\" >\n\n                <TextView\n                    android:id=\"@+id/orAzimuth\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_weight=\"1\"\n                    android:text=\"@string/value_none\"\n                    android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n                <TextView\n                    android:id=\"@+id/orAziText\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_weight=\"1\"\n                    android:text=\"@string/value_none\"\n                    android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n            </LinearLayout>\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"1\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView29\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_orPitch\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/orPitch\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"1\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/textView30\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_orRoll\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <TextView\n                android:id=\"@+id/orRoll\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n        </LinearLayout>\n\n    </LinearLayout>\n\n    <TextView\n        android:id=\"@+id/miscHeader\"\n        style=\"?android:attr/listSeparatorTextViewStyle\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/title_sensor_misc\" />\n\n    <LinearLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:baselineAligned=\"true\"\n        android:measureWithLargestChild=\"false\"\n        android:weightSum=\"22\" >\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"5\" >\n\n            <TextView\n                android:id=\"@+id/tempStatus\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"fill_parent\"\n                android:gravity=\"right|center_vertical\"\n                android:text=\"@string/dotSpace\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\"\n                android:textColor=\"@android:color/transparent\" />\n\n            <TextView\n                android:id=\"@+id/metTemp\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n            <TextView\n                android:id=\"@+id/tempHeader\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_metTemp\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"8\" >\n\n            <TextView\n                android:id=\"@+id/pressureStatus\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"fill_parent\"\n                android:gravity=\"right|center_vertical\"\n                android:text=\"@string/dotSpace\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\"\n                android:textColor=\"@android:color/transparent\" />\n\n            <TextView\n                android:id=\"@+id/metPressure\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n            <TextView\n                android:id=\"@+id/pressureHeader\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_metPressure\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"9\" >\n\n            <TextView\n                android:id=\"@+id/lightStatus\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"fill_parent\"\n                android:gravity=\"right|center_vertical\"\n                android:text=\"@string/dotSpace\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\"\n                android:textColor=\"@android:color/transparent\" />\n\n            <TextView\n                android:id=\"@+id/light\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n            <TextView\n                android:id=\"@+id/lightHeader\"\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_light\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n        </LinearLayout>\n\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:baselineAligned=\"true\"\n        android:measureWithLargestChild=\"false\"\n        android:weightSum=\"22\" >\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"13\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/humidHeader\"\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_metHumid\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\" >\n\n                <TextView\n                    android:id=\"@+id/humidStatus\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"fill_parent\"\n                    android:gravity=\"right|center_vertical\"\n                    android:text=\"@string/dotSpace\"\n                    android:textAppearance=\"?android:attr/textAppearanceSmall\"\n                    android:textColor=\"@android:color/transparent\" />\n\n                <TextView\n                    android:id=\"@+id/metHumid\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:gravity=\"right\"\n                    android:text=\"@string/value_none\"\n                    android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n            </LinearLayout>\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"fill_parent\"\n            android:layout_weight=\"9\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/proximityHeader\"\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_proximity\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\" >\n\n                <TextView\n                    android:id=\"@+id/proximityStatus\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"fill_parent\"\n                    android:gravity=\"right|center_vertical\"\n                    android:text=\"@string/dotSpace\"\n                    android:textAppearance=\"?android:attr/textAppearanceSmall\"\n                    android:textColor=\"@android:color/transparent\" />\n\n                <TextView\n                    android:id=\"@+id/proximity\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:gravity=\"right\"\n                    android:text=\"@string/value_none\"\n                    android:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n            </LinearLayout>\n\n        </LinearLayout>\n\n    </LinearLayout>\n\n</LinearLayout>\n</ScrollView>"
  },
  {
    "path": "res/layout/ril_cdma_list_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<TableRow xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:weightSum=\"26\" >\n\n    <TextView\n        android:id=\"@+id/type\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_weight=\"2\"\n        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n    <TextView\n        android:id=\"@+id/sid\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_weight=\"6\"\n        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n    <TextView\n        android:id=\"@+id/nid\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_weight=\"5\"\n        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n    <TextView\n        android:id=\"@+id/bsid\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_weight=\"9\"\n        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n    <TextView\n        android:id=\"@+id/dbm\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_weight=\"4\"\n        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n</TableRow>"
  },
  {
    "path": "res/layout/ril_list_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<TableRow xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:weightSum=\"29\" >\n\n    <TextView\n        android:id=\"@+id/type\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:layout_weight=\"2\"\n        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n    <TextView\n        android:id=\"@+id/mcc\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:layout_weight=\"3\"\n        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n    <TextView\n        android:id=\"@+id/mnc\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:layout_weight=\"3\"\n        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n    <TextView\n        android:id=\"@+id/area\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:layout_weight=\"5\"\n        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n    <LinearLayout\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:layout_weight=\"9\"\n        android:orientation=\"vertical\" >\n\n        <TextView\n            android:id=\"@+id/cell\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n        <TextView\n            android:id=\"@+id/cell2\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n    </LinearLayout>\n\n    <TextView\n        android:id=\"@+id/unit\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:layout_weight=\"3\"\n        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n    <TextView\n        android:id=\"@+id/dbm\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:layout_weight=\"4\"\n        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n</TableRow>"
  },
  {
    "path": "res/layout/tree_list_item_wrapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\" android:layout_width=\"fill_parent\"\n\tandroid:layout_height=\"fill_parent\" >\n\t<LinearLayout android:id=\"@+id/treeview_list_item_image_layout\" android:layout_width=\"80dip\"\n\t\tandroid:layout_height=\"fill_parent\" android:gravity=\"right|center_vertical\">\n\t\t<ImageView android:id=\"@+id/treeview_list_item_image\" android:layout_width=\"wrap_content\"\n\t\t\tandroid:layout_height=\"wrap_content\" android:src=\"@drawable/collapsed\" >\n\t\t</ImageView>\n\t</LinearLayout>\n\t<FrameLayout android:id=\"@+id/treeview_list_item_frame\" android:layout_width=\"fill_parent\"\n\t\tandroid:layout_height=\"fill_parent\" android:layout_weight=\"1\">\n\t</FrameLayout>\n</LinearLayout>\n"
  },
  {
    "path": "res/layout-land/fragment_main_gps.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\" >\n\n    <LinearLayout\n        android:id=\"@+id/gpsRootLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\"\n        android:layout_weight=\"1\" >\n\n        <LinearLayout\n            android:layout_width=\"320dp\"\n            android:layout_height=\"match_parent\"\n            android:gravity=\"center_vertical\"\n            android:orientation=\"vertical\" >\n\n            <LinearLayout\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"wrap_content\"\n                android:baselineAligned=\"false\"\n                android:measureWithLargestChild=\"true\"\n                android:weightSum=\"24\" >\n\n                <LinearLayout\n                    android:id=\"@+id/gpsLatLayout\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_weight=\"9\"\n                    android:orientation=\"vertical\" >\n\n                    <TextView\n                        android:id=\"@+id/textView2\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"bottom\"\n                        android:text=\"@string/title_gps_lat\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <TextView\n                        android:id=\"@+id/gpsLat\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:maxLines=\"1\"\n                        android:text=\"@string/value_none\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:id=\"@+id/gpsLonLayout\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_weight=\"10\"\n                    android:orientation=\"vertical\" >\n\n                    <TextView\n                        android:id=\"@+id/textView4\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"bottom\"\n                        android:text=\"@string/title_gps_lon\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <TextView\n                        android:id=\"@+id/gpsLon\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:maxLines=\"1\"\n                        android:text=\"@string/value_none\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:id=\"@+id/gpsCoordLayout\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"wrap_content\"\n                    android:layout_weight=\"19\"\n                    android:orientation=\"vertical\"\n                    android:visibility=\"gone\" >\n\n                    <TextView\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"bottom\"\n                        android:text=\"@string/title_gps_coord\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <TextView\n                        android:id=\"@+id/gpsCoord\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:maxLines=\"1\"\n                        android:text=\"@string/value_none\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"5\"\n                    android:orientation=\"vertical\" >\n\n                    <TextView\n                        android:id=\"@+id/textView8\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"bottom\"\n                        android:maxLines=\"1\"\n                        android:text=\"@string/title_gps_alt\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <LinearLayout\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:weightSum=\"8\" >\n\n                        <TextView\n                            android:id=\"@+id/gpsAlt\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:maxLines=\"1\"\n                            android:text=\"@string/value_none\"\n                            android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n                        <TextView\n                            android:id=\"@+id/gpsAltUnit\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:gravity=\"bottom\"\n                            android:lines=\"1\"\n                            android:text=\"\"\n                            android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n                    </LinearLayout>\n                </LinearLayout>\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"wrap_content\"\n                android:baselineAligned=\"true\"\n                android:measureWithLargestChild=\"true\"\n                android:weightSum=\"24\" >\n\n                <LinearLayout\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"9\"\n                    android:orientation=\"vertical\" >\n\n                    <TextView\n                        android:id=\"@+id/textView12\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"bottom\"\n                        android:lines=\"1\"\n                        android:text=\"@string/title_gps_bear\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <LinearLayout\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:weightSum=\"9\" >\n\n                        <TextView\n                            android:id=\"@+id/gpsBearing\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:layout_weight=\"4.5\"\n                            android:maxLines=\"1\"\n                            android:text=\"@string/value_none\"\n                            android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n                        <TextView\n                            android:id=\"@+id/gpsOrientation\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:layout_weight=\"4.5\"\n                            android:maxLines=\"1\"\n                            android:text=\"@string/value_none\"\n                            android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                    </LinearLayout>\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"6\"\n                    android:orientation=\"vertical\" >\n\n                    <TextView\n                        android:id=\"@+id/textView6\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"bottom\"\n                        android:lines=\"1\"\n                        android:text=\"@string/title_gps_speed\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <LinearLayout\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:weightSum=\"8\" >\n\n                        <TextView\n                            android:id=\"@+id/gpsSpeed\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:maxLines=\"1\"\n                            android:text=\"@string/value_none\"\n                            android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n                        <TextView\n                            android:id=\"@+id/gpsSpeedUnit\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:gravity=\"bottom\"\n                            android:lines=\"1\"\n                            android:text=\"\"\n                            android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n                    </LinearLayout>\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"4\"\n                    android:orientation=\"vertical\" >\n\n                    <TextView\n                        android:id=\"@+id/textView16\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"bottom\"\n                        android:lines=\"1\"\n                        android:text=\"@string/title_gps_acc\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <LinearLayout\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:weightSum=\"8\" >\n\n                        <TextView\n                            android:id=\"@+id/gpsAccuracy\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:maxLines=\"1\"\n                            android:text=\"@string/value_none\"\n                            android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n                        <TextView\n                            android:id=\"@+id/gpsAccuracyUnit\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:gravity=\"bottom\"\n                            android:lines=\"1\"\n                            android:text=\"\"\n                            android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n                    </LinearLayout>\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"5\"\n                    android:orientation=\"vertical\" >\n\n                    <TextView\n                        android:id=\"@+id/textView17\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"bottom\"\n                        android:lines=\"1\"\n                        android:text=\"@string/title_gps_sats\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <TextView\n                        android:id=\"@+id/gpsSats\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:maxLines=\"1\"\n                        android:text=\"@string/value_none\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"wrap_content\"\n                android:baselineAligned=\"true\"\n                android:measureWithLargestChild=\"true\"\n                android:weightSum=\"24\" >\n\n                <LinearLayout\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"4.5\"\n                    android:orientation=\"vertical\" >\n\n                    <TextView\n                        android:id=\"@+id/textView32\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"bottom\"\n                        android:lines=\"1\"\n                        android:text=\"@string/title_gps_decl\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <TextView\n                        android:id=\"@+id/orDeclination\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:maxLines=\"1\"\n                        android:text=\"@string/value_none\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"4.5\"\n                    android:orientation=\"vertical\" >\n\n                    <TextView\n                        android:id=\"@+id/textView19\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"bottom\"\n                        android:lines=\"1\"\n                        android:text=\"@string/title_gps_ttff\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <TextView\n                        android:id=\"@+id/gpsTtff\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:maxLines=\"1\"\n                        android:text=\"@string/value_none\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"15\"\n                    android:orientation=\"vertical\" >\n\n                    <TextView\n                        android:id=\"@+id/textView10\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"bottom\"\n                        android:lines=\"1\"\n                        android:text=\"@string/title_gps_time\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <TextView\n                        android:id=\"@+id/gpsTime\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:maxLines=\"1\"\n                        android:text=\"@string/value_none\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n            </LinearLayout>\n        </LinearLayout>\n    </LinearLayout>\n\n    <com.vonglasow.michael.satstat.widgets.GpsSnrView\n        android:id=\"@+id/gpsSnrView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\" />\n\n</LinearLayout>"
  },
  {
    "path": "res/layout-land/fragment_main_sensors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" >\n\n<LinearLayout\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\" >\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:baselineAligned=\"false\"\n        android:weightSum=\"48\" >\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"match_parent\"\n            android:layout_weight=\"25\"\n            android:orientation=\"vertical\" >\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\" >\n\n                <TextView\n                    android:id=\"@+id/accStatus\"\n                    style=\"?android:attr/listSeparatorTextViewStyle\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/dotSpace\"\n                    android:textColor=\"@android:color/transparent\" />\n\n                <TextView\n                    android:id=\"@+id/accHeader\"\n                    style=\"?android:attr/listSeparatorTextViewStyle\"\n                    android:layout_width=\"fill_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/title_sensor_acc\" />\n\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"wrap_content\"\n                android:baselineAligned=\"true\"\n                android:measureWithLargestChild=\"false\"\n                android:weightSum=\"4\" >\n\n                <LinearLayout\n                    android:id=\"@+id/accAccuracyContainer\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"1\"\n                    android:orientation=\"vertical\" >\n\n                    <TextView\n                        android:id=\"@+id/textView3\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/title_sensors_accTotal\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <TextView\n                        android:id=\"@+id/accTotal\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:maxLines=\"1\"\n                        android:text=\"@string/value_none\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"1\"\n                    android:orientation=\"vertical\" >\n\n                    <TextView\n                        android:id=\"@+id/textView19\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/title_sensors_accX\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <TextView\n                        android:id=\"@+id/accX\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"right\"\n                        android:maxLines=\"1\"\n                        android:text=\"@string/value_none\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"1\"\n                    android:orientation=\"vertical\"\n                    android:weightSum=\"3\" >\n\n                    <TextView\n                        android:id=\"@+id/textView21\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/title_sensors_accY\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <TextView\n                        android:id=\"@+id/accY\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"right\"\n                        android:maxLines=\"1\"\n                        android:text=\"@string/value_none\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"1\"\n                    android:orientation=\"vertical\" >\n\n                    <TextView\n                        android:id=\"@+id/textView23\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/title_sensors_accZ\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <TextView\n                        android:id=\"@+id/accZ\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"right\"\n                        android:maxLines=\"1\"\n                        android:text=\"@string/value_none\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\" >\n\n                <TextView\n                    android:id=\"@+id/rotStatus\"\n                    style=\"?android:attr/listSeparatorTextViewStyle\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/dotSpace\"\n                    android:textColor=\"@android:color/transparent\" />\n\n                <TextView\n                    android:id=\"@+id/rotHeader\"\n                    style=\"?android:attr/listSeparatorTextViewStyle\"\n                    android:layout_width=\"fill_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/title_sensor_rot\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"wrap_content\"\n                android:baselineAligned=\"true\"\n                android:measureWithLargestChild=\"false\"\n                android:weightSum=\"4\" >\n\n                <LinearLayout\n                    android:id=\"@+id/rotAccuracyContainer\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"1\"\n                    android:orientation=\"vertical\" >\n\n                    <TextView\n                        android:id=\"@+id/textView36\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/title_sensors_rotTotal\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <TextView\n                        android:id=\"@+id/rotTotal\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:maxLines=\"1\"\n                        android:text=\"@string/value_none\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"1\"\n                    android:orientation=\"vertical\" >\n\n                    <TextView\n                        android:id=\"@+id/textView33\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/title_sensors_rotX\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <TextView\n                        android:id=\"@+id/rotX\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"right\"\n                        android:maxLines=\"1\"\n                        android:text=\"@string/value_none\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"1\"\n                    android:orientation=\"vertical\"\n                    android:weightSum=\"3\" >\n\n                    <TextView\n                        android:id=\"@+id/textView34\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/title_sensors_rotY\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <TextView\n                        android:id=\"@+id/rotY\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"right\"\n                        android:maxLines=\"1\"\n                        android:text=\"@string/value_none\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"1\"\n                    android:orientation=\"vertical\" >\n\n                    <TextView\n                        android:id=\"@+id/textView35\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/title_sensors_rotZ\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <TextView\n                        android:id=\"@+id/rotZ\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"right\"\n                        android:maxLines=\"1\"\n                        android:text=\"@string/value_none\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n            </LinearLayout>\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"match_parent\"\n            android:layout_weight=\"23\"\n            android:orientation=\"vertical\"\n            android:paddingLeft=\"@dimen/activity_vertical_margin\" >\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\" >\n\n                <TextView\n                    android:id=\"@+id/magStatus\"\n                    style=\"?android:attr/listSeparatorTextViewStyle\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/dotSpace\"\n                    android:textColor=\"@android:color/transparent\" />\n\n                <TextView\n                    android:id=\"@+id/magHeader\"\n                    style=\"?android:attr/listSeparatorTextViewStyle\"\n                    android:layout_width=\"fill_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/title_sensor_mag\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"wrap_content\"\n                android:baselineAligned=\"true\"\n                android:measureWithLargestChild=\"false\"\n                android:weightSum=\"4\" >\n\n                <LinearLayout\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"1\"\n                    android:orientation=\"vertical\" >\n\n                    <TextView\n                        android:id=\"@+id/textView24\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/title_sensors_magTotal\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <TextView\n                        android:id=\"@+id/magTotal\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:maxLines=\"1\"\n                        android:text=\"@string/value_none\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"1\"\n                    android:orientation=\"vertical\" >\n\n                    <TextView\n                        android:id=\"@+id/textView25\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/title_sensors_magX\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <TextView\n                        android:id=\"@+id/magX\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"right\"\n                        android:maxLines=\"1\"\n                        android:text=\"@string/value_none\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"1\"\n                    android:orientation=\"vertical\"\n                    android:weightSum=\"3\" >\n\n                    <TextView\n                        android:id=\"@+id/textView26\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/title_sensors_magY\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <TextView\n                        android:id=\"@+id/magY\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"right\"\n                        android:maxLines=\"1\"\n                        android:text=\"@string/value_none\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"1\"\n                    android:orientation=\"vertical\" >\n\n                    <TextView\n                        android:id=\"@+id/textView27\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/title_sensors_magZ\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <TextView\n                        android:id=\"@+id/magZ\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"right\"\n                        android:maxLines=\"1\"\n                        android:text=\"@string/value_none\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\" >\n\n                <TextView\n                    android:id=\"@+id/orStatus\"\n                    style=\"?android:attr/listSeparatorTextViewStyle\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/dotSpace\"\n                    android:textColor=\"@android:color/transparent\" />\n\n                <TextView\n                    android:id=\"@+id/orHeader\"\n                    style=\"?android:attr/listSeparatorTextViewStyle\"\n                    android:layout_width=\"fill_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/title_sensor_or\" />\n            </LinearLayout>\n\n            <LinearLayout\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"wrap_content\"\n                android:baselineAligned=\"false\"\n                android:measureWithLargestChild=\"false\"\n                android:weightSum=\"4\" >\n\n                <LinearLayout\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"2\"\n                    android:orientation=\"vertical\"\n                    android:weightSum=\"2\" >\n\n                    <TextView\n                        android:id=\"@+id/textView28\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/title_sensors_orAzimuth\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <LinearLayout\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\" >\n\n                        <TextView\n                            android:id=\"@+id/orAzimuth\"\n                            android:layout_width=\"0dp\"\n                            android:layout_height=\"wrap_content\"\n                            android:layout_weight=\"1\"\n                            android:text=\"@string/value_none\"\n                            android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n                        <TextView\n                            android:id=\"@+id/orAziText\"\n                            android:layout_width=\"0dp\"\n                            android:layout_height=\"wrap_content\"\n                            android:layout_weight=\"1\"\n                            android:text=\"@string/value_none\"\n                            android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n                    </LinearLayout>\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"1\"\n                    android:orientation=\"vertical\" >\n\n                    <TextView\n                        android:id=\"@+id/textView29\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/title_sensors_orPitch\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <TextView\n                        android:id=\"@+id/orPitch\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"right\"\n                        android:text=\"@string/value_none\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n\n                <LinearLayout\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"fill_parent\"\n                    android:layout_weight=\"1\"\n                    android:orientation=\"vertical\" >\n\n                    <TextView\n                        android:id=\"@+id/textView30\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:text=\"@string/title_sensors_orRoll\"\n                        android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n                    <TextView\n                        android:id=\"@+id/orRoll\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:gravity=\"right\"\n                        android:text=\"@string/value_none\"\n                        android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n                </LinearLayout>\n            </LinearLayout>\n\n        </LinearLayout>\n\n    </LinearLayout>\n\n    <TextView\n        android:id=\"@+id/miscHeader\"\n        style=\"?android:attr/listSeparatorTextViewStyle\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/title_sensor_misc\" />\n\n    <LinearLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:baselineAligned=\"false\"\n        android:measureWithLargestChild=\"false\"\n        android:weightSum=\"44\" >\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"5\" >\n\n            <TextView\n                android:id=\"@+id/tempStatus\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"fill_parent\"\n                android:gravity=\"right|center_vertical\"\n                android:text=\"@string/dotSpace\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\"\n                android:textColor=\"@android:color/transparent\" />\n\n            <TextView\n                android:id=\"@+id/metTemp\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n            <TextView\n                android:id=\"@+id/tempHeader\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_metTemp\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"8\" >\n\n            <TextView\n                android:id=\"@+id/pressureStatus\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"fill_parent\"\n                android:gravity=\"right|center_vertical\"\n                android:text=\"@string/dotSpace\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\"\n                android:textColor=\"@android:color/transparent\" />\n\n            <TextView\n                android:id=\"@+id/metPressure\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n            <TextView\n                android:id=\"@+id/pressureHeader\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_metPressure\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"9\" >\n\n            <TextView\n                android:id=\"@+id/lightStatus\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"fill_parent\"\n                android:gravity=\"right|center_vertical\"\n                android:text=\"@string/dotSpace\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\"\n                android:textColor=\"@android:color/transparent\" />\n\n            <TextView\n                android:id=\"@+id/light\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:gravity=\"right\"\n                android:text=\"@string/value_none\"\n                android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n            <TextView\n                android:id=\"@+id/lightHeader\"\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_light\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"13\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/humidHeader\"\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_metHumid\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:baselineAligned=\"true\" >\n\n                <TextView\n                    android:id=\"@+id/humidStatus\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"fill_parent\"\n                    android:gravity=\"right|center_vertical\"\n                    android:text=\"@string/dotSpace\"\n                    android:textAppearance=\"?android:attr/textAppearanceSmall\"\n                    android:textColor=\"@android:color/transparent\" />\n\n                <TextView\n                    android:id=\"@+id/metHumid\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:gravity=\"right\"\n                    android:text=\"@string/value_none\"\n                    android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n            </LinearLayout>\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"9\"\n            android:orientation=\"vertical\" >\n\n            <TextView\n                android:id=\"@+id/proximityHeader\"\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/title_sensors_proximity\"\n                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\" >\n\n                <TextView\n                    android:id=\"@+id/proximityStatus\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"fill_parent\"\n                    android:gravity=\"right|center_vertical\"\n                    android:text=\"@string/dotSpace\"\n                    android:textAppearance=\"?android:attr/textAppearanceSmall\"\n                    android:textColor=\"@android:color/transparent\" />\n\n                <TextView\n                    android:id=\"@+id/proximity\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:gravity=\"right\"\n                    android:text=\"@string/value_none\"\n                    android:textAppearance=\"?android:attr/textAppearanceMedium\" />\n            </LinearLayout>\n        </LinearLayout>\n\n    </LinearLayout>\n\n</LinearLayout>\n</ScrollView>"
  },
  {
    "path": "res/layout-w640dp/fragment_main_radio.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" >\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:baselineAligned=\"false\"\n        android:orientation=\"horizontal\"\n        android:weightSum=\"55\" >\n        \n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"match_parent\"\n            android:layout_weight=\"28\"\n            android:orientation=\"vertical\"\n            android:paddingRight=\"@dimen/activity_vertical_margin\" >\n            \n            <!-- left pane: cell networks -->\n\n            <LinearLayout\n                android:id=\"@+id/rilLteLayout\"\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"vertical\" >\n\n                <TextView\n                    android:id=\"@+id/TextView06\"\n                    style=\"?android:attr/listSeparatorTextViewStyle\"\n                    android:layout_width=\"fill_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:text=\"@string/title_radio_lte\" />\n\n                <LinearLayout\n                    android:layout_width=\"fill_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:baselineAligned=\"true\"\n                    android:measureWithLargestChild=\"false\"\n                    android:weightSum=\"29\" >\n\n                    <LinearLayout\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"fill_parent\"\n                        android:layout_weight=\"2\"\n                        android:orientation=\"vertical\" >\n\n                        <TextView\n                            android:id=\"@+id/TextView02\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n                    </LinearLayout>\n\n                    <LinearLayout\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"fill_parent\"\n                        android:layout_weight=\"3\"\n                        android:orientation=\"vertical\" >\n\n                        <TextView\n                            android:id=\"@+id/TextView04\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:text=\"@string/title_radio_mcc\"\n                            android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n                    </LinearLayout>\n\n                    <LinearLayout\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"fill_parent\"\n                        android:layout_weight=\"3\"\n                        android:orientation=\"vertical\"\n                        android:weightSum=\"3\" >\n\n                        <TextView\n                            android:id=\"@+id/TextView01\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:text=\"@string/title_radio_mnc\"\n                            android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n                    </LinearLayout>\n\n                    <LinearLayout\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"fill_parent\"\n                        android:layout_weight=\"5\"\n                        android:orientation=\"vertical\" >\n\n                        <TextView\n                            android:id=\"@+id/TextView05\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:text=\"@string/title_radio_tac\"\n                            android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n                    </LinearLayout>\n\n                    <LinearLayout\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"fill_parent\"\n                        android:layout_weight=\"9\"\n                        android:orientation=\"vertical\" >\n\n                        <TextView\n                            android:id=\"@+id/TextView03\"\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:text=\"@string/title_radio_cellid\"\n                            android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n                    </LinearLayout>\n\n                    <LinearLayout\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"fill_parent\"\n                        android:layout_weight=\"3\"\n                        android:orientation=\"vertical\" >\n\n                        <TextView\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:text=\"@string/title_radio_pci\"\n                            android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n                    </LinearLayout>\n\n                    <LinearLayout\n                        android:layout_width=\"0dp\"\n                        android:layout_height=\"fill_parent\"\n                        android:layout_weight=\"4\"\n                        android:orientation=\"vertical\" >\n\n                        <TextView\n                            android:layout_width=\"wrap_content\"\n                            android:layout_height=\"wrap_content\"\n                            android:text=\"@string/title_radio_level\"\n                            android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n                    </LinearLayout>\n                </LinearLayout>\n\n                <TableLayout\n                    android:id=\"@+id/rilLteCells\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\" >\n                </TableLayout>\n            </LinearLayout>\n\n\t\t\t<LinearLayout\n\t\t\t    android:id=\"@+id/rilGsmLayout\"\n\t\t\t    android:layout_width=\"fill_parent\"\n\t\t\t    android:layout_height=\"wrap_content\"\n\t\t\t    android:orientation=\"vertical\" >\n\t\t\t\n\t\t\t    <TextView\n\t\t\t        android:id=\"@+id/rotHeader\"\n\t\t\t        style=\"?android:attr/listSeparatorTextViewStyle\"\n\t\t\t        android:layout_width=\"fill_parent\"\n\t\t\t        android:layout_height=\"wrap_content\"\n\t\t\t        android:text=\"@string/title_radio_gsm\" />\n\t\t\t\n\t\t\t    <LinearLayout\n\t\t\t        android:layout_width=\"fill_parent\"\n\t\t\t        android:layout_height=\"wrap_content\"\n\t\t\t        android:baselineAligned=\"true\"\n\t\t\t        android:measureWithLargestChild=\"false\"\n\t\t\t        android:weightSum=\"29\" >\n\t\t\t\n\t\t\t        <LinearLayout\n\t\t\t            android:layout_width=\"0dp\"\n\t\t\t            android:layout_height=\"fill_parent\"\n\t\t\t            android:layout_weight=\"2\"\n\t\t\t            android:orientation=\"vertical\" >\n\t\t\t\n\t\t\t            <TextView\n\t\t\t                android:id=\"@+id/textView190\"\n\t\t\t                android:layout_width=\"wrap_content\"\n\t\t\t                android:layout_height=\"wrap_content\"\n\t\t\t                android:text=\"\"\n\t\t\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\t\t\n\t\t\t        </LinearLayout>\n\t\t\t\n\t\t\t        <LinearLayout\n\t\t\t            android:layout_width=\"0dp\"\n\t\t\t            android:layout_height=\"fill_parent\"\n\t\t\t            android:layout_weight=\"3\"\n\t\t\t            android:orientation=\"vertical\" >\n\t\t\t\n\t\t\t            <TextView\n\t\t\t                android:id=\"@+id/textView19\"\n\t\t\t                android:layout_width=\"wrap_content\"\n\t\t\t                android:layout_height=\"wrap_content\"\n\t\t\t                android:text=\"@string/title_radio_mcc\"\n\t\t\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\t\t\n\t\t\t        </LinearLayout>\n\t\t\t\n\t\t\t        <LinearLayout\n\t\t\t            android:layout_width=\"0dp\"\n\t\t\t            android:layout_height=\"fill_parent\"\n\t\t\t            android:layout_weight=\"3\"\n\t\t\t            android:orientation=\"vertical\"\n\t\t\t            android:weightSum=\"3\" >\n\t\t\t\n\t\t\t            <TextView\n\t\t\t                android:id=\"@+id/textView21\"\n\t\t\t                android:layout_width=\"wrap_content\"\n\t\t\t                android:layout_height=\"wrap_content\"\n\t\t\t                android:text=\"@string/title_radio_mnc\"\n\t\t\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\t\t\n\t\t\t        </LinearLayout>\n\t\t\t        \n\t\t\t        <LinearLayout\n\t\t\t            android:layout_width=\"0dp\"\n\t\t\t            android:layout_height=\"fill_parent\"\n\t\t\t            android:layout_weight=\"5\"\n\t\t\t            android:orientation=\"vertical\" >\n\t\t\t\n\t\t\t            <TextView\n\t\t\t                android:id=\"@+id/textView3\"\n\t\t\t                android:layout_width=\"wrap_content\"\n\t\t\t                android:layout_height=\"wrap_content\"\n\t\t\t                android:text=\"@string/title_radio_lac\"\n\t\t\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\t\t\n\t\t\t        </LinearLayout>\n\t\t\t        \n\t\t\t        <LinearLayout\n\t\t\t            android:layout_width=\"0dp\"\n\t\t\t            android:layout_height=\"fill_parent\"\n\t\t\t            android:layout_weight=\"9\"\n\t\t\t            android:orientation=\"vertical\" >\n\t\t\t\n\t\t\t            <TextView\n\t\t\t                android:id=\"@+id/textView23\"\n\t\t\t                android:layout_width=\"wrap_content\"\n\t\t\t                android:layout_height=\"wrap_content\"\n\t\t\t                android:text=\"@string/title_radio_cellid\"\n\t\t\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\t\t\n\t\t\t        </LinearLayout>\n\t\t\t\n\t\t\t        <LinearLayout\n\t\t\t            android:layout_width=\"0dp\"\n\t\t\t            android:layout_height=\"fill_parent\"\n\t\t\t            android:layout_weight=\"3\"\n\t\t\t            android:orientation=\"vertical\" >\n\t\t\n\t\t\t            <TextView\n\t\t\t                android:layout_width=\"wrap_content\"\n\t\t\t                android:layout_height=\"wrap_content\"\n\t\t\t                android:text=\"@string/title_radio_psc\"\n\t\t\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\t\t        </LinearLayout>\n\t\t\t\n\t\t\t        <LinearLayout\n\t\t\t            android:layout_width=\"0dp\"\n\t\t\t            android:layout_height=\"fill_parent\"\n\t\t\t            android:layout_weight=\"4\"\n\t\t\t            android:orientation=\"vertical\" >\n\t\t\t\n\t\t\t            <TextView\n\t\t\t                android:layout_width=\"wrap_content\"\n\t\t\t                android:layout_height=\"wrap_content\"\n\t\t\t                android:text=\"@string/title_radio_level\"\n\t\t\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\t\t\n\t\t\t        </LinearLayout>\n\t\t\t\n\t\t\t        </LinearLayout>\n\t\t\t\n\t\t\t    <TableLayout\n\t\t\t        android:id=\"@+id/rilCells\"\n\t\t\t        android:layout_width=\"match_parent\"\n\t\t\t        android:layout_height=\"wrap_content\" >\n\t\t\t\n\t\t\t    </TableLayout>\n\t\t\t\n\t\t\t</LinearLayout>\n\t\t\n\t\t\t<LinearLayout\n\t\t\t    android:id=\"@+id/rilCdmaLayout\"\n\t\t\t    android:layout_width=\"fill_parent\"\n\t\t\t    android:layout_height=\"wrap_content\"\n\t\t\t    android:orientation=\"vertical\">\n\t\t\t    \n\t\t\t    <TextView\n\t\t\t        android:id=\"@+id/textView5\"\n\t\t\t        style=\"?android:attr/listSeparatorTextViewStyle\"\n\t\t\t        android:layout_width=\"fill_parent\"\n\t\t\t        android:layout_height=\"wrap_content\"\n\t\t\t        android:text=\"@string/title_radio_cdma\" />\n\t\t\t\n\t\t\t    <LinearLayout\n\t\t\t        android:layout_width=\"fill_parent\"\n\t\t\t        android:layout_height=\"wrap_content\"\n\t\t\t        android:baselineAligned=\"true\"\n\t\t\t        android:measureWithLargestChild=\"false\"\n\t\t\t        android:weightSum=\"26\" >\n\t\t\t\n\t\t\t        <LinearLayout\n\t\t\t            android:layout_width=\"0dp\"\n\t\t\t            android:layout_height=\"fill_parent\"\n\t\t\t            android:layout_weight=\"2\"\n\t\t\t            android:orientation=\"vertical\" >\n\t\t\t\n\t\t\t            <TextView\n\t\t\t                android:id=\"@+id/textView191\"\n\t\t\t                android:layout_width=\"wrap_content\"\n\t\t\t                android:layout_height=\"wrap_content\"\n\t\t\t                android:text=\"\"\n\t\t\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\t\t\n\t\t\t        </LinearLayout>\n\t\t\t\n\t\t\t        <LinearLayout\n\t\t\t            android:layout_width=\"0dp\"\n\t\t\t            android:layout_height=\"fill_parent\"\n\t\t\t            android:layout_weight=\"6\"\n\t\t\t            android:orientation=\"vertical\" >\n\t\t\t\n\t\t\t            <TextView\n\t\t\t                android:id=\"@+id/textView22\"\n\t\t\t                android:layout_width=\"wrap_content\"\n\t\t\t                android:layout_height=\"wrap_content\"\n\t\t\t                android:text=\"@string/title_radio_sid\"\n\t\t\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\t\t\n\t\t\t        </LinearLayout>\n\t\t\t\n\t\t\t        <LinearLayout\n\t\t\t            android:layout_width=\"0dp\"\n\t\t\t            android:layout_height=\"fill_parent\"\n\t\t\t            android:layout_weight=\"5\"\n\t\t\t            android:orientation=\"vertical\">\n\t\t\t\n\t\t\t            <TextView\n\t\t\t                android:id=\"@+id/textView25\"\n\t\t\t                android:layout_width=\"wrap_content\"\n\t\t\t                android:layout_height=\"wrap_content\"\n\t\t\t                android:text=\"@string/title_radio_nid\"\n\t\t\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\t\t\n\t\t\t        </LinearLayout>\n\t\t\t\n\t\t\t        <LinearLayout\n\t\t\t            android:layout_width=\"0dp\"\n\t\t\t            android:layout_height=\"fill_parent\"\n\t\t\t            android:layout_weight=\"9\"\n\t\t\t            android:orientation=\"vertical\" >\n\t\t\t\n\t\t\t            <TextView\n\t\t\t                android:id=\"@+id/textView24\"\n\t\t\t                android:layout_width=\"wrap_content\"\n\t\t\t                android:layout_height=\"wrap_content\"\n\t\t\t                android:text=\"@string/title_radio_bsid\"\n\t\t\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\t\t\n\t\t\t        </LinearLayout>\n\t\t\t\n\t\t\t        <LinearLayout\n\t\t\t            android:layout_width=\"0dp\"\n\t\t\t            android:layout_height=\"fill_parent\"\n\t\t\t            android:layout_weight=\"4\"\n\t\t\t            android:orientation=\"vertical\" >\n\t\t\t\n\t\t\t            <TextView\n\t\t\t                android:layout_width=\"wrap_content\"\n\t\t\t                android:layout_height=\"wrap_content\"\n\t\t\t                android:text=\"@string/title_radio_level\"\n\t\t\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\t\t\n\t\t\t        </LinearLayout>\n\t\t\t\n\t\t\t        </LinearLayout>\n\t\t\t\n\t\t\t    <TableLayout\n\t\t\t        android:id=\"@+id/rilCdmaCells\"\n\t\t\t        android:layout_width=\"match_parent\"\n\t\t\t        android:layout_height=\"wrap_content\" >\n\t\t\t\n\t\t\t    </TableLayout>\n\t\t\t</LinearLayout>\n            \n        </LinearLayout>\n        \n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"match_parent\"\n            android:layout_weight=\"27\"\n            android:orientation=\"vertical\">\n            \n            <!-- right pane: WiFi networks -->\n\t\t    <TextView\n\t\t        android:id=\"@+id/textView4\"\n\t\t        style=\"?android:attr/listSeparatorTextViewStyle\"\n\t\t        android:layout_width=\"fill_parent\"\n\t\t        android:layout_height=\"wrap_content\"\n\t\t        android:text=\"@string/title_radio_wifi\" />\n\t\t\n\t\t    <LinearLayout\n\t\t        android:layout_width=\"fill_parent\"\n\t\t        android:layout_height=\"wrap_content\"\n\t\t        android:baselineAligned=\"true\"\n\t\t        android:measureWithLargestChild=\"false\"\n\t\t        android:weightSum=\"22\" >\n\t\t\n\t\t        <TextView\n\t\t            android:layout_width=\"0dp\"\n\t\t            android:layout_height=\"wrap_content\"\n\t\t            android:layout_weight=\"3\"\n\t\t            android:text=\"\"\n\t\t            android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\t\n\t\t        <TextView\n\t\t            android:layout_width=\"0dp\"\n\t\t            android:layout_height=\"wrap_content\"\n\t\t            android:layout_weight=\"14\"\n\t\t            android:text=\"@string/title_radio_mac\"\n\t\t            android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\t\n\t\t        <TextView\n\t\t            android:layout_width=\"0dp\"\n\t\t            android:layout_height=\"wrap_content\"\n\t\t            android:layout_weight=\"2\"\n\t\t            android:text=\"@string/title_radio_channel\"\n\t\t            android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\t\n\t\t            <TextView\n\t\t                android:layout_width=\"0dp\"\n\t\t                android:layout_height=\"wrap_content\"\n\t\t                android:layout_weight=\"3\"\n\t\t                android:text=\"@string/title_radio_level\"\n\t\t                android:textAppearance=\"?android:attr/textAppearanceSmall\" />\n\t\t\n\t\t        </LinearLayout>\n\t\t\n\t\t    <LinearLayout\n\t\t        android:id=\"@+id/wifiAps\"\n\t\t        android:layout_width=\"match_parent\"\n\t\t        android:layout_height=\"wrap_content\"\n\t\t        android:orientation=\"vertical\" >\n\t\t\n\t\t    </LinearLayout>\n            \n        </LinearLayout>\n        \n    </LinearLayout>\n    \n\n</ScrollView>\n"
  },
  {
    "path": "res/menu/main.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\" >\n\n    <item\n        android:id=\"@+id/action_agps\"\n        android:showAsAction=\"never\"\n        android:title=\"@string/action_agps\"/>\n    <item\n        android:id=\"@+id/action_settings\"\n        android:showAsAction=\"never\"\n        android:title=\"@string/action_settings\"/>\n    <item android:id=\"@+id/action_legend\" android:title=\"@string/action_legend\"></item>\n    <item android:id=\"@+id/action_about\" android:title=\"@string/action_about\"></item>\n    \n\n</menu>\n"
  },
  {
    "path": "res/raw/.gitignore",
    "content": ""
  },
  {
    "path": "res/values/attrs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<declare-styleable name=\"TreeViewList\">\n\t    <attr name=\"collapsible\" format=\"boolean\" />\n\t\t<attr name=\"src_expanded\" format=\"reference|color\" />\n\t\t<attr name=\"src_collapsed\" format=\"reference|color\" />\n        <attr name=\"indent_width\" format=\"dimension\" />\n        <attr name=\"handle_trackball_press\" format=\"boolean\" />\n\t\t<attr name=\"indicator_gravity\">\n\t\t\t<!-- Push object to the top of its container, not changing its size. -->\n\t\t\t<flag name=\"top\" value=\"0x30\" />\n\t\t\t<!-- Push object to the bottom of its container, not changing its size. -->\n\t\t\t<flag name=\"bottom\" value=\"0x50\" />\n\t\t\t<!-- Push object to the left of its container, not changing its size. -->\n\t\t\t<flag name=\"left\" value=\"0x03\" />\n\t\t\t<!-- Push object to the right of its container, not changing its size. -->\n\t\t\t<flag name=\"right\" value=\"0x05\" />\n\t\t\t<!-- Place object in the vertical center of its container, not changing its size. -->\n\t\t\t<flag name=\"center_vertical\" value=\"0x10\" />\n\t\t\t<!-- Grow the vertical size of the object if needed so it completely fills its container. -->\n\t\t\t<flag name=\"fill_vertical\" value=\"0x70\" />\n\t\t\t<!-- Place object in the horizontal center of its container, not changing its size. -->\n\t\t\t<flag name=\"center_horizontal\" value=\"0x01\" />\n\t\t\t<!-- Grow the horizontal size of the object if needed so it completely fills its container. -->\n\t\t\t<flag name=\"fill_horizontal\" value=\"0x07\" />\n\t\t\t<!-- Place the object in the center of its container in both the vertical and horizontal axis, not changing its size. -->\n\t\t\t<flag name=\"center\" value=\"0x11\" />\n\t\t\t<!-- Grow the horizontal and vertical size of the object if needed so it completely fills its container. -->\n\t\t\t<flag name=\"fill\" value=\"0x77\" />\n\t\t\t<!-- Additional option that can be set to have the top and/or bottom edges of the child clipped to its container's bounds.\n\t\t\t\tThe clip will be based on the vertical gravity: a top gravity will clip the bottom edge, a bottom gravity will clip the top\n\t\t\t\tedge, and neither will clip both edges. -->\n\t\t\t<flag name=\"clip_vertical\" value=\"0x80\" />\n\t\t\t<!-- Additional option that can be set to have the left and/or right edges of the child clipped to its container's bounds.\n\t\t\t\tThe clip will be based on the horizontal gravity: a left gravity will clip the right edge, a right gravity will clip the\n\t\t\t\tleft edge, and neither will clip both edges. -->\n\t\t\t<flag name=\"clip_horizontal\" value=\"0x08\" />\n\t\t</attr>\n        <attr name=\"indicator_background\" format=\"reference|color\" />\n        <attr name=\"row_background\" format=\"reference|color\" />\n\t</declare-styleable>\n</resources>"
  },
  {
    "path": "res/values/dimens.xml",
    "content": "<resources>\n\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n\n</resources>\n"
  },
  {
    "path": "res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:ignore=\"MissingTranslation\">\n\n    <string name=\"app_name\">SatStat</string>\n    <string name=\"action_settings\">Settings</string>\n    <string name=\"action_record\">Record</string>\n    <string name=\"action_stop_record\">Stop recording</string>\n    <string name=\"action_agps\">Reload AGPS data</string>\n    <string name=\"action_legend\">Legend</string>\n    <string name=\"action_about\">About</string>\n    <string name=\"dot\" translatable=\"false\">&#x25a0;</string>\n    <string name=\"smallDot\" translatable=\"false\">&#x25fc;</string>\n    <string name=\"dotSpace\" translatable=\"false\">&#x25a0;&#x00a0;</string>\n    <string name=\"error_write_file\">Error writing to file</string>\n    <string name=\"error_ext_storage\">External storage not available</string>\n    <string name=\"info_start_rec\">Started recording</string>\n    <string name=\"notify_record_title\">Recording sensor data</string>\n    <string name=\"notify_record_body\">Touch to stop recording</string>\n    <string name=\"status_agps\">AGPS data reload requested – note that Android does not report success or failure</string>\n    <string name=\"status_agps_captive\">Can\\'t refresh data – launch browser, sign into the network and try again</string>\n    <string name=\"status_agps_error\">No network available, can\\'t reload AGPS data</string>\n    <string name=\"title_section1\">GPS</string>\n    <string name=\"title_section2\">Sensors</string>\n    <string name=\"title_section3\">Radio</string>\n    <string name=\"title_section4\">Map</string>\n    <string name=\"title_gps_lat\">Latitude</string>\n    <string name=\"title_gps_lon\">Longitude</string>\n    <string name=\"title_gps_coord\">Coordinates</string>\n    <string name=\"title_gps_decl\">Decl.</string>\n    <string name=\"title_gps_speed\">Speed</string>\n    <string name=\"title_gps_alt\">Altitude</string>\n    <string name=\"title_gps_time\">Last Fix Obtained</string>\n    <string name=\"title_gps_bear\">Bearing</string>\n    <string name=\"title_gps_orient\">Orientation</string>\n    <string name=\"title_gps_acc\">Error</string>\n    <string name=\"title_gps_sats\">Satellites</string>\n    <string name=\"title_gps_ttff\">TTFF (s)</string>\n    <string name=\"value_none\">–</string>\n    <string name=\"value_N\">N</string>\n    <string name=\"value_NNE\">NNE</string>\n    <string name=\"value_NE\">NE</string>\n    <string name=\"value_ENE\">ENE</string>\n    <string name=\"value_E\">E</string>\n    <string name=\"value_ESE\">ESE</string>\n    <string name=\"value_SE\">SE</string>\n    <string name=\"value_SSE\">SSE</string>\n    <string name=\"value_S\">S</string>\n    <string name=\"value_SSW\">SSW</string>\n    <string name=\"value_SW\">SW</string>\n    <string name=\"value_WSW\">WSW</string>\n    <string name=\"value_W\">W</string>\n    <string name=\"value_WNW\">WNW</string>\n    <string name=\"value_NW\">NW</string>\n    <string name=\"value_NNW\">NNW</string>\n    <string name=\"title_sensor_acc\">Acceleration and Gravity</string>\n    <string name=\"title_sensors_accTotal\">Σg (m/s²)</string>\n    <string name=\"title_sensors_accX\">gx (m/s²)</string>\n    <string name=\"title_sensors_accY\">gy (m/s²)</string>\n    <string name=\"title_sensors_accZ\">gz (m/s²)</string>\n    <string name=\"title_sensor_rot\">Rotation</string>\n    <string name=\"title_sensors_rotTotal\">Σω (rad/s)</string>\n    <string name=\"title_sensors_rotX\">ωx (rad/s)</string>\n    <string name=\"title_sensors_rotY\">ωy (rad/s)</string>\n    <string name=\"title_sensors_rotZ\">ωz (rad/s)</string>\n    <string name=\"title_sensor_or\">Orientation</string>\n    <string name=\"title_sensors_orAzimuth\">Azimuth</string>\n    <string name=\"title_sensors_orAziText\">Orientation</string>\n    <string name=\"title_sensors_orPitch\">Pitch</string>\n    <string name=\"title_sensors_orRoll\">Roll</string>\n    <string name=\"title_sensor_mag\">Magnetic Field</string>\n    <string name=\"title_sensors_magTotal\">ΣB (µT)</string>\n    <string name=\"title_sensors_magX\">Bx (µT)</string>\n    <string name=\"title_sensors_magY\">By (µT)</string>\n    <string name=\"title_sensors_magZ\">Bz (µT)</string>\n    <string name=\"title_sensor_met\">Meteorology</string>\n    <string name=\"title_sensors_metTemp\">&#x00a0;°C</string>\n    <string name=\"title_sensors_metPressure\">&#x00a0;hPa</string>\n    <string name=\"title_sensors_metHumid\">Relative Humidity (%)</string>\n    <string name=\"title_sensor_misc\">Miscellaneous</string>\n    <string name=\"title_sensors_light\">&#x00a0;lux</string>\n    <string name=\"title_sensors_proximity\">Proximity (cm)</string>\n    <string name=\"title_radio_gsm\">GSM</string>\n    <string name=\"title_radio_mcc\">MCC</string>\n    <string name=\"title_radio_mnc\">MNC</string>\n    <string name=\"title_radio_cellid\">Cell ID</string>\n    <string name=\"title_radio_lac\">LAC</string>\n    <string name=\"title_radio_psc\">PSC</string>\n    <string name=\"title_radio_asu\">ASU</string>\n    <string name=\"title_radio_cdma\">CDMA</string>\n    <string name=\"title_radio_sid\">SID</string>\n    <string name=\"title_radio_nid\">NID</string>\n    <string name=\"title_radio_bsid\">BSID</string>\n    <string name=\"title_radio_lte\">LTE</string>\n    <string name=\"title_radio_tac\">TAC</string>\n    <string name=\"title_radio_pci\">PCI</string>\n    <string name=\"title_radio_wifi\">WiFi</string>\n    <string name=\"title_radio_mac\">BSSID (MAC)</string>\n    <string name=\"title_radio_channel\">Ch</string>\n    <string name=\"title_radio_level\">dBm</string>\n    <string name=\"title_activity_about\">About</string>\n    <string name=\"about_caption\">Location, Sensor and Radio Network Status</string>\n    <string name=\"about_version\">Version</string>\n    <string name=\"about_text\">&lt;p&gt;\n        If something doesn\\'t work the way it should, or if you have an idea to make this\n        app even greater, please report it at:\n        &lt;br/&gt;\n        &lt;a href=\"https://github.com/mvglasow/satstat/issues&gt;https://github.com/mvglasow/satstat/issues&lt;/a&gt;\n        &lt;/p&gt;&lt;p&gt;\n        Copyright &#169; 2013 – 2018 Michael von Glasow and contributors\n        &lt;br/&gt;\n        Portions &#169; Jonathan Stott, k9mail, mapsforge.org, Polidea\n        &lt;/p&gt;&lt;p&gt;\n        This program is free software: you can redistribute it and/or modify\n        it under the terms of the GNU General Public License as published by\n        the Free Software Foundation, either version 3 of the License, or\n        (at your option) any later version.\n        &lt;/p&gt;&lt;p&gt;\n        This program is distributed in the hope that it will be useful,\n        but WITHOUT ANY WARRANTY; without even the implied warranty of\n        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n        GNU General Public License for more details.\n        &lt;/p&gt;&lt;p&gt;\n        You should have received a copy of the GNU General Public License\n        along with this program. If not, see \n        &lt;a href=\"http://www.gnu.org/licenses/\"&gt;http://www.gnu.org/licenses/&lt;/a&gt;.\n        &lt;/p&gt;&lt;p&gt;\n        Map data and information provided by MapQuest, \n        &lt;a href=\"http://www.openstreetmap.org/copyright\"&gt;OpenStreetMap&lt;/a&gt; \n        and contributors, \n        &lt;a href=\"http://wiki.openstreetmap.org/wiki/Legal_FAQ#3a._I_would_like_to_use_OpenStreetMap_maps._How_should_I_credit_you.3F\"&gt;ODbL&lt;/a&gt;.\n        &lt;/p&gt;&lt;p&gt;\n        The source code for this application is at:\n        &lt;br/&gt;\n        &lt;a href=\"https://github.com/mvglasow/satstat&gt;https://github.com/mvglasow/satstat&lt;/a&gt;</string>\n    <string name=\"unit_degree\">°</string>\n    <string name=\"unit_km_h\">&#x00a0;km/h</string>\n    <string name=\"unit_mph\">&#x00a0;mph</string>\n    <string name=\"unit_kn\">&#x00a0;kn</string>\n    <string name=\"unit_meter\">&#x00a0;m</string>\n    <string name=\"unit_feet\">&#x00a0;ft</string>\n    <string name=\"pref_notify\">GPS fix notification</string>\n    <string name=\"pref_notify_summary\">Notify me when an application gets my location from GPS</string>\n    <string name=\"pref_search\">GPS search notification</string>\n    <string name=\"pref_search_summary\">Notify me when an application is searching for my location</string>\n    <string name=\"title_activity_settings\">Settings</string>\n    <string name=\"notify_nolocation_title\">Searching for location</string>\n    <string name=\"notify_nolocation_body\">Touch to view status</string>\n    <string name=\"pref_notifications\">Notifications</string>\n    <string name=\"pref_agps\">AGPS Data Update</string>\n    <string name=\"pref_wifi\">On WiFi connect</string>\n    <string name=\"pref_wifi_summary\">Refresh AGPS data when my device connects to a WiFi network</string>\n    <string name=\"pref_freq\">Update frequency</string>\n    <string name=\"pref_freq_summary\" translatable=\"false\">%s</string>\n\n    <string-array name=\"prefs_freq_keys\">\n        <item>Every time</item>\n        <item>Once a day</item>\n        <item>Once in 2 days</item>\n        <item>Once in 3 days</item>\n        <item>Once in 4 days</item>\n        <item>Once in 5 days</item>\n        <item>Once in 6 days</item>\n        <item>Once a week</item>\n    </string-array>\n    <string-array name=\"prefs_freq_values\" translatable=\"false\">\n        <item>0</item>\n        <item>1</item>\n        <item>2</item>\n        <item>3</item>\n        <item>4</item>\n        <item>5</item>\n        <item>6</item>\n        <item>7</item>\n    </string-array>\n\n    <string name=\"pref_lastupdate\">Last update</string>\n    <string name=\"pref_lastupdate_summary\">%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS</string>\n    <string name=\"pref_networks\">Networks</string>\n    <string name=\"pref_networks_summary\">Refresh AGPS data when my device connects to one of the selected networks</string>\n    <string name=\"pref_map\">Map</string>\n    <string name=\"pref_loc_prov\">Location Sources</string>\n    <string name=\"pref_loc_prov_summary\">Show selected location sources on the map</string>\n    <string name=\"pref_data\">Data display</string>\n    <string name=\"pref_unit\">Use metric units</string>\n    <string name=\"pref_unit_summary\">If unchecked, Imperial units will be used (except in sensor view)</string>\n    <string name=\"pref_knots\">Show speed in knots</string>\n    <string name=\"pref_knots_summary\">If unchecked, metric or imperial units (as selected above) will be used</string>\n    <string name=\"pref_coord\">Coordinates</string>\n\n    <string-array name=\"prefs_coord_keys\">\n        <item>Decimal degrees</item>\n        <item>Degrees, minutes</item>\n        <item>Degrees, minutes, seconds</item>\n        <item>MGRS</item>\n        <item>UTM</item>\n    </string-array>\n    <string-array name=\"prefs_coord_values\" translatable=\"false\">\n        <item>0</item>\n        <item>1</item>\n        <item>2</item>\n        <item>3</item>\n        <item>4</item>\n    </string-array>\n\n    <string name=\"pref_utc\">Display GPS time in UTC</string>\n    <string name=\"pref_utc_summary\">Display GPS fix time in UTC rather than in local time</string>\n    <string name=\"pref_cid\">Split UMTS/LTE cell IDs</string>\n    <string name=\"pref_cid_summary\">Split cell IDs into RNCID/CID or eNodeB/sector ID</string>\n    <string name=\"osm_attribution\" translatable=\"false\">© OpenStreetMap contributors</string>\n    <string name=\"title_activity_legend\">Legend</string>\n    <string name=\"title_legend_gps\">GPS</string>\n    <string name=\"title_legend_gps_used\">Satellite used in fix</string>\n    <string name=\"title_legend_gps_unused\">Satellite not used in fix</string>\n    <string name=\"title_legend_sensors\">Sensors</string>\n    <string name=\"title_legend_sensors_high\">High accuracy</string>\n    <string name=\"title_legend_sensors_medium\">Medium accuracy</string>\n    <string name=\"title_legend_sensors_low\">Low accuracy</string>\n    <string name=\"title_legend_sensors_unreliable\">Unreliable</string>\n    <string name=\"title_legend_radio\">Radio</string>\n    <string name=\"title_legend_radio_2g\">2G cell</string>\n    <string name=\"title_legend_radio_3g\">3G cell</string>\n    <string name=\"title_legend_radio_4g\">4G cell</string>\n    <string name=\"title_legend_radio_open\">Open WiFi</string>\n    <string name=\"title_legend_radio_wep\">WEP security</string>\n    <string name=\"title_legend_radio_psk\">WPA-PSK (personal) security</string>\n    <string name=\"title_legend_radio_eap\">WPA-EAP (enterprise) security</string>\n    <string name=\"title_legend_radio_adhoc\">Ad-hoc WiFi</string>\n    <string name=\"title_legend_radio_unknown\">Unknown WiFi type</string>\n    <string name=\"title_legend_map\">Map</string>\n    <string name=\"title_legend_map_prov\">Location from %s provider</string>\n    <string name=\"title_legend_map_stale\">Stale location from any provider</string>\n    <string name=\"title_nmea_001_032\">GPS</string>\n    <string name=\"title_nmea_033_054\">SBAS</string>\n    <string name=\"title_nmea_055_064\">55–64</string>\n    <string name=\"title_nmea_065_088\">GLONASS</string>\n    <string name=\"title_nmea_097_192\">97–192</string>\n    <string name=\"title_nmea_193_195\">QZSS</string>\n    <string name=\"title_nmea_201_235\">Beidou</string>\n    <string name=\"title_nmea_236_300\">236–300</string>\n    <string name=\"title_nmea_301_336\">Galileo</string>\n    <string name=\"pref_wifi_sort\">Sort WiFi networks</string>\n\n    <string-array name=\"prefs_wifi_sort_keys\">\n        <item>By BSSID</item>\n        <item>By name</item>\n        <item>By channel</item>\n        <item>By signal strength</item>\n    </string-array>\n    <string-array name=\"prefs_wifi_sort_values\" translatable=\"false\">\n        <item>0</item>\n        <item>1</item>\n        <item>2</item>\n        <item>3</item>\n    </string-array>\n\n    <string name=\"pref_map_offline\">Use offline map</string>\n    <string name=\"pref_map_offline_summary\">Render maps on the device (requires downloading an offline map)</string>\n    <string name=\"pref_map_path\">Offline map folder</string>\n    <string name=\"map_path_title\">Path to offline map folder:</string>\n    <string name=\"map_path_fileman\">To browse for the offline map folder, you need to install a supported file manager.</string>\n    <string name=\"map_path_cm_filemanager\">CM File Manager</string>\n    <string name=\"map_path_oi_filemanager\">OI File Manager</string>\n    <string name=\"action_ok\">OK</string>\n    <string name=\"action_cancel\">Cancel</string>\n    <string name=\"pref_map_download\">Download offline map</string>\n    <string name=\"pref_map_download_summary\">Get maps from Mapsforge</string>\n    <string name=\"pref_map_purge\">Clear map tile cache</string>\n    <string name=\"pref_map_purge_summary\">Force all tiles to be fetched or rendered again, useful for troubleshooting</string>\n    <string name=\"status_map_purged\">Map tile cache cleared</string>\n    <string name=\"title_activity_map_download\">Download offline map</string>\n    <string name=\"status_folder_empty\">Folder is empty</string>\n    <string name=\"status_already_downloading\">A download for a map of the same name is already in progress</string>\n    <string name=\"confirm_download\">It seems you already have an up-to-date copy of this map. Do you want to download it anyway?</string>\n    <string name=\"action_yes\">Yes</string>\n    <string name=\"action_no\">No</string>\n    <string name=\"status_downloads_completed\">All downloads have completed</string>\n    <string name=\"status_perm_map_download\">To download offline maps, you must grant permission to access files</string>\n    <string name=\"status_perm_location\">No location or radio information will be shown unless you grant permission to access the device\\'s location</string>\n    <string name=\"status_perm_refresh_agps\">To refresh AGPS data, you must grant permission to access the device\\'s location</string>\n    <string name=\"status_perm_offline_map\">To use offline maps, you must grant permission to access files</string>\n    <string name=\"notify_perm_title\">SatStat is requesting permissions</string>\n    <string name=\"notify_perm_body\">Touch to grant or deny</string>\n    <string name=\"pref_cid2\">Display both cell ID formats</string>\n    <string name=\"pref_cid2_summary\">Show both split and non-split forms, with the one selected above appearing on top</string>\n    <string name=\"utm_outside_latitude_range\">Position outside UTM range</string>\n    <string name=\"download_error\">An error occurred while trying to retrieve the list of maps from the server</string>\n    <string name=\"download_retry\">Retry</string>\n\n</resources>"
  },
  {
    "path": "res/values/styles.xml",
    "content": "<resources>\n\n    <!--\n        Base application theme, dependent on API level. This theme is replaced\n        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.\n    -->\n    <style name=\"AppBaseTheme\" parent=\"Theme.AppCompat\">\n        <!--\n            Theme customizations available in newer API levels can go in\n            res/values-vXX/styles.xml, while customizations related to\n            backward-compatibility can go here.\n        -->\n    </style>\n\n    <!-- Application theme. -->\n    <style name=\"AppTheme\" parent=\"AppBaseTheme\">\n        <!-- All customizations that are NOT specific to a particular API-level can go here. -->\n    </style>\n    \n    <color name=\"accent\">#FF9800</color>\n    \n    <color name=\"gpsUsed\">#80CBC4</color>\n    <color name=\"gpsUnused\">#F44336</color>\n    \n    <color name=\"accHigh\">#8BC344</color>\n    <color name=\"accMedium\">#FF9800</color>\n    <color name=\"accLow\">#F44336</color>\n    <color name=\"accUnreliable\">#9C27B0</color>\n\n    <color name=\"gen2\">#8BC344</color>\n    <color name=\"gen3\">#80CBC4</color>\n    <color name=\"gen4\">#9C27B0</color>\n    \n    <color name=\"circle_blue_stroke\">#FF2196F3</color>\n    <color name=\"circle_blue_fill\">#4D2196F3</color>\n    <color name=\"circle_purple_stroke\">#FF9C27B0</color>\n    <color name=\"circle_purple_fill\">#4D9C27B0</color>\n    <color name=\"circle_green_stroke\">#FF8BC344</color>\n    <color name=\"circle_green_fill\">#4D8BC344</color>\n    <color name=\"circle_orange_stroke\">#FFFF9800</color>\n    <color name=\"circle_orange_fill\">#4DFF9800</color>\n    <color name=\"circle_red_stroke\">#FFF44336</color>\n    <color name=\"circle_red_fill\">#4DF44336</color>\n    <color name=\"circle_gray_stroke\">#FF9E9E9E</color>\n    <color name=\"circle_gray_fill\">#4D9E9E9E</color>\n    <array name=\"location_provider_blue\">\n        <item>@drawable/ic_context_marker_blue</item>\n        <item>@color/circle_blue_stroke</item>\n        <item>@color/circle_blue_fill</item>\n    </array>\n    <array name=\"location_provider_purple\">\n        <item>@drawable/ic_context_marker_purple</item>\n        <item>@color/circle_purple_stroke</item>\n        <item>@color/circle_purple_fill</item>\n    </array>\n    <array name=\"location_provider_green\">\n        <item>@drawable/ic_context_marker_green</item>\n        <item>@color/circle_green_stroke</item>\n        <item>@color/circle_green_fill</item>\n    </array>\n    <array name=\"location_provider_orange\">\n        <item>@drawable/ic_context_marker_orange</item>\n        <item>@color/circle_orange_stroke</item>\n        <item>@color/circle_orange_fill</item>\n    </array>\n    <array name=\"location_provider_red\">\n        <item>@drawable/ic_context_marker_red</item>\n        <item>@color/circle_red_stroke</item>\n        <item>@color/circle_red_fill</item>\n    </array>\n    <array name=\"location_provider_gray\">\n        <item>@drawable/ic_context_marker_gray</item>\n        <item>@color/circle_gray_stroke</item>\n        <item>@color/circle_gray_fill</item>\n    </array>\n    <color name=\"halo\">#B2FFFFFF</color>\n    <dimen name=\"bitmap_padding\">2dp</dimen>\n    <dimen name=\"legend_rowheight\">36dp</dimen>\n    \n</resources>\n"
  },
  {
    "path": "res/values/treeview_styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style parent=\"@android:attr/listViewStyle\" name=\"treeViewListStyle\">\n        <item name=\"android:background\">@android:color/white</item>\n        <item name=\"android:divider\">@drawable/divider</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "res/values-ca/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"app_name\">SatStat</string>\n    <string name=\"action_settings\">Configuració</string>\n    <string name=\"action_record\">Registra</string>\n    <string name=\"action_stop_record\">Acaba registració</string>\n    <string name=\"action_agps\">Actualitza dades AGPS</string>\n    <string name=\"action_legend\">Llegenda</string>\n    <string name=\"action_about\">Quant a l\\'aplicació</string>\n    <string name=\"error_write_file\">Error escrivint fitxer</string>\n    <string name=\"error_ext_storage\">Emmagatzematge extern no disponible</string>\n    <string name=\"info_start_rec\">Registració començada</string>\n    <string name=\"notify_record_title\">Registració dades</string>\n    <string name=\"notify_record_body\">Toca si vols acabar la registració</string>\n    <string name=\"status_agps\">Actualització dades AGPS sol·lecitada – ten present que Android no informa de l\\'èxit de l\\'operació</string>\n    <string name=\"status_agps_captive\">Impossible actualitzar dades – inicia el navegador, inicia la sessió a la xarxa i torna-ho a provar</string>\n    <string name=\"status_agps_error\">Cap xarxa disponible, impossible actualitzar dades AGPS</string>\n    <string name=\"title_section1\">GPS</string>\n    <string name=\"title_section2\">Sensors</string>\n    <string name=\"title_section3\">Xarxes</string>\n    <string name=\"title_gps_lat\">Latitud</string>\n    <string name=\"title_gps_lon\">Longitud</string>\n    <string name=\"title_gps_coord\">Coordenades</string>\n    <string name=\"title_gps_decl\">Decl.</string>\n    <string name=\"title_gps_speed\">Velocitat</string>\n    <string name=\"title_gps_alt\">Altitud</string>\n    <string name=\"title_gps_time\">Última ubicació rebuda</string>\n    <string name=\"title_gps_bear\">Rumb</string>\n    <string name=\"title_gps_orient\">Orientació</string>\n    <string name=\"title_gps_acc\">Error</string>\n    <string name=\"title_gps_sats\">Satèl·lits</string>\n    <string name=\"title_gps_ttff\">TTFF (s)</string>\n    <string name=\"value_none\">–</string>\n    <string name=\"value_N\">N</string>\n    <string name=\"value_NNE\">NNE</string>\n    <string name=\"value_NE\">NE</string>\n    <string name=\"value_ENE\">ENE</string>\n    <string name=\"value_E\">E</string>\n    <string name=\"value_ESE\">ESE</string>\n    <string name=\"value_SE\">SE</string>\n    <string name=\"value_SSE\">SSE</string>\n    <string name=\"value_S\">S</string>\n    <string name=\"value_SSW\">SSO</string>\n    <string name=\"value_SW\">SO</string>\n    <string name=\"value_WSW\">OSO</string>\n    <string name=\"value_W\">O</string>\n    <string name=\"value_WNW\">ONO</string>\n    <string name=\"value_NW\">NO</string>\n    <string name=\"value_NNW\">NNO</string>\n    <string name=\"title_sensor_acc\">Acceleració i gravetat</string>\n    <string name=\"title_sensors_accTotal\">Σg (m/s²)</string>\n    <string name=\"title_sensors_accX\">gx (m/s²)</string>\n    <string name=\"title_sensors_accY\">gy (m/s²)</string>\n    <string name=\"title_sensors_accZ\">gz (m/s²)</string>\n    <string name=\"title_sensor_rot\">Rotació</string>\n    <string name=\"title_sensors_rotTotal\">Σω (rad/s)</string>\n    <string name=\"title_sensors_rotX\">ωx (rad/s)</string>\n    <string name=\"title_sensors_rotY\">ωy (rad/s)</string>\n    <string name=\"title_sensors_rotZ\">ωz (rad/s)</string>\n    <string name=\"title_sensor_or\">Orientació</string>\n    <string name=\"title_sensors_orAzimuth\">Azimut</string>\n    <string name=\"title_sensors_orAziText\">Orientació</string>\n    <string name=\"title_sensors_orPitch\">Capcin.</string>\n    <string name=\"title_sensors_orRoll\">Balanc.</string>\n    <string name=\"title_sensor_mag\">Camp magnètic</string>\n    <string name=\"title_sensors_magTotal\">ΣB (µT)</string>\n    <string name=\"title_sensors_magX\">Bx (µT)</string>\n    <string name=\"title_sensors_magY\">By (µT)</string>\n    <string name=\"title_sensors_magZ\">Bz (µT)</string>\n    <string name=\"title_sensor_met\">Meteorologia</string>\n    <string name=\"title_sensors_metTemp\">&#x00a0;°C</string>\n    <string name=\"title_sensors_metPressure\">&#x00a0;hPa</string>\n    <string name=\"title_sensors_metHumid\">Humitat relativa (%)</string>\n    <string name=\"title_sensor_misc\">Miscel·lani</string>\n    <string name=\"title_sensors_light\">&#x00a0;lux</string>\n    <string name=\"title_sensors_proximity\">Proximitat (cm)</string>\n    <string name=\"title_radio_gsm\">GSM</string>\n    <string name=\"title_radio_mcc\">MCC</string>\n    <string name=\"title_radio_mnc\">MNC</string>\n    <string name=\"title_radio_cellid\">Cell ID</string>\n    <string name=\"title_radio_lac\">LAC</string>\n    <string name=\"title_radio_asu\">ASU</string>\n    <string name=\"title_radio_cdma\">CDMA</string>\n    <string name=\"title_radio_sid\">SID</string>\n    <string name=\"title_radio_nid\">NID</string>\n    <string name=\"title_radio_bsid\">BSID</string>\n    <string name=\"title_radio_wifi\">WiFi</string>\n    <string name=\"title_radio_mac\">BSSID (MAC)</string>\n    <string name=\"title_radio_channel\">C.</string>\n    <string name=\"title_radio_level\">dBm</string>\n    <string name=\"title_activity_about\">Quant a l\\'aplicació</string>\n    <string name=\"about_caption\">Location, Sensor and Radio Network Status</string>\n    <string name=\"about_version\">Versió</string>\n    <string name=\"about_text\">&lt;p&gt;\n        Si quelcom no funciona com es deuria,\n        o si tens una idea com millorar encara aquesta aplicació,\n        per a favor senyala-hi a:\n        &lt;br/&gt;\n        &lt;a href=\"https://github.com/mvglasow/satstat/issues&gt;https://github.com/mvglasow/satstat/issues&lt;/a&gt;\n        &lt;/p&gt;&lt;p&gt;\n        Copyright &#169; 2013 – 2018 Michael von Glasow and contributors\n        &lt;br/&gt;\n        Portions &#169; Jonathan Stott, k9mail, mapsforge.org, Polidea\n        &lt;/p&gt;&lt;p&gt;\n        This program is free software: you can redistribute it and/or modify\n        it under the terms of the GNU General Public License as published by\n        the Free Software Foundation, either version 3 of the License, or\n        (at your option) any later version.\n        &lt;/p&gt;&lt;p&gt;\n        This program is distributed in the hope that it will be useful,\n        but WITHOUT ANY WARRANTY; without even the implied warranty of\n        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n        GNU General Public License for more details.\n        &lt;/p&gt;&lt;p&gt;\n        You should have received a copy of the GNU General Public License\n        along with this program. If not, see \n        &lt;a href=\"http://www.gnu.org/licenses/\"&gt;http://www.gnu.org/licenses/&lt;/a&gt;.\n        &lt;/p&gt;&lt;p&gt;\n        Map data and information provided by MapQuest, \n        &lt;a href=\"http://www.openstreetmap.org/copyright\"&gt;OpenStreetMap&lt;/a&gt; \n        and contributors, \n        &lt;a href=\"http://wiki.openstreetmap.org/wiki/Legal_FAQ#3a._I_would_like_to_use_OpenStreetMap_maps._How_should_I_credit_you.3F\"&gt;ODbL&lt;/a&gt;.\n        &lt;/p&gt;&lt;p&gt;\n        El codi font d\\'aquesta aplicació es troba a:\n        &lt;br/&gt;\n        &lt;a href=\"https://github.com/mvglasow/satstat&gt;https://github.com/mvglasow/satstat&lt;/a&gt;</string>\n    <string name=\"unit_degree\">°</string>\n    <string name=\"unit_km_h\">&#x00a0;km/h</string>\n    <string name=\"unit_meter\">&#x00a0;m</string>\n    <string name=\"pref_notify\">Notificació d\\'ubicació GPS</string>\n    <string name=\"pref_notify_summary\">Notifica\\'m quan una aplicació obtingui la meva ubicació desde GPS</string>\n    <string name=\"pref_search\">Notificació de recerca GPS</string>\n    <string name=\"pref_search_summary\">Notifica\\'m quan una aplicació cerqui la meva ubicació</string>\n    <string name=\"title_activity_settings\">Configuració</string>\n    <string name=\"notify_nolocation_title\">S\\'està cercant l\\'ubicació</string>\n    <string name=\"notify_nolocation_body\">Toca per veure l\\'estat</string>\n    <string name=\"pref_notifications\">Notificaciones</string>\n    <string name=\"pref_agps\">Actualització dades AGPS</string>\n    <string name=\"pref_wifi\">En connexion WiFi</string>\n    <string name=\"pref_wifi_summary\">Actualitza dades AGPS quan el meu dispositiu es connecta amb una xarxa WiFi</string>\n    <string name=\"pref_freq\">Freqüència</string>\n\n    <string-array name=\"prefs_freq_keys\">\n        <item>Cada cop</item>\n        <item>Un cop cada dia</item>\n        <item>Un cop cada 2 dies</item>\n        <item>Un cop cada 3 dies</item>\n        <item>Un cop cada 4 dies</item>\n        <item>Un cop cada 5 dies</item>\n        <item>Un cop cada 6 dies</item>\n        <item>Un cop cada setmana</item>\n    </string-array>\n\n    <string name=\"pref_lastupdate\">Última actualització</string>\n    <string name=\"pref_networks\">Xarxes</string>\n    <string name=\"pref_networks_summary\">Actualitza dades AGPS quan el meu dispositiu es connecta amb una de les xarxes seleccionades</string>\n    <string name=\"pref_map\">Mapa</string>\n    <string name=\"pref_loc_prov\">Fonts d\\'ubicació</string>\n    <string name=\"pref_loc_prov_summary\">Mostra les fonts d\\'ubicació seleccionades al mapa</string>\n    <string name=\"pref_data\">Representació de dades</string>\n    <string name=\"pref_unit\">Utilitza unitats mètriques</string>\n    <!-- <string name=\"pref_unit_summary\">If unchecked, Imperial units will be used (except in sensor view)</string> -->\n    <string name=\"pref_knots\">Mostra velocitat en nusos</string>\n    <!-- <string name=\"pref_knots_summary\">If unchecked, metric or imperial units (as selected above) will be used</string> -->\n    <string name=\"pref_coord\">Coordenades</string>\n\n    <string-array name=\"prefs_coord_keys\">\n        <item>Graus</item>\n        <item>Graus i minuts</item>\n        <item>Graus, minuts i segons</item>\n        <item>MGRS</item>\n        <item>UTM</item>\n    </string-array>\n\n    <string name=\"pref_utc\">Mostra temps GPS en UTC</string>\n    <string name=\"pref_utc_summary\">Utilitza UTC en comptes de temps local per mostrar el temps GPS</string>\n    <string name=\"pref_cid\">Subdivideix l\\'ID de cel·les UMTS/LTE</string>\n    <string name=\"pref_cid_summary\">Subdivideix l\\'ID de cel·les en RNCID/CID o eNodeB/sector ID</string>\n    <string name=\"title_activity_legend\">Llegenda</string>\n    <string name=\"title_legend_gps\">GPS</string>\n    <string name=\"title_legend_gps_used\">Satèl·lite utilitzat per l\\'ubicació</string>\n    <string name=\"title_legend_gps_unused\">Satèl·lite no utilitzat per l\\'ubicació</string>\n    <string name=\"title_legend_sensors\">Sensors</string>\n    <string name=\"title_legend_sensors_high\">Exactitud alta</string>\n    <string name=\"title_legend_sensors_medium\">Exactitud mitjana</string>\n    <string name=\"title_legend_sensors_low\">Exactitud baixa</string>\n    <!-- <string name=\"title_legend_sensors_unreliable\">Unreliable</string> -->\n    <string name=\"title_legend_radio\">Xarxes</string>\n    <string name=\"title_legend_radio_2g\">Cel·la 2G</string>\n    <string name=\"title_legend_radio_3g\">Cel·la 3G</string>\n    <string name=\"title_legend_radio_4g\">Cel·la 4G</string>\n    <string name=\"title_legend_radio_open\">WiFi obert</string>\n    <string name=\"title_legend_radio_wep\">Seguretat WEP</string>\n    <string name=\"title_legend_radio_psk\">Seguretat WPA-PSK (personal)</string>\n    <string name=\"title_legend_radio_eap\">Seguretat WPA-EAP (enterprise)</string>\n    <string name=\"title_legend_radio_adhoc\">WiFi ad hoc</string>\n    <string name=\"title_legend_radio_unknown\">Tipus de WiFi desconegut</string>\n    <string name=\"title_legend_map\">Mapa</string>\n    <string name=\"title_legend_map_prov\">Ubicació de la font %s</string>\n    <string name=\"title_legend_map_stale\">Ubicació gastada de qualsevol font</string>\n    <string name=\"pref_wifi_sort\">Ordena les xarxes WiFi</string>\n\n    <string-array name=\"prefs_wifi_sort_keys\">\n        <item>Per BSSID</item>\n        <item>Per nom</item>\n        <item>Per canal</item>\n        <item>Per nivell del senyal</item>\n    </string-array>\n\n    <string name=\"pref_map_offline\">Utilitza mapa offline</string>\n    <string name=\"pref_map_offline_summary\">Renderitza les mapes en el dispositiu (es necessita baixar una mapa offline)</string>\n    <string name=\"pref_map_path\">Carpeta per a les mapes offline</string>\n    <string name=\"map_path_title\">Ruta a la carpeta per a les mapes offline:</string>\n    <string name=\"map_path_fileman\">Per poder seleccionar la carpeta, es necessita instal·lar un gestor d\\'arxius suportat.</string>\n    <string name=\"action_ok\">D\\'acord</string>\n    <string name=\"action_cancel\">Cancel·la</string>\n    <string name=\"pref_map_download\">Baixar mapa offline</string>\n    <string name=\"pref_map_download_summary\">Baixar mapes desde Mapsforge</string>\n    <string name=\"pref_map_purge\">Esborrar les teseles a memòria cau</string>\n    <string name=\"pref_map_purge_summary\">Todes les teseles seran baixades o renderitzades de nou, útil en cas de problemes</string>\n    <string name=\"status_map_purged\">Les teseles a memòria cau han estat esborrades</string>\n    <string name=\"title_activity_map_download\">Baixar mapa offline</string>\n    <string name=\"status_folder_empty\">La carpeta està buida</string>\n    <string name=\"status_already_downloading\">S\\'està ja baixant una mapa del meteix nom</string>\n    <string name=\"confirm_download\">Sembla que ja tens una còpia actual d\\'aquesta mapa. Vols baixar-la tanmateix?</string>\n    <string name=\"action_yes\">Sí</string>\n    <string name=\"action_no\">No</string>\n    <string name=\"status_downloads_completed\">Totes les baixades s\\'han completades</string>\n    <string name=\"status_perm_map_download\">Per baixar mapes offline, cal permetre l\\'accés a fitxers</string>\n    <string name=\"status_perm_location\">Per veure ubicació o xarxes, cal permetre l\\'accés a l\\'ubicació del dispositiu</string>\n    <string name=\"status_perm_refresh_agps\">Per actualitzar dades AGPS, cal permetre l\\'accés a l\\'ubicació del dispositiu</string>\n    <string name=\"status_perm_offline_map\">Per utilitzar mapes offline, cal permetre l\\'accés a fitxers</string>\n    <string name=\"notify_perm_title\">SatStat necessita permisos</string>\n    <string name=\"notify_perm_body\">Toca per permetre o denegar</string>\n    <string name=\"pref_cid2\">Mostra tots dos formats de l\\'ID de cel·la</string>\n    <!-- <string name=\"pref_cid2_summary\">Show both split and non-split forms, with the one selected above appearing on top</string> -->\n\n</resources>"
  },
  {
    "path": "res/values-de/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"app_name\">SatStat</string>\n    <string name=\"action_settings\">Einstellungen</string>\n    <string name=\"action_record\">Aufzeichnen</string>\n    <string name=\"action_stop_record\">Aufzeichnung beenden</string>\n    <string name=\"action_agps\">AGPS-Daten neu laden</string>\n    <string name=\"action_legend\">Legende</string>\n    <string name=\"action_about\">Info</string>\n    <string name=\"error_write_file\">Fehler beim Schreiben in Datei</string>\n    <string name=\"error_ext_storage\">Externer Speicher nicht verfügbar</string>\n    <string name=\"info_start_rec\">Aufzeichnung gestartet</string>\n    <string name=\"notify_record_title\">Sensordaten werden aufgezeichnet</string>\n    <string name=\"notify_record_body\">Zum Beenden antippen</string>\n    <string name=\"status_agps\">Aktualisierung der AGPS-Daten angefordert – beachten Sie, dass Android den Erfolg der Aktualisierung nicht meldet</string>\n    <string name=\"status_agps_captive\">Daten können nicht aktualisiert werden – starten Sie den Browser, melden Sie sich am Netzwerk an und versuchen Sie es erneut</string>\n    <string name=\"status_agps_error\">Kein Netz verfügbar, AGPS-Daten können nicht aktualisiert werden</string>\n    <string name=\"title_section1\">GPS</string>\n    <string name=\"title_section2\">Sensoren</string>\n    <string name=\"title_section3\">Funknetze</string>\n    <string name=\"title_section4\">Karte</string>\n    <string name=\"title_gps_lat\">Breite</string>\n    <string name=\"title_gps_lon\">Länge</string>\n    <string name=\"title_gps_coord\">Koordinaten</string>\n    <string name=\"title_gps_decl\">Dekl.</string>\n    <string name=\"title_gps_speed\">Geschw.</string>\n    <string name=\"title_gps_alt\">Höhe</string>\n    <string name=\"title_gps_time\">Letzte Positionsbestimmung</string>\n    <string name=\"title_gps_bear\">Kurs</string>\n    <string name=\"title_gps_orient\">Orientation</string>\n    <string name=\"title_gps_acc\">Fehler</string>\n    <string name=\"title_gps_sats\">Satelliten</string>\n    <string name=\"title_gps_ttff\">TTFF (s)</string>\n    <string name=\"value_none\">–</string>\n    <string name=\"value_N\">N</string>\n    <string name=\"value_NNE\">NNO</string>\n    <string name=\"value_NE\">NO</string>\n    <string name=\"value_ENE\">ONO</string>\n    <string name=\"value_E\">O</string>\n    <string name=\"value_ESE\">OSO</string>\n    <string name=\"value_SE\">SO</string>\n    <string name=\"value_SSE\">SSO</string>\n    <string name=\"value_S\">S</string>\n    <string name=\"value_SSW\">SSW</string>\n    <string name=\"value_SW\">SW</string>\n    <string name=\"value_WSW\">WSW</string>\n    <string name=\"value_W\">W</string>\n    <string name=\"value_WNW\">WNW</string>\n    <string name=\"value_NW\">NW</string>\n    <string name=\"value_NNW\">NNW</string>\n    <string name=\"title_sensor_acc\">Beschleunigung/Schwerkraft</string>\n    <string name=\"title_sensors_accTotal\">Σg (m/s²)</string>\n    <string name=\"title_sensors_accX\">gx (m/s²)</string>\n    <string name=\"title_sensors_accY\">gy (m/s²)</string>\n    <string name=\"title_sensors_accZ\">gz (m/s²)</string>\n    <string name=\"title_sensor_rot\">Rotation</string>\n    <string name=\"title_sensors_rotTotal\">Σω (rad/s)</string>\n    <string name=\"title_sensors_rotX\">ωx (rad/s)</string>\n    <string name=\"title_sensors_rotY\">ωy (rad/s)</string>\n    <string name=\"title_sensors_rotZ\">ωz (rad/s)</string>\n    <string name=\"title_sensor_or\">Lage</string>\n    <string name=\"title_sensors_orAzimuth\">Azimut</string>\n    <string name=\"title_sensors_orAziText\">Orientation</string>\n    <string name=\"title_sensors_orPitch\">Nick</string>\n    <string name=\"title_sensors_orRoll\">Roll</string>\n    <string name=\"title_sensor_mag\">Magnetfeld</string>\n    <string name=\"title_sensors_magTotal\">ΣB (µT)</string>\n    <string name=\"title_sensors_magX\">Bx (µT)</string>\n    <string name=\"title_sensors_magY\">By (µT)</string>\n    <string name=\"title_sensors_magZ\">Bz (µT)</string>\n    <string name=\"title_sensor_met\">Meteorologie</string>\n    <string name=\"title_sensors_metTemp\">&#x00a0;°C</string>\n    <string name=\"title_sensors_metPressure\">&#x00a0;hPa</string>\n    <string name=\"title_sensors_metHumid\">Rel. Luftfeuchte (%)</string>\n    <string name=\"title_sensor_misc\">Verschiedenes</string>\n    <string name=\"title_sensors_light\">&#x00a0;lux</string>\n    <string name=\"title_sensors_proximity\">Abstand (cm)</string>\n    <string name=\"title_radio_gsm\">GSM</string>\n    <string name=\"title_radio_mcc\">MCC</string>\n    <string name=\"title_radio_mnc\">MNC</string>\n    <string name=\"title_radio_cellid\">Cell ID</string>\n    <string name=\"title_radio_lac\">LAC</string>\n    <string name=\"title_radio_psc\">PSC</string>\n    <string name=\"title_radio_asu\">ASU</string>\n    <string name=\"title_radio_cdma\">CDMA</string>\n    <string name=\"title_radio_sid\">SID</string>\n    <string name=\"title_radio_nid\">NID</string>\n    <string name=\"title_radio_bsid\">BSID</string>\n    <string name=\"title_radio_lte\">LTE</string>\n    <string name=\"title_radio_tac\">TAC</string>\n    <string name=\"title_radio_pci\">PCI</string>\n    <string name=\"title_radio_wifi\">WiFi</string>\n    <string name=\"title_radio_mac\">BSSID (MAC)</string>\n    <string name=\"title_radio_channel\">K.</string>\n    <string name=\"title_radio_level\">dBm</string>\n    <string name=\"title_activity_about\">Info</string>\n    <string name=\"about_caption\">Status für Standort, Sensoren und Netzwerk</string>\n    <string name=\"about_version\">Version</string>\n    <string name=\"about_text\">&lt;p&gt;\n        Wenn etwas nicht so funktioniert, wie es sollte, oder\n        wenn Sie Verbesserungsvorschläge haben, melden Sie diese bitte unter:\n        &lt;br/&gt;\n        &lt;a href=\"https://github.com/mvglasow/satstat/issues&gt;https://github.com/mvglasow/satstat/issues&lt;/a&gt;\n        &lt;/p&gt;&lt;p&gt;\n        Copyright &#169; 2013 – 2018 Michael von Glasow and contributors\n        &lt;br/&gt;\n        Portions &#169; Jonathan Stott, k9mail, mapsforge.org, Polidea\n        &lt;/p&gt;&lt;p&gt;\n        Dieses Programm ist Freie Software: Sie können es unter den Bedingungen\n        der GNU General Public License, wie von der Free Software Foundation,\n        Version 3 der Lizenz oder (nach Ihrer Wahl) jeder neueren\n        veröffentlichten Version, weiterverbreiten und/oder modifizieren.\n        &lt;/p&gt;&lt;p&gt;\n        Dieses Programm wird in der Hoffnung, dass es nützlich sein wird, aber\n        OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite\n        Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK.\n        Siehe die GNU General Public License für weitere Details.\n        &lt;/p&gt;&lt;p&gt;\n        Sie sollten eine Kopie der GNU General Public License zusammen mit diesem\n        Programm erhalten haben. Wenn nicht, siehe\n        &lt;a href=\"http://www.gnu.org/licenses/\"&gt;http://www.gnu.org/licenses/&lt;/a&gt;.\n        &lt;/p&gt;&lt;p&gt;\n        Map data and information provided by MapQuest, \n        &lt;a href=\"http://www.openstreetmap.org/copyright\"&gt;OpenStreetMap&lt;/a&gt; \n        and contributors, \n        &lt;a href=\"http://wiki.openstreetmap.org/wiki/Legal_FAQ#3a._I_would_like_to_use_OpenStreetMap_maps._How_should_I_credit_you.3F\"&gt;ODbL&lt;/a&gt;.\n        &lt;/p&gt;&lt;p&gt;\n        Der Quellcode für diese Applikation ist unter:\n        &lt;br/&gt;\n        &lt;a href=\"https://github.com/mvglasow/satstat&gt;https://github.com/mvglasow/satstat&lt;/a&gt;</string>\n    <string name=\"unit_degree\">°</string>\n    <string name=\"unit_km_h\">&#x00a0;km/h</string>\n    <string name=\"unit_kn\">&#x00a0;kn</string>\n    <string name=\"unit_meter\">&#x00a0;m</string>\n    <string name=\"pref_notify\">GPS-Standorthinweis</string>\n    <string name=\"pref_notify_summary\">Benachrichtigen, wenn eine Applikation meinen Standort mit GPS bestimmt hat</string>\n    <string name=\"pref_search\">Hinweis bei GPS-Suche</string>\n    <string name=\"pref_search_summary\">Benachrichtigen, wenn eine Applikation versucht, meinen Standort zu bestimmen</string>\n    <string name=\"title_activity_settings\">Einstellungen</string>\n    <string name=\"notify_nolocation_title\">Standort wird gesucht</string>\n    <string name=\"notify_nolocation_body\">Antippen, um Status zu sehen</string>\n    <string name=\"pref_notifications\">Benachrichtigungen</string>\n    <string name=\"pref_agps\">AGPS-Aktualisierung</string>\n    <string name=\"pref_wifi\">Bei WLAN-Verbindung</string>\n    <string name=\"pref_wifi_summary\">AGPS-Daten aktualisieren, wenn sich mein Gerät mit einem WLAN-Netzwerk verbindet</string>\n    <string name=\"pref_freq\">Aktualisierungsintervall</string>\n\n    <string-array name=\"prefs_freq_keys\">\n        <item>Jedes Mal</item>\n        <item>Einmal täglich</item>\n        <item>Einmal alle 2 Tage</item>\n        <item>Einmal alle 3 Tage</item>\n        <item>Einmal alle 4 Tage</item>\n        <item>Einmal alle 5 Tage</item>\n        <item>Einmal alle 6 Tage</item>\n        <item>Einmal pro Woche</item>\n    </string-array>\n\n    <string name=\"pref_lastupdate\">Letzte Aktualisierung</string>\n    <string name=\"pref_lastupdate_summary\">%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS</string>\n    <string name=\"pref_networks\">Netze</string>\n    <string name=\"pref_networks_summary\">AGPS-Daten aktualisieren, wenn sich mein Gerät mit einem der ausgewählten Netzwerke verbindet</string>\n    <string name=\"pref_map\">Karte</string>\n    <string name=\"pref_loc_prov\">Standortquellen</string>\n    <string name=\"pref_loc_prov_summary\">Ausgewählte Standortquellen auf der Karte anzeigen</string>\n    <string name=\"pref_data\">Datenanzeige</string>\n    <string name=\"pref_unit\">Metrische Einheiten verwenden</string>\n    <string name=\"pref_unit_summary\">Wenn abgewählt, werden amerikanische Maßeinheiten verwendet (Ausnahme Sensoransicht)</string>\n    <string name=\"pref_knots\">Geschwindigkeit in Knoten anzeigen</string>\n    <string name=\"pref_knots_summary\">Wenn abgewählt, werden metrische oder amerikanische Maßeinheiten verwendet (wie oben ausgewählt)</string>\n    <string name=\"pref_coord\">Koordinaten</string>\n\n    <string-array name=\"prefs_coord_keys\">\n        <item>Grad (dezimal)</item>\n        <item>Grad, Minuten</item>\n        <item>Grad, Minuten, Sekunden</item>\n        <item>MGRS</item>\n        <item>UTM</item>\n    </string-array>\n\n    <string name=\"pref_utc\">GPS-Zeit in UTC anzeigen</string>\n    <string name=\"pref_utc_summary\">GPS-Zeit in UTC statt in Ortszeit anzeigen</string>\n    <string name=\"pref_cid\">UMTS/LTE Cell-IDs teilen</string>\n    <string name=\"pref_cid_summary\">Cell-IDs in RNCID/CID bzw. eNodeB/Sector ID aufteilen</string>\n    <string name=\"title_activity_legend\">Legende</string>\n    <string name=\"title_legend_gps\">GPS</string>\n    <string name=\"title_legend_gps_used\">Satellit zur Positionsbestimmung verwendet</string>\n    <string name=\"title_legend_gps_unused\">Satellit nicht zur Positionsbestimmung verwendet</string>\n    <string name=\"title_legend_sensors\">Sensoren</string>\n    <string name=\"title_legend_sensors_high\">Hohe Genauigkeit</string>\n    <string name=\"title_legend_sensors_medium\">Mittlere Genauigkeit</string>\n    <string name=\"title_legend_sensors_low\">Niedrige Genauigkeit</string>\n    <string name=\"title_legend_sensors_unreliable\">Unzuverlässig</string>\n    <string name=\"title_legend_radio\">Netze</string>\n    <string name=\"title_legend_radio_2g\">2G-Zelle</string>\n    <string name=\"title_legend_radio_3g\">3G-Zelle</string>\n    <string name=\"title_legend_radio_4g\">4G-Zelle</string>\n    <string name=\"title_legend_radio_open\">Offenes WLAN-Netz</string>\n    <string name=\"title_legend_radio_wep\">WEP-Sicherheit</string>\n    <string name=\"title_legend_radio_psk\">WPA-PSK (Personal)-Sicherheit</string>\n    <string name=\"title_legend_radio_eap\">WPA-EAP (Enterprise)-Sicherheit</string>\n    <string name=\"title_legend_radio_adhoc\">Ad-hoc-WLAN</string>\n    <string name=\"title_legend_radio_unknown\">Unbekanter WLAN-Typ</string>\n    <string name=\"title_legend_map\">Karte</string>\n    <string name=\"title_legend_map_prov\">Standort von Quelle: %s</string>\n    <string name=\"title_legend_map_stale\">Veralteter Standort von beliebiger Quelle</string>\n    <string name=\"pref_wifi_sort\">WLAN-Netze sortieren</string>\n\n    <string-array name=\"prefs_wifi_sort_keys\">\n        <item>Nach BSSID</item>\n        <item>Nach Name</item>\n        <item>Nach Kanal</item>\n        <item>Nach Signalstärke</item>\n    </string-array>\n\n    <string name=\"pref_map_offline\">Offline-Karte benutzen</string>\n    <string name=\"pref_map_offline_summary\">Karten auf dem Gerät rendern (Download einer Offline-Karte erforderlich)</string>\n    <string name=\"pref_map_path\">Ordner für Offline-Karten</string>\n    <string name=\"map_path_title\">Pfad des Ordners für Offline-Karten:</string>\n    <string name=\"map_path_fileman\">Um nach dem Ordner für Offline-Karten zu suchen, benötigen Sie einen unterstützten Dateimanager.</string>\n    <string name=\"action_ok\">OK</string>\n    <string name=\"action_cancel\">Abbrechen</string>\n    <string name=\"pref_map_download\">Offline-Karte herunterladen</string>\n    <string name=\"pref_map_download_summary\">Kartenmaterial von Mapsforge laden</string>\n    <string name=\"pref_map_purge\">Karten-Cache löschen</string>\n    <string name=\"pref_map_purge_summary\">Alle Kacheln werden neu heruntergeladen oder gerendert, hilfreich bei der Fehlersuche</string>\n    <string name=\"status_map_purged\">Karten-Cache gelöscht</string>\n    <string name=\"title_activity_map_download\">Offline-Karte herunterladen</string>\n    <string name=\"status_folder_empty\">Ordner ist leer</string>\n    <string name=\"status_already_downloading\">Ein Download für eine Karte mit gleichem Namen läuft bereits</string>\n    <string name=\"confirm_download\">Sie haben offensichtlich bereits eine aktuelle Version dieser Karte. Trotzdem herunterladen?</string>\n    <string name=\"action_yes\">Ja</string>\n    <string name=\"action_no\">Nein</string>\n    <string name=\"status_downloads_completed\">Alle Downloads sind abgeschlossen</string>\n    <string name=\"status_perm_map_download\">Um Offline-Karten herunterzuladen, müssen Sie den Zugriff auf Dateien zulassen</string>\n    <string name=\"status_perm_location\">Standort- und Funknetzinformationen werden erst angezeigt, wenn Sie den Zugriff auf den Standort Ihres Geräts zulassen</string>\n    <string name=\"status_perm_refresh_agps\">Um AGPS-Daten zu aktualisieren, müsen Sie den Zugriff auf den Standort Ihres Geräts zulassen</string>\n    <string name=\"status_perm_offline_map\">Um Offline-Karten zu benutzen, müssen Sie den Zugriff auf Dateien zulassen</string>\n    <string name=\"notify_perm_title\">SatStat benötigt Berechtigungen</string>\n    <string name=\"notify_perm_body\">Zum Zulassen oder Verweigern antippen</string>\n    <string name=\"pref_cid2\">Cell-IDs in beiden Formaten anzeigen</string>\n    <string name=\"pref_cid2_summary\">Sowohl die geteilte als auch die ungeteilte Form werden angezeigt; die vorherige Option gibt an, welche Form zuerst erscheint</string>\n    <string name=\"utm_outside_latitude_range\">UTM-Koordinaten nicht definiert</string>\n    <string name=\"download_error\">Beim Abruf der Liste vom Server ist ein Fehler aufgetreten</string>\n    <string name=\"download_retry\">Erneut versuchen</string>\n    \n</resources>"
  },
  {
    "path": "res/values-es/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"app_name\">SatStat</string>\n    <string name=\"action_settings\">Ajustes</string>\n    <string name=\"action_record\">Grabar</string>\n    <string name=\"action_stop_record\">Terminar registración</string>\n    <string name=\"action_agps\">Actualizar dados AGPS</string>\n    <string name=\"action_legend\">Leyenda</string>\n    <string name=\"action_about\">Acerca de</string>\n    <string name=\"error_write_file\">Error escribiendo archivo</string>\n    <string name=\"error_ext_storage\">Almanecenamiento externo no disponible</string>\n    <string name=\"info_start_rec\">Grabación iniciada</string>\n    <string name=\"notify_record_title\">Grabación de dados</string>\n    <string name=\"notify_record_body\">Toca para terminar la grabación</string>\n    <string name=\"status_agps\">Actualización de dados AGPS solecitada – ten en cuenta que Android no informa del éxito de la operación</string>\n    <string name=\"status_agps_captive\">Imposible actualizar dados – inicia el navegador, inicia la sesión en red y vuelve a intentarlo</string>\n    <string name=\"status_agps_error\">Ninguna red disponible, imposible actualizar dados AGPS</string>\n    <string name=\"title_section1\">GPS</string>\n    <string name=\"title_section2\">Sensores</string>\n    <string name=\"title_section3\">Redes</string>\n    <string name=\"title_gps_lat\">Latitud</string>\n    <string name=\"title_gps_lon\">Longitud</string>\n    <string name=\"title_gps_coord\">Coordenadas</string>\n    <string name=\"title_gps_decl\">Decl.</string>\n    <string name=\"title_gps_speed\">Velocidad</string>\n    <string name=\"title_gps_alt\">Altitud</string>\n    <string name=\"title_gps_time\">Última ubicación recibida</string>\n    <string name=\"title_gps_bear\">Rumbo</string>\n    <string name=\"title_gps_orient\">Orientación</string>\n    <string name=\"title_gps_acc\">Error</string>\n    <string name=\"title_gps_sats\">Satélites</string>\n    <string name=\"title_gps_ttff\">TTFF (s)</string>\n    <string name=\"value_none\">–</string>\n    <string name=\"value_N\">N</string>\n    <string name=\"value_NNE\">NNE</string>\n    <string name=\"value_NE\">NE</string>\n    <string name=\"value_ENE\">ENE</string>\n    <string name=\"value_E\">E</string>\n    <string name=\"value_ESE\">ESE</string>\n    <string name=\"value_SE\">SE</string>\n    <string name=\"value_SSE\">SSE</string>\n    <string name=\"value_S\">S</string>\n    <string name=\"value_SSW\">SSO</string>\n    <string name=\"value_SW\">SO</string>\n    <string name=\"value_WSW\">OSO</string>\n    <string name=\"value_W\">O</string>\n    <string name=\"value_WNW\">ONO</string>\n    <string name=\"value_NW\">NO</string>\n    <string name=\"value_NNW\">NNO</string>\n    <string name=\"title_sensor_acc\">Acceleración y gravedad</string>\n    <string name=\"title_sensors_accTotal\">Σg (m/s²)</string>\n    <string name=\"title_sensors_accX\">gx (m/s²)</string>\n    <string name=\"title_sensors_accY\">gy (m/s²)</string>\n    <string name=\"title_sensors_accZ\">gz (m/s²)</string>\n    <string name=\"title_sensor_rot\">Rotación</string>\n    <string name=\"title_sensors_rotTotal\">Σω (rad/s)</string>\n    <string name=\"title_sensors_rotX\">ωx (rad/s)</string>\n    <string name=\"title_sensors_rotY\">ωy (rad/s)</string>\n    <string name=\"title_sensors_rotZ\">ωz (rad/s)</string>\n    <string name=\"title_sensor_or\">Orientación</string>\n    <string name=\"title_sensors_orAzimuth\">Azimut</string>\n    <string name=\"title_sensors_orAziText\">Orientación</string>\n    <string name=\"title_sensors_orPitch\">Cabeceo</string>\n    <string name=\"title_sensors_orRoll\">Alabeo</string>\n    <string name=\"title_sensor_mag\">Campo magnetico</string>\n    <string name=\"title_sensors_magTotal\">ΣB (µT)</string>\n    <string name=\"title_sensors_magX\">Bx (µT)</string>\n    <string name=\"title_sensors_magY\">By (µT)</string>\n    <string name=\"title_sensors_magZ\">Bz (µT)</string>\n    <string name=\"title_sensor_met\">Meteorología</string>\n    <string name=\"title_sensors_metTemp\">&#x00a0;°C</string>\n    <string name=\"title_sensors_metPressure\">&#x00a0;hPa</string>\n    <string name=\"title_sensors_metHumid\">Humedad relativa (%)</string>\n    <string name=\"title_sensor_misc\">Miscelánea</string>\n    <string name=\"title_sensors_light\">&#x00a0;lux</string>\n    <string name=\"title_sensors_proximity\">Proximidad (cm)</string>\n    <string name=\"title_radio_gsm\">GSM</string>\n    <string name=\"title_radio_mcc\">MCC</string>\n    <string name=\"title_radio_mnc\">MNC</string>\n    <string name=\"title_radio_cellid\">Cell ID</string>\n    <string name=\"title_radio_lac\">LAC</string>\n    <string name=\"title_radio_asu\">ASU</string>\n    <string name=\"title_radio_cdma\">CDMA</string>\n    <string name=\"title_radio_sid\">SID</string>\n    <string name=\"title_radio_nid\">NID</string>\n    <string name=\"title_radio_bsid\">BSID</string>\n    <string name=\"title_radio_wifi\">WiFi</string>\n    <string name=\"title_radio_mac\">BSSID (MAC)</string>\n    <string name=\"title_radio_channel\">C.</string>\n    <string name=\"title_radio_level\">dBm</string>\n    <string name=\"title_activity_about\">Acerca de</string>\n    <string name=\"about_caption\">Location, Sensor and Radio Network Status</string>\n    <string name=\"about_version\">Versión</string>\n    <string name=\"about_text\">&lt;p&gt;\n        Si no funciona algo como debería,\n        o si tienes una idea como mejorar aún esa aplicación,\n        por favor señalalo a:\n        &lt;br/&gt;\n        &lt;a href=\"https://github.com/mvglasow/satstat/issues&gt;https://github.com/mvglasow/satstat/issues&lt;/a&gt;\n        &lt;/p&gt;&lt;p&gt;\n        Copyright &#169; 2013 – 2018 Michael von Glasow and contributors\n        &lt;br/&gt;\n        Portions &#169; Jonathan Stott, k9mail, mapsforge.org, Polidea\n        &lt;/p&gt;&lt;p&gt;\n        This program is free software: you can redistribute it and/or modify\n        it under the terms of the GNU General Public License as published by\n        the Free Software Foundation, either version 3 of the License, or\n        (at your option) any later version.\n        &lt;/p&gt;&lt;p&gt;\n        This program is distributed in the hope that it will be useful,\n        but WITHOUT ANY WARRANTY; without even the implied warranty of\n        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n        GNU General Public License for more details.\n        &lt;/p&gt;&lt;p&gt;\n        You should have received a copy of the GNU General Public License\n        along with this program. If not, see \n        &lt;a href=\"http://www.gnu.org/licenses/\"&gt;http://www.gnu.org/licenses/&lt;/a&gt;.\n        &lt;/p&gt;&lt;p&gt;\n        Map data and information provided by MapQuest, \n        &lt;a href=\"http://www.openstreetmap.org/copyright\"&gt;OpenStreetMap&lt;/a&gt; \n        and contributors, \n        &lt;a href=\"http://wiki.openstreetmap.org/wiki/Legal_FAQ#3a._I_would_like_to_use_OpenStreetMap_maps._How_should_I_credit_you.3F\"&gt;ODbL&lt;/a&gt;.\n        &lt;/p&gt;&lt;p&gt;\n        El código fuente de esa aplicación se encuentra a:\n        &lt;br/&gt;\n        &lt;a href=\"https://github.com/mvglasow/satstat&gt;https://github.com/mvglasow/satstat&lt;/a&gt;</string>\n    <string name=\"unit_degree\">°</string>\n    <string name=\"unit_km_h\">&#x00a0;km/h</string>\n    <string name=\"unit_kn\">&#x00a0;kn</string>\n    <string name=\"unit_meter\">&#x00a0;m</string>\n    <string name=\"pref_notify\">Notificación de ubicación GPS</string>\n    <string name=\"pref_notify_summary\">Notificarme cuando una aplicación obtenga mi ubicación desde GPS</string>\n    <string name=\"pref_search\">Notificación de busqueda GPS</string>\n    <string name=\"pref_search_summary\">Notificarme cuando una aplicación busque mi ubicación</string>\n    <string name=\"title_activity_settings\">Ajustes</string>\n    <string name=\"notify_nolocation_title\">Buscando ubicación</string>\n    <string name=\"notify_nolocation_body\">Toca para ver el estado</string>\n    <string name=\"pref_notifications\">Notificaciones</string>\n    <string name=\"pref_agps\">Actualización AGPS</string>\n    <string name=\"pref_wifi\">En conexión WiFi</string>\n    <string name=\"pref_wifi_summary\">Actualizar dados AGPS al establecer una conexión a una red WiFi</string>\n    <string name=\"pref_freq\">Frecuencia de actualización</string>\n\n    <string-array name=\"prefs_freq_keys\">\n        <item>Cada vez</item>\n        <item>Una vez cada día</item>\n        <item>Una vez cada 2 días</item>\n        <item>Una vez cada 3 días</item>\n        <item>Una vez cada 4 días</item>\n        <item>Una vez cada 5 días</item>\n        <item>Una vez cada 7 días</item>\n        <item>Una vez cada semana</item>\n    </string-array>\n\n    <string name=\"pref_lastupdate\">Última actualización</string>\n    <string name=\"pref_networks\">Redes</string>\n    <string name=\"pref_networks_summary\">Actualizar dados AGPS al establecer una conexión a una de las redes seleccionadas</string>\n    <string name=\"pref_map\">Mapa</string>\n    <string name=\"pref_loc_prov\">Fuentes de ubicación</string>\n    <string name=\"pref_loc_prov_summary\">Mostrar las fuentes de ubicación seleccionadas en el mapa</string>\n    <string name=\"pref_data\">Representación de dados</string>\n    <string name=\"pref_unit\">Usar unidades métricas</string>\n    <!-- <string name=\"pref_unit_summary\">If unchecked, Imperial units will be used (except in sensor view)</string> -->\n    <string name=\"pref_knots\">Mostrar velocidad en nudos</string>\n    <!-- <string name=\"pref_knots_summary\">If unchecked, metric or imperial units (as selected above) will be used</string> -->\n    <string name=\"pref_coord\">Coordenadas</string>\n\n    <string-array name=\"prefs_coord_keys\">\n        <item>Grados (notación decimal)</item>\n        <item>Grados y minutos</item>\n        <item>Grados, minutos y segundos</item>\n        <item>MGRS</item>\n        <item>UTM</item>\n    </string-array>\n    <!-- <string name=\"pref_utc\">Display GPS time in UTC</string> -->\n    <!-- <string name=\"pref_utc_summary\">Display GPS fix time in UTC rather than in local time</string> -->\n    <!-- <string name=\"pref_cid\">Split UMTS/LTE cell IDs</string> -->\n    <!-- <string name=\"pref_cid_summary\">Split cell IDs into RNCID/CID or eNodeB/sector ID</string> -->\n    <string name=\"title_activity_legend\">Leyenda</string>\n    <string name=\"title_legend_gps\">GPS</string>\n    <!-- <string name=\"title_legend_gps_used\">Satellite used in fix</string> -->\n    <!-- <string name=\"title_legend_gps_unused\">Satellite not used in fix</string> -->\n    <string name=\"title_legend_sensors\">Sensores</string>\n    <string name=\"title_legend_sensors_high\">Exactitud alta</string>\n    <string name=\"title_legend_sensors_medium\">Exactitud media</string>\n    <string name=\"title_legend_sensors_low\">Exactitud baja</string>\n    <!-- <string name=\"title_legend_sensors_unreliable\">Unreliable</string> -->\n    <string name=\"title_legend_radio\">Redes</string>\n    <string name=\"title_legend_radio_2g\">Celda 2G</string>\n    <string name=\"title_legend_radio_3g\">Celda 3G</string>\n    <string name=\"title_legend_radio_4g\">Celda 4G</string>\n    <string name=\"title_legend_radio_open\">WiFi abierto</string>\n    <string name=\"title_legend_radio_wep\">Seguridad WEP</string>\n    <string name=\"title_legend_radio_psk\">Seguridad WPA-PSK (personal)</string>\n    <string name=\"title_legend_radio_eap\">Seguridad WPA-EAP (enterprise)</string>\n    <string name=\"title_legend_radio_adhoc\">WiFi ad hoc</string>\n    <string name=\"title_legend_radio_unknown\">Tipo WiFi desconocido</string>\n    <string name=\"title_legend_map\">Mapa</string>\n    <string name=\"title_legend_map_prov\">Ubicación de fuente %s</string>\n    <!-- <string name=\"title_legend_map_stale\">Stale location from any provider</string> -->\n    <string name=\"pref_wifi_sort\">Ordenar las redes WiFi</string>\n\n    <string-array name=\"prefs_wifi_sort_keys\">\n        <item>Por BSSID</item>\n        <item>Por nombre</item>\n        <item>Por canal</item>\n        <item>Por livel del señal</item>\n    </string-array>\n\n    <string name=\"pref_map_offline\">Usar mapa offline</string>\n    <string name=\"pref_map_offline_summary\">Renderizar las mapas en el dispositivo (se necesita descargar una mapa offline)</string>\n    <string name=\"pref_map_path\">Carpeta para mapas offline</string>\n    <string name=\"map_path_title\">Ruta a la carpeta para mapas offline:</string>\n    <string name=\"map_path_fileman\">Para poder seleccionar la carpeta, necesita instalar un administrador de archivos soportado.</string>\n    <string name=\"action_ok\">Aceptar</string>\n    <string name=\"action_cancel\">Cancelar</string>\n    <string name=\"pref_map_download\">Descargar mapa offline</string>\n    <string name=\"pref_map_download_summary\">Descargar mapas de Mapsforge</string>\n    <string name=\"pref_map_purge\">Borrar tiles en caché</string>\n    <string name=\"pref_map_purge_summary\">Todos los tiles serán descargados o renderizados de nuevo, útil en caso de problemas</string>\n    <string name=\"status_map_purged\">Los tiles en cache están borrados</string>\n    <string name=\"title_activity_map_download\">Descargar mapa offline</string>\n    <string name=\"status_folder_empty\">La carpeta está vacía</string>\n    <string name=\"status_already_downloading\">Una descarga de una mapa del mismo nombre ya está en curso</string>\n    <string name=\"confirm_download\">Parece que ya tienes una copia actual de esta mapa. ¿Quieres descargarla sin embargo?</string>\n    <string name=\"action_yes\">Si</string>\n    <string name=\"action_no\">No</string>\n    <string name=\"status_downloads_completed\">Todas las descargas están terminadas</string>\n    <string name=\"status_perm_map_download\">Para descargar mapas offline, tienes que permitir el acceso a archivos de tu dispositivo</string>\n    <string name=\"status_perm_location\">Para ver ubicación o redes inalámbricas, tienes que permitir el acceso a la ubicación de tu dispositivo</string>\n    <string name=\"status_perm_refresh_agps\">Para actualizar dados AGPS, tienes que permitir el acceso a la ubicación de tu dispositivo</string>\n    <string name=\"status_perm_offline_map\">Para usar mapas offline, tienes que permitir el acceso a archivos de tu dispositivo</string>\n    <string name=\"notify_perm_title\">SatStat necesita permisos</string>\n    <string name=\"notify_perm_body\">Toca para permitir o rechazar</string>\n    <!-- <string name=\"pref_cid2\">Display both cell ID formats</string> -->\n    <!-- <string name=\"pref_cid2_summary\">Show both split and non-split forms, with the one selected above appearing on top</string> -->\n\n</resources>"
  },
  {
    "path": "res/values-fr/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"app_name\">SatStat</string>\n    <string name=\"action_settings\">Configuration</string>\n    <string name=\"action_record\">Enregistrement</string>\n    <string name=\"action_stop_record\">Stopper l\\'enregistrement</string>\n    <string name=\"action_agps\">Recharger les données AGPS</string>\n    <string name=\"action_legend\">Légende</string>\n    <string name=\"action_about\">A propos</string>\n    <string name=\"error_write_file\">Erreur lors de l\\'écriture du fichier</string>\n    <string name=\"error_ext_storage\">Stockage externe non disponible</string>\n    <string name=\"info_start_rec\">Début d\\'enregistrement</string>\n    <string name=\"notify_record_title\">Enregistrement des données du capteur</string>\n    <string name=\"notify_record_body\">Appuyer pour stopper l\\'enregistrement</string>\n    <string name=\"status_agps\">Demande de chargement des données AGPS</string>\n    <string name=\"status_agps_captive\">Impossible charger données – lancez navigateur, connectez-vous au réseau et réessayez</string>\n    <string name=\"status_agps_error\">Aucun réseau disponible, impossible charger données AGPS</string>\n    <string name=\"title_section1\">GPS</string>\n    <string name=\"title_section2\">Capteurs</string>\n    <string name=\"title_section3\">Radio</string>\n    <string name=\"title_section4\">Carte</string>\n    <string name=\"title_gps_lat\">Latitude</string>\n    <string name=\"title_gps_lon\">Longitude</string>\n    <string name=\"title_gps_coord\">Coordonnées</string>\n    <string name=\"title_gps_decl\">Decl.</string>\n    <string name=\"title_gps_speed\">Vitesse</string>\n    <string name=\"title_gps_alt\">Altitude</string>\n    <string name=\"title_gps_time\">Obtention du dernier Fix</string>\n    <string name=\"title_gps_bear\">Bearing</string>\n    <string name=\"title_gps_orient\">Orientation</string>\n    <string name=\"title_gps_acc\">Erreur</string>\n    <string name=\"title_gps_sats\">Satellites</string>\n    <string name=\"title_gps_ttff\">TTFF (s)</string>\n    <string name=\"value_none\">–</string>\n    <string name=\"value_N\">N</string>\n    <string name=\"value_NNE\">NNE</string>\n    <string name=\"value_NE\">NE</string>\n    <string name=\"value_ENE\">ENE</string>\n    <string name=\"value_E\">E</string>\n    <string name=\"value_ESE\">ESE</string>\n    <string name=\"value_SE\">SE</string>\n    <string name=\"value_SSE\">SSE</string>\n    <string name=\"value_S\">S</string>\n    <string name=\"value_SSW\">SSO</string>\n    <string name=\"value_SW\">SO</string>\n    <string name=\"value_WSW\">OSO</string>\n    <string name=\"value_W\">O</string>\n    <string name=\"value_WNW\">ONO</string>\n    <string name=\"value_NW\">NO</string>\n    <string name=\"value_NNW\">NNO</string>\n    <string name=\"title_sensor_acc\">Accélération et gravité</string>\n    <string name=\"title_sensors_accTotal\">Σg (m/s²)</string>\n    <string name=\"title_sensors_accX\">gx (m/s²)</string>\n    <string name=\"title_sensors_accY\">gy (m/s²)</string>\n    <string name=\"title_sensors_accZ\">gz (m/s²)</string>\n    <string name=\"title_sensor_rot\">Rotation</string>\n    <string name=\"title_sensors_rotTotal\">Σω (rad/s)</string>\n    <string name=\"title_sensors_rotX\">ωx (rad/s)</string>\n    <string name=\"title_sensors_rotY\">ωy (rad/s)</string>\n    <string name=\"title_sensors_rotZ\">ωz (rad/s)</string>\n    <string name=\"title_sensor_or\">Orientation</string>\n    <string name=\"title_sensors_orAzimuth\">Azimuth</string>\n    <string name=\"title_sensors_orAziText\">Orientation</string>\n    <string name=\"title_sensors_orPitch\">Pitch</string>\n    <string name=\"title_sensors_orRoll\">Roll</string>\n    <string name=\"title_sensor_mag\">Champ magnétique</string>\n    <string name=\"title_sensors_magTotal\">ΣB (µT)</string>\n    <string name=\"title_sensors_magX\">Bx (µT)</string>\n    <string name=\"title_sensors_magY\">By (µT)</string>\n    <string name=\"title_sensors_magZ\">Bz (µT)</string>\n    <string name=\"title_sensor_met\">Météorologie</string>\n    <string name=\"title_sensors_metTemp\">&#x00a0;°C</string>\n    <string name=\"title_sensors_metPressure\">&#x00a0;hPa</string>\n    <string name=\"title_sensors_metHumid\">Humidité relative (%)</string>\n    <string name=\"title_sensor_misc\">Divers</string>\n    <string name=\"title_sensors_light\">&#x00a0;lux</string>\n    <string name=\"title_sensors_proximity\">Proximité (cm)</string>\n    <string name=\"title_radio_gsm\">GSM</string>\n    <string name=\"title_radio_mcc\">MCC</string>\n    <string name=\"title_radio_mnc\">MNC</string>\n    <string name=\"title_radio_cellid\">Cell ID</string>\n    <string name=\"title_radio_lac\">LAC</string>\n    <string name=\"title_radio_psc\">PSC</string>\n    <string name=\"title_radio_asu\">ASU</string>\n    <string name=\"title_radio_cdma\">CDMA</string>\n    <string name=\"title_radio_sid\">SID</string>\n    <string name=\"title_radio_nid\">NID</string>\n    <string name=\"title_radio_bsid\">BSID</string>\n    <string name=\"title_radio_lte\">LTE</string>\n    <string name=\"title_radio_tac\">TAC</string>\n    <string name=\"title_radio_pci\">PCI</string>\n    <string name=\"title_radio_wifi\">WiFi</string>\n    <string name=\"title_radio_mac\">BSSID (MAC)</string>\n    <string name=\"title_radio_channel\">Ch</string>\n    <string name=\"title_radio_level\">dBm</string>\n    <string name=\"title_activity_about\">A propos</string>\n    <string name=\"about_caption\">Location, Sensor and Radio Network Status</string>\n    <string name=\"about_version\">Version</string>\n    <string name=\"about_text\">&lt;p&gt;\n        Si quelque chose ne fonctionne pas comme il le devrait,\n        ou si vous avez une idée pour rendre l\\'application meilleure, vous pouvez\n        soumettre vos idées à l\\'adresse:\n        &lt;br/&gt;\n        &lt;a href=\"https://github.com/mvglasow/satstat/issues&gt;https://github.com/mvglasow/satstat/issues&lt;/a&gt;\n        &lt;/p&gt;&lt;p&gt;\n        Copyright &#169; 2013 – 2018 Michael von Glasow and contributors\n        &lt;br/&gt;\n        Portions &#169; Jonathan Stott, k9mail, mapsforge.org, Polidea\n        &lt;/p&gt;&lt;p&gt;\n        Ce programme est un logiciel libre; vous pouvez le redistribuer ou le\n        modifier suivant les termes de la GNU General Public License telle que publiée\n        par la Free Software Foundation; soit la version 3 de la licence,\n        soit (à votre gré) toute version ultérieure.\n        &lt;/p&gt;&lt;p&gt;\n        Ce programme est distribué dans l\\'espoir qu\\'il sera utile, mais SANS\n        AUCUNE GARANTIE; pas même la garantie implicite de COMMERCIABILISABILITÉ\n        ni d\\'ADÉQUATION à UN OBJECTIF PARTICULIER. Consultez la GNU General Public\n        License pour plus de détails.\n        &lt;/p&gt;&lt;p&gt;\n        Vous devez avoir reçu une copie de la GNU General Public License en même\n        temps que ce programme; si ce n\\'est pas le cas, consultez \n        &lt;a href=\"http://www.gnu.org/licenses/\"&gt;http://www.gnu.org/licenses/&lt;/a&gt;.\n        &lt;/p&gt;&lt;p&gt;\n        Le code source de cette application est disponible à l\\'adresse:\n        &lt;br/&gt;\n        &lt;a href=\"https://github.com/mvglasow/satstat&gt;https://github.com/mvglasow/satstat&lt;/a&gt;</string>\n    <string name=\"unit_degree\">°</string>\n    <string name=\"unit_km_h\">&#x00a0;km/h</string>\n    <string name=\"unit_mph\">&#x00a0;mph</string>\n    <string name=\"unit_kn\">&#x00a0;kn</string>\n    <string name=\"unit_meter\">&#x00a0;m</string>\n    <string name=\"pref_notify\">Notification de localisation GPS</string>\n    <string name=\"pref_notify_summary\">Me notifier lorsqu\\'une application m\\'a localisé via GPS</string>\n    <string name=\"pref_search\">Notification de recherche GPS</string>\n    <string name=\"pref_search_summary\">Me notifier lorsqu\\'une application cherche ma position via GPS</string>\n    <string name=\"title_activity_settings\">Configuration</string>\n    <string name=\"notify_nolocation_title\">Recherche de position</string>\n    <string name=\"notify_nolocation_body\">Appuyez pour voir l\\'état</string>\n    <string name=\"pref_notifications\">Notifications</string>\n    <string name=\"pref_agps\">Mise à jour AGPS</string>\n    <string name=\"pref_wifi\">A la connexion WiFi</string>\n    <string name=\"pref_wifi_summary\">Recharger données AGPS lorsque mon appareil se connecte à un réseau WiFi</string>\n    <string name=\"pref_freq\">Fréquence</string>\n\n    <string-array name=\"prefs_freq_keys\">\n        <item>Chaque fois</item>\n        <item>Une fois par jour</item>\n        <item>Une fois chaque 2 jours</item>\n        <item>Une fois chaque 3 jours</item>\n        <item>Une fois chaque 4 jours</item>\n        <item>Une fois chaque 5 jours</item>\n        <item>Une fois chaque 6 jours</item>\n        <item>Une fois par semaine</item>\n    </string-array>\n\n    <string name=\"pref_lastupdate\">Dernière mise à jour</string>\n    <!-- Do not translate this unless the string differs from default -->\n    <!-- <string name=\"pref_lastupdate_summary\">%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS</string> -->\n    <string name=\"pref_networks\">Réseaux</string>\n    <string name=\"pref_networks_summary\">Recharger données AGPS lorsque mon appareil se connecte à un des réseaux selectionnés</string>\n    <string name=\"pref_map\">Carte</string>\n    <string name=\"pref_loc_prov\">Sources de localisation</string>\n    <string name=\"pref_loc_prov_summary\">Montrer les sources de localisation selectionnées sur la carte</string>\n    <string name=\"pref_data\">Affichage des données</string> \n    <string name=\"pref_unit\">Utiliser des unités métriques</string>\n    <string name=\"pref_unit_summary\">Si ce n\\'est pas coché, les unités impériales seront utilisées sauf dans la vue des capteurs</string>\n    <string name=\"pref_knots\">Montrer vitesse en nœuds</string>\n    <string name=\"pref_knots_summary\">Si ce n\\'est pas coché, les unités métriques ou impériales (selon ce qui est sélectionné plus haut) seront utilisées</string>\n    <string name=\"pref_coord\">Coordonnées</string>\n\n    <string-array name=\"prefs_coord_keys\">\n        <item>Degrés</item>\n        <item>Degrés et minutes</item>\n        <item>Degrés, minutes et secondes</item>\n        <item>MGRS</item>\n        <item>UTM</item>\n    </string-array>\n    <string name=\"pref_utc\">Afficher l\\'heure du GPS en UTC</string>\n    <string name=\"pref_utc_summary\">Afficher l\\'heure du fix GPS en UTC au lieu de l\\'heure locale</string>\n\t<string name=\"pref_cid\">Séparer les identifiants de cellules UMTS/LTE</string>\n\t<string name=\"pref_cid_summary\">Séparer les identifiants de cellules en RNCID/CID ou eNodeB/Sector ID</string>\n    <string name=\"title_activity_legend\">Légende</string>\n    <string name=\"title_legend_gps\">GPS</string>\n    <string name=\"title_legend_gps_used\">Satellite utilisé pour le fix</string>\n    <string name=\"title_legend_gps_unused\">Satellite non utilisé pour le fix</string>\n    <string name=\"title_legend_sensors\">Capteurs</string>\n    <string name=\"title_legend_sensors_high\">Justesse haute</string>\n    <string name=\"title_legend_sensors_medium\">Justesse moyenne</string>\n    <string name=\"title_legend_sensors_low\">Justesse baisse</string>\n    <string name=\"title_legend_sensors_unreliable\">Pas fiable</string>\n    <string name=\"title_legend_radio\">Radio</string>\n    <string name=\"title_legend_radio_2g\">Cellule 2G</string>\n    <string name=\"title_legend_radio_3g\">Cellule 3G</string>\n    <string name=\"title_legend_radio_4g\">Cellule 4G</string>\n    <string name=\"title_legend_radio_open\">WiFi ouvert</string>\n    <string name=\"title_legend_radio_wep\">Sécurité WEP</string>\n    <string name=\"title_legend_radio_psk\">Sécurité WPA-PSK (personal)</string>\n    <string name=\"title_legend_radio_eap\">Sécurité WPA-EAP (enterprise)</string>\n    <string name=\"title_legend_radio_adhoc\">WiFi ad hoc</string>\n    <string name=\"title_legend_radio_unknown\">Type de réseau WiFi inconnu</string>\n    <string name=\"title_legend_map\">Carte</string>\n    <string name=\"title_legend_map_prov\">Localisation fournie par %s</string>\n    <string name=\"title_legend_map_stale\">Dernière localisation connue (périmée)</string>\n    <string name=\"title_nmea_001_032\">GPS</string>\n    <string name=\"title_nmea_033_054\">SBAS</string>\n    <string name=\"title_nmea_055_064\">55–64</string>\n    <string name=\"title_nmea_065_088\">GLONASS</string>\n    <string name=\"title_nmea_097_192\">97–192</string>\n    <string name=\"title_nmea_193_195\">QZSS</string>\n    <string name=\"title_nmea_201_235\">Beidou</string>\n    <string name=\"pref_wifi_sort\">Trier les reseaux WiFi</string>\n\n    <string-array name=\"prefs_wifi_sort_keys\">\n        <item>Par BSSID</item>\n        <item>Par nom</item>\n        <item>Par canal</item>\n        <item>Par niveau du signal</item>\n    </string-array>\n\n    <string name=\"pref_map_offline\">Utiliser la carte hors connexion</string>\n    <string name=\"pref_map_offline_summary\">Rendu des cartes sur l\\'appareil (nécessite le téléchargement d\\'une carte hors connexion)</string>\n    <string name=\"pref_map_path\">Dossier des cartes hors connexion</string>\n    <string name=\"map_path_title\">Chemin du dossier des cartes hors connexion:</string>\n    <string name=\"map_path_fileman\">Pour parcourrir le dossier des cartes hors connexion, vous devez installer un gestionnaire de fichiers compatible.</string>\n    <string name=\"map_path_cm_filemanager\">Gestionnaire de fichier CM</string>\n    <string name=\"map_path_oi_filemanager\">Gestionnaire de fichiers OI</string>\n    <string name=\"action_ok\">OK</string>\n    <string name=\"action_cancel\">Annuler</string>\n    <string name=\"pref_map_download\">Télécharger la carte hors connexion</string>\n    <string name=\"pref_map_download_summary\">Télécharger des cartes de Mapsforge</string>\n    <string name=\"pref_map_purge\">Effacer le cache des tuiles de carte</string>\n    <string name=\"pref_map_purge_summary\">Forcer le téléchargement ou le rendu des tuiles, utile en cas de problème</string>\n    <string name=\"status_map_purged\">Cache des tuiles de carte effacé</string>\n    <string name=\"title_activity_map_download\">Télécharger la carte hors connexion</string>\n    <string name=\"status_folder_empty\">Le dossier est vide</string>\n    <string name=\"status_already_downloading\">Le téléchargement d\\'une carte du même nom est déjà en cours.</string>\n    <string name=\"confirm_download\">Il semble que vous ayez déjà une copie à jour de cette carte. Voulez-vous vraiment la télécharger ?</string>\n    <string name=\"action_yes\">Oui</string>\n    <string name=\"action_no\">Non</string>\n    <string name=\"status_downloads_completed\">Tous les téléchargements sont terminés</string>\n    <string name=\"status_perm_map_download\">Pour télécharger les cartes hors connexion, vous devez donner la permission d\\'accéder aux fichiers</string>\n    <string name=\"status_perm_location\">Aucune position ou information radio ne peut être affichée sans la permission d\\'accéder à la position de l\\'appareil</string>\n    <string name=\"status_perm_refresh_agps\">Pour rafraîchir les données AGPS, vous devez donner la permission d\\'accéder à la position de l\\'appareil</string>\n    <string name=\"status_perm_offline_map\">Pour utiliser les cartes hors connexion, vous devez donner la permission d\\'accéder aux fichiers</string>\n    <string name=\"notify_perm_title\">SatStat requiert des permissions</string>\n    <string name=\"notify_perm_body\">Toucher pour accepter ou refuser</string>\n    <string name=\"pref_cid2\">Afficher les deux formats de cellule</string>\n    <string name=\"pref_cid2_summary\">Afficher les deux formes fractionnées et non fractionnées, avec celui sélectionné ci-dessus apparaissant au-dessus</string>\n\n</resources>\n"
  },
  {
    "path": "res/values-hu/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:ignore=\"MissingTranslation\">\n\n    <string name=\"app_name\">SatStat</string>\n    <string name=\"action_settings\">Beállítások</string>\n    <string name=\"action_record\">Rögzítés</string>\n    <string name=\"action_stop_record\">Rögzítés megállítása</string>\n    <string name=\"action_agps\">AGPS adatok újratöltése</string>\n    <string name=\"action_legend\">Jelmagyarázat</string>\n    <string name=\"action_about\">Névjegy</string>\n    <string name=\"error_write_file\">Hiba a fájlba írás közben</string>\n    <string name=\"error_ext_storage\">A külső tároló nem elérhető</string>\n    <string name=\"info_start_rec\">Rögzítés elindítva</string>\n    <string name=\"notify_record_title\">Érzékelő adatok rögzítése</string>\n    <string name=\"notify_record_body\">Érintse meg a rögzítés leállításához</string>\n    <string name=\"status_agps\">AGPS adat újratöltése kérvényezve - az Android nem ad tájékoztatást, hogy sikerült-e a frissítés</string>\n    <string name=\"status_agps_captive\">Nem sikerült frissíteni az adatot – indítsa el a böngészőt, jelentkezzen be a hálózatra, és próbálja meg újra</string>\n    <string name=\"status_agps_error\">Nincs elérhető hálózat, nem lehet újratölteni az AGPS adatokat</string>\n    <string name=\"title_section1\">GPS</string>\n    <string name=\"title_section2\">Érzékelők</string>\n    <string name=\"title_section3\">Vezeték nélküli</string>\n    <string name=\"title_section4\">Térkép</string>\n    <string name=\"title_gps_lat\">Szélesség</string>\n    <string name=\"title_gps_lon\">Hosszúság</string>\n    <string name=\"title_gps_coord\">Koordináták</string>\n    <string name=\"title_gps_decl\">Dekl.</string>\n    <string name=\"title_gps_speed\">Sebesség</string>\n    <string name=\"title_gps_alt\">Magasság</string>\n    <string name=\"title_gps_time\">Legutóbbi fix pozíció</string>\n    <string name=\"title_gps_bear\">Hal. irány</string>\n    <string name=\"title_gps_orient\">Néz. irány</string>\n    <string name=\"title_gps_acc\">Hiba</string>\n    <string name=\"title_gps_sats\">Műholdak</string>\n    <string name=\"title_gps_ttff\">TTFF (mp)</string>\n    <string name=\"value_none\">–</string>\n    <string name=\"value_N\">É</string>\n    <string name=\"value_NNE\">ÉÉK</string>\n    <string name=\"value_NE\">ÉK</string>\n    <string name=\"value_ENE\">KÉK</string>\n    <string name=\"value_E\">K</string>\n    <string name=\"value_ESE\">KDK</string>\n    <string name=\"value_SE\">DK</string>\n    <string name=\"value_SSE\">DDK</string>\n    <string name=\"value_S\">D</string>\n    <string name=\"value_SSW\">DDNy</string>\n    <string name=\"value_SW\">DNy</string>\n    <string name=\"value_WSW\">NyDNy</string>\n    <string name=\"value_W\">Ny</string>\n    <string name=\"value_WNW\">NyÉNy</string>\n    <string name=\"value_NW\">ÉNy</string>\n    <string name=\"value_NNW\">ÉÉNy</string>\n    <string name=\"title_sensor_acc\">Gyorsulás és gravitáció</string>\n    <string name=\"title_sensors_accTotal\">Σg (m/s²)</string>\n    <string name=\"title_sensors_accX\">gx (m/s²)</string>\n    <string name=\"title_sensors_accY\">gy (m/s²)</string>\n    <string name=\"title_sensors_accZ\">gz (m/s²)</string>\n    <string name=\"title_sensor_rot\">Elfordulás</string>\n    <string name=\"title_sensors_rotTotal\">Σω (rad/s)</string>\n    <string name=\"title_sensors_rotX\">ωx (rad/s)</string>\n    <string name=\"title_sensors_rotY\">ωy (rad/s)</string>\n    <string name=\"title_sensors_rotZ\">ωz (rad/s)</string>\n    <string name=\"title_sensor_or\">Irány</string>\n    <string name=\"title_sensors_orAzimuth\">Azimut</string>\n    <string name=\"title_sensors_orAziText\">Égtáj</string>\n    <string name=\"title_sensors_orPitch\">Billenés</string>\n    <string name=\"title_sensors_orRoll\">Dőlés</string>\n    <string name=\"title_sensor_mag\">Mágneses mező</string>\n    <string name=\"title_sensors_magTotal\">ΣB (µT)</string>\n    <string name=\"title_sensors_magX\">Bx (µT)</string>\n    <string name=\"title_sensors_magY\">By (µT)</string>\n    <string name=\"title_sensors_magZ\">Bz (µT)</string>\n    <string name=\"title_sensor_met\">Meteorológia</string>\n    <string name=\"title_sensors_metTemp\">&#x00a0;°C</string>\n    <string name=\"title_sensors_metPressure\">&#x00a0;hPa</string>\n    <string name=\"title_sensors_metHumid\">Relatív páratartalom (%)</string>\n    <string name=\"title_sensor_misc\">Egyebek</string>\n    <string name=\"title_sensors_light\">&#x00a0;lux</string>\n    <string name=\"title_sensors_proximity\">Közelség (cm)</string>\n    <string name=\"title_radio_gsm\">GSM</string>\n    <string name=\"title_radio_mcc\">MCC</string>\n    <string name=\"title_radio_mnc\">MNC</string>\n    <string name=\"title_radio_cellid\">Cella ID</string>\n    <string name=\"title_radio_lac\">LAC</string>\n    <string name=\"title_radio_psc\">PSC</string>\n    <string name=\"title_radio_asu\">ASU</string>\n    <string name=\"title_radio_cdma\">CDMA</string>\n    <string name=\"title_radio_sid\">SID</string>\n    <string name=\"title_radio_nid\">NID</string>\n    <string name=\"title_radio_bsid\">BSID</string>\n    <string name=\"title_radio_lte\">LTE</string>\n    <string name=\"title_radio_tac\">TAC</string>\n    <string name=\"title_radio_pci\">PCI</string>\n    <string name=\"title_radio_wifi\">WiFi</string>\n    <string name=\"title_radio_mac\">BSSID (MAC)</string>\n    <string name=\"title_radio_channel\">Ch</string>\n    <string name=\"title_radio_level\">dBm</string>\n    <string name=\"title_activity_about\">Névjegy</string>\n    <string name=\"about_caption\">Location, Sensor and Radio Network Status</string>\n    <string name=\"about_version\">Verzió</string>\n    <string name=\"about_text\">&lt;p&gt;\n        If something doesn\\'t work the way it should, or if you have an idea to make this\n        app even greater, please report it at:\n        &lt;br/&gt;\n        &lt;a href=\"https://github.com/mvglasow/satstat/issues&gt;https://github.com/mvglasow/satstat/issues&lt;/a&gt;\n        &lt;/p&gt;&lt;p&gt;\n        Copyright &#169; 2013 – 2018 Michael von Glasow and contributors\n        &lt;br/&gt;\n        Portions &#169; Jonathan Stott, k9mail, mapsforge.org, Polidea\n        &lt;/p&gt;&lt;p&gt;\n        This program is free software: you can redistribute it and/or modify\n        it under the terms of the GNU General Public License as published by\n        the Free Software Foundation, either version 3 of the License, or\n        (at your option) any later version.\n        &lt;/p&gt;&lt;p&gt;\n        This program is distributed in the hope that it will be useful,\n        but WITHOUT ANY WARRANTY; without even the implied warranty of\n        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n        GNU General Public License for more details.\n        &lt;/p&gt;&lt;p&gt;\n        You should have received a copy of the GNU General Public License\n        along with this program. If not, see \n        &lt;a href=\"http://www.gnu.org/licenses/\"&gt;http://www.gnu.org/licenses/&lt;/a&gt;.\n        &lt;/p&gt;&lt;p&gt;\n        Map data and information provided by MapQuest, \n        &lt;a href=\"http://www.openstreetmap.org/copyright\"&gt;OpenStreetMap&lt;/a&gt; \n        and contributors, \n        &lt;a href=\"http://wiki.openstreetmap.org/wiki/Legal_FAQ#3a._I_would_like_to_use_OpenStreetMap_maps._How_should_I_credit_you.3F\"&gt;ODbL&lt;/a&gt;.\n        &lt;/p&gt;&lt;p&gt;\n        The source code for this application is at:\n        &lt;br/&gt;\n        &lt;a href=\"https://github.com/mvglasow/satstat&gt;https://github.com/mvglasow/satstat&lt;/a&gt;</string>\n    <string name=\"unit_degree\">°</string>\n    <string name=\"unit_km_h\">&#x00a0;km/h</string>\n    <string name=\"unit_mph\">&#x00a0;mph</string>\n    <string name=\"unit_kn\">&#x00a0;kn</string>\n    <string name=\"unit_meter\">&#x00a0;m</string>\n    <string name=\"unit_feet\">&#x00a0;ft</string>\n    <string name=\"pref_notify\">GPS fix pozíció értesítés</string>\n    <string name=\"pref_notify_summary\">Értesítsen, ha egy program megkapja a GPS pozíciómat</string>\n    <string name=\"pref_search\">GPS keresési értesítés</string>\n    <string name=\"pref_search_summary\">Értesítsen, ha egy program keresi a GPS pozíciómat</string>\n    <string name=\"title_activity_settings\">Beállítások</string>\n    <string name=\"notify_nolocation_title\">Pozíció keresése</string>\n    <string name=\"notify_nolocation_body\">Érintse meg az állapot megtekintéséhez</string>\n    <string name=\"pref_notifications\">Értesítések</string>\n    <string name=\"pref_agps\">AGPS adat frissítése</string>\n    <string name=\"pref_wifi\">WiFi kapcsolatkor</string>\n    <string name=\"pref_wifi_summary\">AGPS adat frissítése, ha az eszközöm WiFi hálózaton van</string>\n    <string name=\"pref_freq\">Frissítési gyakoriság</string>\n\n    <string-array name=\"prefs_freq_keys\">\n        <item>Minden alkalommal</item>\n        <item>Naponta egyszer</item>\n        <item>Kétnaponta</item>\n        <item>Háromnaponta</item>\n        <item>Négynaponta</item>\n        <item>Ötnaponta</item>\n        <item>Hatnaponta</item>\n        <item>Hetente egyszer</item>\n    </string-array>\n\n    <string name=\"pref_lastupdate\">Utolsó frissítés</string>\n    <string name=\"pref_networks\">Hálózatok</string>\n    <string name=\"pref_networks_summary\">AGPS adat frissítése, ha az eszközöm valamelyik kiválasztott hálózathoz csatlakozik</string>\n    <string name=\"pref_map\">Térkép</string>\n    <string name=\"pref_loc_prov\">Pozíció források</string>\n    <string name=\"pref_loc_prov_summary\">Kiválasztott pozícióforrások megjelenítése a térképen</string>\n    <string name=\"pref_data\">Adatmegjelenítés</string>\n    <string name=\"pref_unit\">Metrikus mértékegységek használata</string>\n    <string name=\"pref_unit_summary\">Kikapcsolva angolszász mértékegységek fognak megjelenni (kivéve az érzékelők lapján)</string>\n    <string name=\"pref_knots\">Sebesség megjelenítése csomóban</string>\n    <string name=\"pref_knots_summary\">Ha nincs bejelölve, metrikus vagy angolszász mértékegységek (a fentebb kiválasztottak szerint) fognak megjelenni</string>\n    <string name=\"pref_coord\">Koordináták</string>\n\n    <string-array name=\"prefs_coord_keys\">\n        <item>Tizedes fok</item>\n        <item>Fok, tizedes perc</item>\n        <item>Fok, perc, másodperc</item>\n        <item>MGRS</item>\n        <item>UTM</item>\n    </string-array>\n\n    <string name=\"pref_utc\">GPS idő megjelenítése UTC-ben</string>\n    <string name=\"pref_utc_summary\">A GPS fix pozíció megjelenítése UTC szerint helyi idő helyett.</string>\n    <string name=\"pref_cid\">UMTS/LTE cella ID-k különválasztása</string>\n    <string name=\"pref_cid_summary\">Cella ID-k különválasztása RNCID/CID vagy eNodeB/sector ID szerint</string>\n    <string name=\"title_activity_legend\">Jelmagyarázat</string>\n    <string name=\"title_legend_gps\">GPS</string>\n    <string name=\"title_legend_gps_used\">Fix pozícióhoz felhasznált műholdak</string>\n    <string name=\"title_legend_gps_unused\">Fix pozícióhoz fel nem használt műholdak</string>\n    <string name=\"title_legend_sensors\">Érzékelők</string>\n    <string name=\"title_legend_sensors_high\">Nagy pontosság</string>\n    <string name=\"title_legend_sensors_medium\">Közepes pontosság</string>\n    <string name=\"title_legend_sensors_low\">Alacsony pontosság</string>\n    <string name=\"title_legend_sensors_unreliable\">Megbízhatatlan</string>\n    <string name=\"title_legend_radio\">Vezeték nélküli</string>\n    <string name=\"title_legend_radio_2g\">2G cella</string>\n    <string name=\"title_legend_radio_3g\">3G cella</string>\n    <string name=\"title_legend_radio_4g\">4G cella</string>\n    <string name=\"title_legend_radio_open\">Nyílt WiFi</string>\n    <string name=\"title_legend_radio_wep\">WEP titkosítás</string>\n    <string name=\"title_legend_radio_psk\">WPA-PSK titkosítás</string>\n    <string name=\"title_legend_radio_eap\">WPA-EAP titkosítás</string>\n    <string name=\"title_legend_radio_adhoc\">Ad-hoc WiFi</string>\n    <string name=\"title_legend_radio_unknown\">Ismeretlen WiFi típus</string>\n    <string name=\"title_legend_map\">Térkép</string>\n    <string name=\"title_legend_map_prov\">Pozíció %s forrásból</string>\n    <string name=\"title_legend_map_stale\">Pontatlan pozíció bármely forrásból</string>\n    <string name=\"title_nmea_001_032\">GPS</string>\n    <string name=\"title_nmea_033_054\">SBAS</string>\n    <string name=\"title_nmea_055_064\">55–64</string>\n    <string name=\"title_nmea_065_088\">GLONASS</string>\n    <string name=\"title_nmea_097_192\">97–192</string>\n    <string name=\"title_nmea_193_195\">QZSS</string>\n    <string name=\"title_nmea_201_235\">Beidou</string>\n    <string name=\"title_nmea_236_300\">236–300</string>\n    <string name=\"title_nmea_301_336\">Galileo</string>\n    <string name=\"pref_wifi_sort\">WiFi hálózatok rendezése</string>\n\n    <string-array name=\"prefs_wifi_sort_keys\">\n        <item>BSSID szerint</item>\n        <item>Név szerint</item>\n        <item>Csatorna szerint</item>\n        <item>Jel erősség szerint</item>\n    </string-array>\n\n    <string name=\"pref_map_offline\">Offline térkép használata</string>\n    <string name=\"pref_map_offline_summary\">Térkép rajzolása az eszközön (le kell tölteni hozzá egy térképet)</string>\n    <string name=\"pref_map_path\">Térkép könyvtára</string>\n    <string name=\"map_path_title\">Elérési út a térkép könyvtárához:</string>\n    <string name=\"map_path_fileman\">A térkép könyvtárának kitallózásához telepíteni kell egy támogatott fájlkezelőt.</string>\n    <string name=\"map_path_cm_filemanager\">CM File Manager</string>\n    <string name=\"map_path_oi_filemanager\">OI File Manager</string>\n    <string name=\"action_ok\">OK</string>\n    <string name=\"action_cancel\">Mégse</string>\n    <string name=\"pref_map_download\">Offline térkép letöltése</string>\n    <string name=\"pref_map_download_summary\">Térképek beszerzése a Mapsforge-tól</string>\n    <string name=\"pref_map_purge\">Térképcsempék gyorsítótárának törlése</string>\n    <string name=\"pref_map_purge_summary\">Az összes térképcsempe újratöltésének vagy újrarajzolásának kényszerítése. Hibakereséshez hasznos lehet</string>\n    <string name=\"status_map_purged\">A térképcsempék gyorsítótára kiürítve</string>\n    <string name=\"title_activity_map_download\">Offline térkép letöltése</string>\n    <string name=\"status_folder_empty\">A könyvtár üres</string>\n    <string name=\"status_already_downloading\">Egy azonos nevű térkép letöltése már folyamatban van</string>\n    <string name=\"confirm_download\">Úgy tűnik, már megvan a legfrissebb változatod ebből a térképből. Biztos le akarod tölteni?</string>\n    <string name=\"action_yes\">Igen</string>\n    <string name=\"action_no\">Nem</string>\n    <string name=\"status_downloads_completed\">Minden letöltés befejeződött</string>\n    <string name=\"status_perm_map_download\">Offline térkép letöltéséhez engedélyezned kell a hozzáférést a Tárolóhoz</string>\n    <string name=\"status_perm_location\">A pozíció és a hálózati információk megjelenítéséhez engedélyezned kell a hozzáférést az eszköz helyadataihoz</string>\n    <string name=\"status_perm_refresh_agps\">Az AGPS adatok frissítéséhez engedélyezned kell a hozzáférést az eszköz helyadataihoz</string>\n    <string name=\"status_perm_offline_map\">Offline térkép letöltéséhez engedélyezned kell a hozzáférést a Tárolóhoz</string>\n    <string name=\"notify_perm_title\">A SatStat megigényli a szükséges jogokat</string>\n    <string name=\"notify_perm_body\">Érintsd meg az engedélyezéshez vagy tiltáshoz</string>\n    <string name=\"pref_cid2\">Mindkét cella ID formátum megjelenítése</string>\n    <string name=\"pref_cid2_summary\">Megjelenítés különválasztott és egyben lévő formában is. A fentebb kiválasztott formátum jelenik meg felül.</string>\n    <string name=\"utm_outside_latitude_range\">A pozíció kívül esik az UTM tartományán</string>\n    <string name=\"download_error\">Hiba történt a térképlista letöltése közben</string>\n    <string name=\"download_retry\">Újra</string>\n\n</resources>"
  },
  {
    "path": "res/values-it/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"app_name\">SatStat</string>\n    <string name=\"action_settings\">Configurazione</string>\n    <string name=\"action_record\">Registra</string>\n    <string name=\"action_stop_record\">Termina registrazione</string>\n    <string name=\"action_agps\">Ricarico dati AGPS</string>\n    <string name=\"action_legend\">Legenda</string>\n    <string name=\"action_about\">A proposito</string>\n    <string name=\"error_write_file\">Errore scrivendo file</string>\n    <string name=\"error_ext_storage\">Memoria esterna non disponibile</string>\n    <string name=\"info_start_rec\">Registrazione iniziata</string>\n    <string name=\"notify_record_title\">Registrazione dati</string>\n    <string name=\"notify_record_body\">Tocca per terminare la registrazione</string>\n    <string name=\"status_agps\">Richiesto ricarico dati AGPS – tieni conto che Android non ne rileva l\\'esito</string>\n    <string name=\"status_agps_captive\">Impossibile ricaricare dati – avvia browser, accedi alla rete e riprova</string>\n    <string name=\"status_agps_error\">Nessuna rete disponibile, impossibile ricaricare dati AGPS</string>\n    <string name=\"title_section1\">GPS</string>\n    <string name=\"title_section2\">Sensori</string>\n    <string name=\"title_section3\">Radio</string>\n    <string name=\"title_gps_lat\">Latitudine</string>\n    <string name=\"title_gps_lon\">Longitudine</string>\n    <string name=\"title_gps_coord\">Coordinati</string>\n    <string name=\"title_gps_decl\">Decl.</string>\n    <string name=\"title_gps_speed\">Velocità</string>\n    <string name=\"title_gps_alt\">Altitudine</string>\n    <string name=\"title_gps_time\">Ultima posizione ricevuta</string>\n    <string name=\"title_gps_bear\">Rotta</string>\n    <string name=\"title_gps_orient\">Orientation</string>\n    <string name=\"title_gps_acc\">Errore</string>\n    <string name=\"title_gps_sats\">Satelliti</string>\n    <string name=\"title_gps_ttff\">TTFF (s)</string>\n    <string name=\"value_none\">–</string>\n    <string name=\"value_N\">N</string>\n    <string name=\"value_NNE\">NNE</string>\n    <string name=\"value_NE\">NE</string>\n    <string name=\"value_ENE\">ENE</string>\n    <string name=\"value_E\">E</string>\n    <string name=\"value_ESE\">ESE</string>\n    <string name=\"value_SE\">SE</string>\n    <string name=\"value_SSE\">SSE</string>\n    <string name=\"value_S\">S</string>\n    <string name=\"value_SSW\">SSO</string>\n    <string name=\"value_SW\">SO</string>\n    <string name=\"value_WSW\">OSO</string>\n    <string name=\"value_W\">O</string>\n    <string name=\"value_WNW\">ONO</string>\n    <string name=\"value_NW\">NO</string>\n    <string name=\"value_NNW\">NNO</string>\n    <string name=\"title_sensor_acc\">Accelerazione e gravità</string>\n    <string name=\"title_sensors_accTotal\">Σg (m/s²)</string>\n    <string name=\"title_sensors_accX\">gx (m/s²)</string>\n    <string name=\"title_sensors_accY\">gy (m/s²)</string>\n    <string name=\"title_sensors_accZ\">gz (m/s²)</string>\n    <string name=\"title_sensor_rot\">Rotazione</string>\n    <string name=\"title_sensors_rotTotal\">Σω (rad/s)</string>\n    <string name=\"title_sensors_rotX\">ωx (rad/s)</string>\n    <string name=\"title_sensors_rotY\">ωy (rad/s)</string>\n    <string name=\"title_sensors_rotZ\">ωz (rad/s)</string>\n    <string name=\"title_sensor_or\">Orientamento</string>\n    <string name=\"title_sensors_orAzimuth\">Azimuth</string>\n    <string name=\"title_sensors_orAziText\">Orientation</string>\n    <string name=\"title_sensors_orPitch\">Becch.</string>\n    <string name=\"title_sensors_orRoll\">Rollio</string>\n    <string name=\"title_sensor_mag\">Campo magnetico</string>\n    <string name=\"title_sensors_magTotal\">ΣB (µT)</string>\n    <string name=\"title_sensors_magX\">Bx (µT)</string>\n    <string name=\"title_sensors_magY\">By (µT)</string>\n    <string name=\"title_sensors_magZ\">Bz (µT)</string>\n    <string name=\"title_sensor_met\">Meteorologia</string>\n    <string name=\"title_sensors_metTemp\">&#x00a0;°C</string>\n    <string name=\"title_sensors_metPressure\">&#x00a0;hPa</string>\n    <string name=\"title_sensors_metHumid\">Umidità relativa (%)</string>\n    <string name=\"title_sensor_misc\">Diversi</string>\n    <string name=\"title_sensors_light\">&#x00a0;lux</string>\n    <string name=\"title_sensors_proximity\">Prossimità (cm)</string>\n    <string name=\"title_radio_gsm\">GSM</string>\n    <string name=\"title_radio_mcc\">MCC</string>\n    <string name=\"title_radio_mnc\">MNC</string>\n    <string name=\"title_radio_cellid\">Cell ID</string>\n    <string name=\"title_radio_lac\">LAC</string>\n    <string name=\"title_radio_asu\">ASU</string>\n    <string name=\"title_radio_cdma\">CDMA</string>\n    <string name=\"title_radio_sid\">SID</string>\n    <string name=\"title_radio_nid\">NID</string>\n    <string name=\"title_radio_bsid\">BSID</string>\n    <string name=\"title_radio_wifi\">WiFi</string>\n    <string name=\"title_radio_mac\">BSSID (MAC)</string>\n    <string name=\"title_radio_channel\">C</string>\n    <string name=\"title_radio_level\">dBm</string>\n    <string name=\"title_activity_about\">A proposito</string>\n    <string name=\"about_caption\">Location, Sensor and Radio Network Status</string>\n    <string name=\"about_version\">Versione</string>\n    <string name=\"about_text\">&lt;p&gt;\n        Se qualcosa non funziona come dovrebbe,\n        ovvero se hai un\\'idea come migliorare ancora quest\\'applicazione,\n        segnalalo a:\n        &lt;br/&gt;\n        &lt;a href=\"https://github.com/mvglasow/satstat/issues&gt;https://github.com/mvglasow/satstat/issues&lt;/a&gt;\n        &lt;/p&gt;&lt;p&gt;\n        Copyright &#169; 2013 – 2018 Michael von Glasow and contributors\n        &lt;br/&gt;\n        Portions &#169; Jonathan Stott, k9mail, mapsforge.org, Polidea\n        &lt;/p&gt;&lt;p&gt;\n        This program is free software: you can redistribute it and/or modify\n        it under the terms of the GNU General Public License as published by\n        the Free Software Foundation, either version 3 of the License, or\n        (at your option) any later version.\n        &lt;/p&gt;&lt;p&gt;\n        This program is distributed in the hope that it will be useful,\n        but WITHOUT ANY WARRANTY; without even the implied warranty of\n        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n        GNU General Public License for more details.\n        &lt;/p&gt;&lt;p&gt;\n        You should have received a copy of the GNU General Public License\n        along with this program. If not, see \n        &lt;a href=\"http://www.gnu.org/licenses/\"&gt;http://www.gnu.org/licenses/&lt;/a&gt;.\n        &lt;/p&gt;&lt;p&gt;\n        Map data and information provided by MapQuest, \n        &lt;a href=\"http://www.openstreetmap.org/copyright\"&gt;OpenStreetMap&lt;/a&gt; \n        and contributors, \n        &lt;a href=\"http://wiki.openstreetmap.org/wiki/Legal_FAQ#3a._I_would_like_to_use_OpenStreetMap_maps._How_should_I_credit_you.3F\"&gt;ODbL&lt;/a&gt;.\n        &lt;/p&gt;&lt;p&gt;\n        Il codice sorgente di quest\\'applicazione si trova a:\n        &lt;br/&gt;\n        &lt;a href=\"https://github.com/mvglasow/satstat&gt;https://github.com/mvglasow/satstat&lt;/a&gt;</string>\n    <string name=\"unit_degree\">°</string>\n    <string name=\"unit_km_h\">&#x00a0;km/h</string>\n    <string name=\"unit_kn\">&#x00a0;kn</string>\n    <string name=\"unit_meter\">&#x00a0;m</string>\n    <string name=\"pref_notify\">Notifica posizione GPS</string>\n    <string name=\"pref_notify_summary\">Avvisa quando un\\'applicazione ottiene la mia posizione dal GPS</string>\n    <string name=\"pref_search\">Notifica ricerca GPS</string>\n    <string name=\"pref_search_summary\">Avvisa quando un\\'applicazione cerca di individuare la mia posizione tramite GPS</string>\n    <string name=\"title_activity_settings\">Configurazione</string>\n    <string name=\"notify_nolocation_title\">Ricerca posizione</string>\n    <string name=\"notify_nolocation_body\">Tocca per vedere lo stato</string>\n    <string name=\"pref_notifications\">Notifiche</string>\n    <string name=\"pref_agps\">Ricarico dati AGPS</string>\n    <string name=\"pref_wifi\">Al collegamento WiFi</string>\n    <string name=\"pref_wifi_summary\">Aggiorna dati AGPS quando il mio dispositivo si collega a una rete WiFi</string>\n    <string name=\"pref_freq\">Frequenza aggiornamenti</string>\n\n    <string-array name=\"prefs_freq_keys\">\n        <item>Ogni volta</item>\n        <item>Una volta al giorno</item>\n        <item>Una volta ogni 2 giorni</item>\n        <item>Una volta ogni 3 giorni</item>\n        <item>Una volta ogni 4 giorni</item>\n        <item>Una volta ogni 5 giorni</item>\n        <item>Una volta ogni 6 giorni</item>\n        <item>Una volta ogni settimana</item>\n    </string-array>\n\n    <string name=\"pref_lastupdate\">Ultimo aggiornamento</string>\n    <string name=\"pref_networks\">Reti</string>\n    <string name=\"pref_networks_summary\">Aggiorna dati AGPS quando il mio dispositivo si collega a una delle reti selezionati</string>\n    <string name=\"pref_map\">Mappa</string>\n    <string name=\"pref_loc_prov\">Fonti di localizzazione</string>\n    <string name=\"pref_loc_prov_summary\">Mostrare le fonti di localizzazione selezionati sulla mappa</string>\n    <string name=\"pref_data\">Rappresentazione dati</string>\n    <string name=\"pref_unit\">Usare unità metriche</string>\n    <string name=\"pref_unit_summary\">Se non selezionato, verranno utilizzate delle unità imperiali (eccetto per i sensori)</string>\n    <string name=\"pref_knots\">Mostrare velocità en nodi</string>\n    <string name=\"pref_knots_summary\">Se non selezionato, verranno delle unità metriche o imperiali (secondo l\\'impostazione qui sopra) will be used</string>\n    <string name=\"pref_coord\">Coordinati</string>\n\n    <string-array name=\"prefs_coord_keys\">\n        <item>Gradi decimali</item>\n        <item>Gradi, minuti</item>\n        <item>Gradi, minuti, secondi</item>\n        <item>MGRS</item>\n        <item>UTM</item>\n    </string-array>\n\n    <string name=\"pref_utc\">Mostrare ora GPS in UTC</string>\n    <string name=\"pref_utc_summary\">Usare GPS anziché il fuso orario locale per mostrare il tempo GPS</string>\n    <string name=\"pref_cid\">Suddividere cell ID UMTS/LTE</string>\n    <string name=\"pref_cid_summary\">Suddividere gli ID di cellule UMTS o LTE in RNCID/CID o eNodeB/sector ID</string>\n    <string name=\"title_activity_legend\">Legenda</string>\n    <string name=\"title_legend_gps\">GPS</string>\n    <string name=\"title_legend_gps_used\">Satellite utilizzato per la localizzazione</string>\n    <string name=\"title_legend_gps_unused\">Satellite non utilizzato per la localizzazione</string>\n    <string name=\"title_legend_sensors\">Sensori</string>\n    <string name=\"title_legend_sensors_high\">Accuratezza alta</string>\n    <string name=\"title_legend_sensors_medium\">Accuratezza media</string>\n    <string name=\"title_legend_sensors_low\">Accuratezza bassa</string>\n    <string name=\"title_legend_sensors_unreliable\">Inaffidabile</string>\n    <string name=\"title_legend_radio\">Radio</string>\n    <string name=\"title_legend_radio_2g\">Cellula 2G</string>\n    <string name=\"title_legend_radio_3g\">Cellula 3G</string>\n    <string name=\"title_legend_radio_4g\">Cellula 4G</string>\n    <string name=\"title_legend_radio_open\">WiFi aperto</string>\n    <string name=\"title_legend_radio_wep\">Sicurezza WEP</string>\n    <string name=\"title_legend_radio_psk\">Sicurezza WPA-PSK (personal)</string>\n    <string name=\"title_legend_radio_eap\">Sicurezza WPA-EAP (enterprise)</string>\n    <string name=\"title_legend_radio_adhoc\">WiFi ad-hoc</string>\n    <string name=\"title_legend_radio_unknown\">Tipo WiFi sconosciuto</string>\n    <string name=\"title_legend_map\">Mappa</string>\n    <string name=\"title_legend_map_prov\">Posizione di fonte: %s</string>\n    <string name=\"title_legend_map_stale\">Posizione superata di qualsiasi fonte</string>\n    <string name=\"pref_wifi_sort\">Ordina reti WiFi</string>\n\n    <string-array name=\"prefs_wifi_sort_keys\">\n        <item>Per BSSID</item>\n        <item>Per nome</item>\n        <item>Per canale</item>\n        <item>Per potenza segnale</item>\n    </string-array>\n\n    <string name=\"pref_map_offline\">Utilizza mappa offline</string>\n    <string name=\"pref_map_offline_summary\">Renderizza le mappe sul dispositivo (bisogna scaricare una mappa offline)</string>\n    <string name=\"pref_map_path\">Cartella per mappe offline</string>\n    <string name=\"map_path_title\">Percorso della cartella per mappe offline:</string>\n    <string name=\"map_path_fileman\">Per poter selezionare la cartella, bisogna installare un file manager supportato.</string>\n    <string name=\"action_ok\">OK</string>\n    <string name=\"action_cancel\">Annulla</string>\n    <string name=\"pref_map_download\">Scarica mappa offline</string>\n    <string name=\"pref_map_download_summary\">Scarica mappe di Mapsforge</string>\n    <string name=\"pref_map_purge\">Svuotare tile cache</string>\n    <string name=\"pref_map_purge_summary\">Tutte le tile saranno scaricate o renderizzate di nuovo, utile in caso di problemi</string>\n    <string name=\"status_map_purged\">Tile cache svuotato</string>\n    <string name=\"title_activity_map_download\">Scarica mappa offline</string>\n    <string name=\"status_folder_empty\">La cartella è vuota</string>\n    <string name=\"status_already_downloading\">Un download per una mappa dello stesso nome è già in corso</string>\n    <string name=\"confirm_download\">Sembra che già abbia una copia attuale di questa mappa. Vuoi scaricarla lo stesso?</string>\n    <string name=\"action_yes\">Sì</string>\n    <string name=\"action_no\">No</string>\n    <string name=\"status_downloads_completed\">Tutti i download hanno finito</string>\n    <string name=\"status_perm_map_download\">Per scaricare delle mappe offline, bisogna consentire l\\'accesso alle file sul dispositivo</string>\n    <string name=\"status_perm_location\">Per vedere posizione o reti, bisogna consentire l\\'accesso alla posizione di questo dispositivo</string>\n    <string name=\"status_perm_refresh_agps\">Per ricaricare dati AGPS, bisogna consentire l\\'accesso alla posizione di questo dispositivo</string>\n    <string name=\"status_perm_offline_map\">Per utilizzare delle mappe offline, bisogna consentire l\\'accesso alle file sul dispositivo</string>\n    <string name=\"notify_perm_title\">SatStat sta richiedendo permessi</string>\n    <string name=\"notify_perm_body\">Tocca per consentire o negare</string>\n    <string name=\"pref_cid2\">Mostrare cell ID in entrambi formati</string>\n    <string name=\"pref_cid2_summary\">Mostrare sia la forma suddivisa che quella combinata (quella selezionata sopra verrà mostrata sopra l\\'altra)</string>\n    <string name=\"download_error\">Errore nello scaricamento della lista di mappe dal server</string>\n    <string name=\"download_retry\">Riprova</string>\n    \n</resources>"
  },
  {
    "path": "res/values-lt/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"app_name\">SatStat</string>\n    <string name=\"action_settings\">Nustatymai</string>\n    <string name=\"action_record\">Įrašyti</string>\n    <string name=\"action_stop_record\">Pabaigti įrašymą</string>\n    <string name=\"action_agps\">Atnaujinti AGPS duomenis</string>\n    <string name=\"action_legend\">Legenda</string>\n    <string name=\"action_about\">Apie</string>\n    <string name=\"error_write_file\">Error writing to file</string>\n    <string name=\"error_ext_storage\">External storage not available</string>\n    <string name=\"info_start_rec\">Started recording</string>\n    <string name=\"notify_record_title\">Recording sensor data</string>\n    <string name=\"notify_record_body\">Touch to stop recording</string>\n    <string name=\"status_agps\">AGPS duomenų atnaujinimo užklausa išsiųsta – dėmesio, kad apie atnaujinimo sekmę nepranešiama</string>\n    <string name=\"status_agps_captive\">Negalima atnaujinti duomenų – paleiskite naršyklę, prisijunkite prie tinklo ir bandykite dar kartą</string>\n    <string name=\"status_agps_error\">Galimo tinklo nėra, negalima atnaujinti AGPS duomenų</string>\n    <string name=\"title_section1\">GPS</string>\n    <string name=\"title_section2\">Jutikliai</string>\n    <string name=\"title_section3\">Tinklai</string>\n    <string name=\"title_gps_lat\">Platuma</string>\n    <string name=\"title_gps_lon\">Ilguma</string>\n    <string name=\"title_gps_coord\">Koordinatės</string>\n    <string name=\"title_gps_decl\">Dekl.</string>\n    <string name=\"title_gps_speed\">Greitis</string>\n    <string name=\"title_gps_alt\">Altitudė</string>\n    <string name=\"title_gps_time\">Paskutinis padėties nustatymas</string>\n    <string name=\"title_gps_bear\">Kryptis</string>\n    <string name=\"title_gps_orient\">Orientation</string>\n    <string name=\"title_gps_acc\">Tiksl.</string>\n    <string name=\"title_gps_sats\">Palydovai</string>\n    <string name=\"title_gps_ttff\">TTFF (s)</string>\n    <string name=\"value_none\">–</string>\n    <string name=\"value_N\">Š</string>\n    <string name=\"value_NNE\">ŠŠR</string>\n    <string name=\"value_NE\">ŠR</string>\n    <string name=\"value_ENE\">RŠR</string>\n    <string name=\"value_E\">R</string>\n    <string name=\"value_ESE\">RPR</string>\n    <string name=\"value_SE\">PR</string>\n    <string name=\"value_SSE\">PPR</string>\n    <string name=\"value_S\">P</string>\n    <string name=\"value_SSW\">PPV</string>\n    <string name=\"value_SW\">PV</string>\n    <string name=\"value_WSW\">VPV</string>\n    <string name=\"value_W\">V</string>\n    <string name=\"value_WNW\">VŠV</string>\n    <string name=\"value_NW\">ŠV</string>\n    <string name=\"value_NNW\">ŠŠV</string>\n    <string name=\"title_sensor_acc\">Pagreitis ir gravitacija</string>\n    <string name=\"title_sensors_accTotal\">Σg (m/s²)</string>\n    <string name=\"title_sensors_accX\">gx (m/s²)</string>\n    <string name=\"title_sensors_accY\">gy (m/s²)</string>\n    <string name=\"title_sensors_accZ\">gz (m/s²)</string>\n    <string name=\"title_sensor_rot\">Sukimas</string>\n    <string name=\"title_sensors_rotTotal\">Σω (rad/s)</string>\n    <string name=\"title_sensors_rotX\">ωx (rad/s)</string>\n    <string name=\"title_sensors_rotY\">ωy (rad/s)</string>\n    <string name=\"title_sensors_rotZ\">ωz (rad/s)</string>\n    <string name=\"title_sensor_or\">Orientacija</string>\n    <string name=\"title_sensors_orAzimuth\">Azimutas</string>\n    <string name=\"title_sensors_orAziText\">Orientation</string>\n    <string name=\"title_sensors_orPitch\">Išilginė</string>\n    <string name=\"title_sensors_orRoll\">Šoninė</string>\n    <string name=\"title_sensor_mag\">Magnetinis laukas</string>\n    <string name=\"title_sensors_magTotal\">ΣB (µT)</string>\n    <string name=\"title_sensors_magX\">Bx (µT)</string>\n    <string name=\"title_sensors_magY\">By (µT)</string>\n    <string name=\"title_sensors_magZ\">Bz (µT)</string>\n    <string name=\"title_sensor_met\">Meteorologija</string>\n    <string name=\"title_sensors_metTemp\">&#x00a0;°C</string>\n    <string name=\"title_sensors_metPressure\">&#x00a0;hPa</string>\n    <string name=\"title_sensors_metHumid\">Santyk. drėgnumas (%)</string>\n    <string name=\"title_sensor_misc\">Įvairūs</string>\n    <string name=\"title_sensors_light\">&#x00a0;lux</string>\n    <string name=\"title_sensors_proximity\">Artumas (cm)</string>\n    <string name=\"title_radio_gsm\">GSM</string>\n    <string name=\"title_radio_mcc\">MCC</string>\n    <string name=\"title_radio_mnc\">MNC</string>\n    <string name=\"title_radio_cellid\">Cell ID</string>\n    <string name=\"title_radio_lac\">LAC</string>\n    <string name=\"title_radio_asu\">ASU</string>\n    <string name=\"title_radio_cdma\">CDMA</string>\n    <string name=\"title_radio_sid\">SID</string>\n    <string name=\"title_radio_nid\">NID</string>\n    <string name=\"title_radio_bsid\">BSID</string>\n    <string name=\"title_radio_wifi\">WiFi</string>\n    <string name=\"title_radio_mac\">BSSID (MAC)</string>\n    <string name=\"title_radio_channel\">K.</string>\n    <string name=\"title_radio_level\">dBm</string>\n    <string name=\"title_activity_about\">Apie</string>\n    <string name=\"about_caption\">Location, Sensor and Radio Network Status</string>\n    <string name=\"about_version\">Versija:</string>\n    <string name=\"about_text\">&lt;p&gt;\n        Jeigu kažkas neveikia kaip turėtų, ar jeigu turite\n        mintį kaip dar tobulinti šią programą, prašome pranešti čia:\n        &lt;br/&gt;\n        &lt;a href=\"https://github.com/mvglasow/satstat/issues&gt;https://github.com/mvglasow/satstat/issues&lt;/a&gt;\n        &lt;/p&gt;&lt;p&gt;\n        Copyright &#169; 2013 – 2018 Michael von Glasow and contributors\n        &lt;br/&gt;\n        Portions &#169; Jonathan Stott, k9mail, mapsforge.org, Polidea\n        &lt;/p&gt;&lt;p&gt;\n        This program is free software: you can redistribute it and/or modify\n        it under the terms of the GNU General Public License as published by\n        the Free Software Foundation, either version 3 of the License, or\n        (at your option) any later version.\n        &lt;/p&gt;&lt;p&gt;\n        This program is distributed in the hope that it will be useful,\n        but WITHOUT ANY WARRANTY; without even the implied warranty of\n        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n        GNU General Public License for more details.\n        &lt;/p&gt;&lt;p&gt;\n        You should have received a copy of the GNU General Public License\n        along with this program. If not, see \n        &lt;a href=\"http://www.gnu.org/licenses/\"&gt;http://www.gnu.org/licenses/&lt;/a&gt;.\n        &lt;/p&gt;&lt;p&gt;\n        Map data and information provided by MapQuest, \n        &lt;a href=\"http://www.openstreetmap.org/copyright\"&gt;OpenStreetMap&lt;/a&gt; \n        and contributors, \n        &lt;a href=\"http://wiki.openstreetmap.org/wiki/Legal_FAQ#3a._I_would_like_to_use_OpenStreetMap_maps._How_should_I_credit_you.3F\"&gt;ODbL&lt;/a&gt;.\n        &lt;/p&gt;&lt;p&gt;\n        Šios programos išeitinis kodas yra čia:\n        &lt;br/&gt;\n        &lt;a href=\"https://github.com/mvglasow/satstat&gt;https://github.com/mvglasow/satstat&lt;/a&gt;</string>\n    <string name=\"unit_degree\">°</string>\n    <string name=\"unit_km_h\">&#x00a0;km/h</string>\n    <string name=\"unit_meter\">&#x00a0;m</string>\n    <string name=\"pref_notify\">GPS vietovės įspėjimas</string>\n    <string name=\"pref_notify_summary\">Pranešti, kai programa nustatys mano vietovę naudodama GPS</string>\n    <string name=\"pref_search\">GPS paieškos įspėjimas</string>\n    <string name=\"pref_search_summary\">Pranešti, kai programa ieškos mano vietovės naudodama GPS</string>\n    <string name=\"title_activity_settings\">Nustatymai</string>\n    <string name=\"notify_nolocation_title\">Ieškoma vietovė</string>\n    <string name=\"notify_nolocation_body\">Palieskite, norėdami žiūrėti padėtį</string>\n    <string name=\"pref_notifications\">Pranešimai</string>\n    <string name=\"pref_agps\">AGPS duomenų atnaujinimas</string>\n    <string name=\"pref_wifi\">Prisijungus prie WiFi tinklo</string>\n    <string name=\"pref_wifi_summary\">Atnaujinti AGPS duomenis, kai mano įranga prisijungs prie WiFi tinklo</string>\n    <string name=\"pref_freq\">Atnaujinimų dažnis</string>\n\n    <string-array name=\"prefs_freq_keys\">\n        <item>Kiekvieną kartą</item>\n        <item>Vieną kartą kasdien</item>\n        <item>Vieną kartą kas 2 dienas</item>\n        <item>Vieną kartą kas 3 dienas</item>\n        <item>Vieną kartą kas 4 dienas</item>\n        <item>Vieną kartą kas 5 dienas</item>\n        <item>Vieną kartą kas 6 dienas</item>\n        <item>Vieną kartą kas savaitę</item>\n    </string-array>\n\n    <string name=\"pref_lastupdate\">Paskutinkart atnaujinta</string>\n    <string name=\"pref_networks\">Tinklai</string>\n    <string name=\"pref_networks_summary\">Atnaujinti AGPS duomenis, kai mano įranga prisijungs prie vieno iš išrinktųjų tinklų</string>\n    <string name=\"pref_map\">Žemėlapis</string>\n    <string name=\"pref_loc_prov\">Vietovės šaltiniai</string>\n    <string name=\"pref_loc_prov_summary\">Nurodyti išrinktuosius vietovės šaltinius žemėlapyje</string>\n    <string name=\"pref_data\">Duomenų nurodymas</string>\n    <string name=\"pref_unit\">Naudoti metrinius vienetus</string>\n    <string name=\"pref_unit_summary\">Jeigu neišrinkta, bus naudojami JAV matavimo vinetai (išskyrus jutikliams)</string>\n    <string name=\"pref_knots\">Nurodyti greitį mazgais</string>\n    <string name=\"pref_knots_summary\">Jeigu neišrinkta, bus naudojami metrini arba JAV vienetai (pagal bendrą vienetų nustatymą) will be used</string>\n    <string name=\"pref_coord\">Koordinatės</string>\n\n    <string-array name=\"prefs_coord_keys\">\n        <item>Laipsniais</item>\n        <item>Laipsniais ir minutėmis</item>\n        <item>Laipsniais, minutėmis ir sekundėmis</item>\n        <item>MGRS</item>\n        <item>UTM</item>\n    </string-array>\n\n    <string name=\"pref_utc\">Nurodyti GPS laiką UTC</string>\n    <string name=\"pref_utc_summary\">Naudoti UTC vietienio laiko juosto vietoj GPS laiko nurodymui</string>\n    <string name=\"pref_cid\">Sudalyti UMTS/LTE ląstelių ID kodus</string>\n    <string name=\"pref_cid_summary\">Sudalyti ID kodus į RNCID/CID arba eNodeB/sector ID</string>\n    <string name=\"title_activity_legend\">Legenda</string>\n    <string name=\"title_legend_gps\">GPS</string>\n    <string name=\"title_legend_gps_used\">Palydovas vartojamas vietovei nustatyti</string>\n    <string name=\"title_legend_gps_unused\">Palydovas nevartojamas vietovei nustatyti</string>\n    <string name=\"title_legend_sensors\">Jutikliai</string>\n    <string name=\"title_legend_sensors_high\">Aukštas tikslumas</string>\n    <string name=\"title_legend_sensors_medium\">Vidutinis tikslumas</string>\n    <string name=\"title_legend_sensors_low\">Žemas tikslumas</string>\n    <string name=\"title_legend_sensors_unreliable\">Nepatikimas</string>\n    <string name=\"title_legend_radio\">Tinklai</string>\n    <string name=\"title_legend_radio_2g\">2G ląstelė</string>\n    <string name=\"title_legend_radio_3g\">3G ląstelė</string>\n    <string name=\"title_legend_radio_4g\">4G ląstelė</string>\n    <string name=\"title_legend_radio_open\">Atviras WiFi tinklas</string>\n    <string name=\"title_legend_radio_wep\">WEP saugumas</string>\n    <string name=\"title_legend_radio_psk\">WPA-PSK (asmeninis) saugumas</string>\n    <string name=\"title_legend_radio_eap\">WPA-EAP (verslo) saugumas</string>\n    <string name=\"title_legend_radio_adhoc\">Ad-hoc WiFi</string>\n    <string name=\"title_legend_radio_unknown\">Nepažįstama WiFi rūšis</string>\n    <string name=\"title_legend_map\">Žemėlapis</string>\n    <string name=\"title_legend_map_prov\">%s šaltinio vietovė</string>\n    <string name=\"title_legend_map_stale\">Pasenusi bet kurio šaltinio vietovė</string>\n    <string name=\"pref_wifi_sort\">Ryšioti WiFi tinklus</string>\n\n    <string-array name=\"prefs_wifi_sort_keys\">\n        <item>Pagal BSSID</item>\n        <item>Pagal pavadimimą</item>\n        <item>Pagal kanalą</item>\n        <item>Pagal signalo galią</item>\n    </string-array>\n\n    <string name=\"pref_map_offline\">Naudoti offline žemėlapį</string>\n    <string name=\"pref_map_offline_summary\">Atvaizduoti žemėlapius įrenginyje (reikės atsisiųsti offline žemėlapį)</string>\n    <string name=\"pref_map_path\">Offline žemėlapių aplankas</string>\n    <string name=\"map_path_title\">Offline žemėlapių aplankas:</string>\n    <string name=\"map_path_fileman\">Kad galėtumete peržvelgti aplankus, reikia įdiegti palaikomą failų tvarkyklę.</string>\n    <string name=\"action_ok\">Gerai</string>\n    <string name=\"action_cancel\">Atšaukti</string>\n    <string name=\"pref_map_download\">Atsisiųsti offline žemėlapį</string>\n    <string name=\"pref_map_download_summary\">Atsisiųsti Mapsforge žemėlapių</string>\n    <string name=\"pref_map_purge\">Išvalyti talpykloje saugomas kaladėles</string>\n    <string name=\"pref_map_purge_summary\">Visos žemėlapio kaladėlės bus parsiunčiamos arba atvaizduojamos iš naujo, naudinga bėdašaudystei</string>\n    <string name=\"status_map_purged\">Talpykloje saugomos kaladėlės išvalytos</string>\n    <string name=\"title_activity_map_download\">Atsisiųsti offline žemėlapį</string>\n    <string name=\"status_folder_empty\">Aplankas yra tuščias</string>\n    <string name=\"status_already_downloading\">Jau vyksta to pačio pavadinimo žemėlapio atsisiuntimas</string>\n    <string name=\"confirm_download\">Atrodo, kad jau turite šio žemėlapio naujausią versiją. Ar jį norite vis tiek atsisiųsti?</string>\n    <string name=\"action_yes\">Taip</string>\n    <string name=\"action_no\">Ne</string>\n    <string name=\"status_downloads_completed\">Visi atsisiuntimai yra pabaigti</string>\n    <string name=\"status_perm_map_download\">Kad galėtumete atsisiųsti offline žemėlapius, reikia leidimo failus įrenginyje pasiekti</string>\n    <string name=\"status_perm_location\">Kad galėtumete matyti vietovės arba bevielių tinklų informacijas, reikia leidimo įrenginio vietovės informaciją pasiekti</string>\n    <string name=\"status_perm_refresh_agps\">Kad galėtumete atnaujinti AGPS duomenis, reikia leidimo įrenginio vietovės informaciją pasiekti</string>\n    <string name=\"status_perm_offline_map\">Kad galėtumete naudoti offline žemėlapius, reikia leidimo failus įrenginyje pasiekti</string>\n    <string name=\"notify_perm_title\">SatStat prašo leidimus</string>\n    <string name=\"notify_perm_body\">Palieskite, norėdami leisti arba atmesti</string>\n    <string name=\"pref_cid2\">Nurodyti abi ląstelių ID formas</string>\n    <string name=\"pref_cid2_summary\">Nurodyti ir sudalytą ir nesudalytą formą (anksčiau išrinkta forma bus nurodama viršuje)</string>\n    <string name=\"download_error\">Klaida parsiunčiant žemelapių sąraša</string>\n    <string name=\"download_retry\">Pabandyti dar kartą</string>\n    \n</resources>"
  },
  {
    "path": "res/values-pt-rBR/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:ignore=\"MissingTranslation\">\n\n    <string name=\"app_name\">SatStat</string>\n    <string name=\"action_settings\">Configurações</string>\n    <string name=\"action_record\">Gravar</string>\n    <string name=\"action_stop_record\">Parar gravação</string>\n    <string name=\"action_agps\">Recarregar dados AGPS</string>\n    <string name=\"action_legend\">Legenda</string>\n    <string name=\"action_about\">Sobre</string>\n    <string name=\"error_write_file\">Erro ao escrever para arquivo</string>\n    <string name=\"error_ext_storage\">Armazenamento externo não disponível</string>\n    <string name=\"info_start_rec\">Gravação iniciada</string>\n    <string name=\"notify_record_title\">Gravando dados do sensor</string>\n    <string name=\"notify_record_body\">Toque para parar a gravação</string>\n    <string name=\"status_agps\">Dados AGPS recarregados solicitados – note que o Android não reporta sucesso ou falha</string>\n    <string name=\"status_agps_captive\">Não foi possível atualizar os dados – abra o navegador, autentique-se na rede e tente novamente</string>\n    <string name=\"status_agps_error\">Sem rede disponível, não foi possível recarregar os dados AGPS</string>\n    <string name=\"title_section1\">GPS</string>\n    <string name=\"title_section2\">Sensores</string>\n    <string name=\"title_section3\">Rádio</string>\n    <string name=\"title_section4\">Mapa</string>\n    <string name=\"title_gps_lat\">Latitude</string>\n    <string name=\"title_gps_lon\">Longitude</string>\n    <string name=\"title_gps_coord\">Coordenadas</string>\n    <string name=\"title_gps_decl\">Decl.</string>\n    <string name=\"title_gps_speed\">Velocidade</string>\n    <string name=\"title_gps_alt\">Altitude</string>\n    <string name=\"title_gps_time\">Último Fix Obtido</string>\n    <string name=\"title_gps_bear\">Rolamento</string>\n    <string name=\"title_gps_orient\">Orientação</string>\n    <string name=\"title_gps_acc\">Erro</string>\n    <string name=\"title_gps_sats\">Satélites</string>\n    <string name=\"title_gps_ttff\">TTFF (s)</string>\n    <string name=\"value_none\">–</string>\n    <string name=\"value_N\">N</string>\n    <string name=\"value_NNE\">NNE</string>\n    <string name=\"value_NE\">NE</string>\n    <string name=\"value_ENE\">ENE</string>\n    <string name=\"value_E\">E</string>\n    <string name=\"value_ESE\">ESE</string>\n    <string name=\"value_SE\">SE</string>\n    <string name=\"value_SSE\">SSE</string>\n    <string name=\"value_S\">S</string>\n    <string name=\"value_SSW\">SSW</string>\n    <string name=\"value_SW\">SW</string>\n    <string name=\"value_WSW\">WSW</string>\n    <string name=\"value_W\">W</string>\n    <string name=\"value_WNW\">WNW</string>\n    <string name=\"value_NW\">NW</string>\n    <string name=\"value_NNW\">NNW</string>\n    <string name=\"title_sensor_acc\">Aceleração e Gravidade</string>\n    <string name=\"title_sensors_accTotal\">Σg (m/s²)</string>\n    <string name=\"title_sensors_accX\">gx (m/s²)</string>\n    <string name=\"title_sensors_accY\">gy (m/s²)</string>\n    <string name=\"title_sensors_accZ\">gz (m/s²)</string>\n    <string name=\"title_sensor_rot\">Rotação</string>\n    <string name=\"title_sensors_rotTotal\">Σω (rad/s)</string>\n    <string name=\"title_sensors_rotX\">ωx (rad/s)</string>\n    <string name=\"title_sensors_rotY\">ωy (rad/s)</string>\n    <string name=\"title_sensors_rotZ\">ωz (rad/s)</string>\n    <string name=\"title_sensor_or\">Orientação</string>\n    <string name=\"title_sensors_orAzimuth\">Azimute</string>\n    <string name=\"title_sensors_orAziText\">Orientação</string>\n    <string name=\"title_sensors_orPitch\">Passo</string>\n    <string name=\"title_sensors_orRoll\">Rolo</string>\n    <string name=\"title_sensor_mag\">Campo magnético</string>\n    <string name=\"title_sensors_magTotal\">ΣB (µT)</string>\n    <string name=\"title_sensors_magX\">Bx (µT)</string>\n    <string name=\"title_sensors_magY\">By (µT)</string>\n    <string name=\"title_sensors_magZ\">Bz (µT)</string>\n    <string name=\"title_sensor_met\">Meteorologia</string>\n    <string name=\"title_sensors_metTemp\">&#x00a0;°C</string>\n    <string name=\"title_sensors_metPressure\">&#x00a0;hPa</string>\n    <string name=\"title_sensors_metHumid\">Umidade relativa (%)</string>\n    <string name=\"title_sensor_misc\">Diversas</string>\n    <string name=\"title_sensors_light\">&#x00a0;lux</string>\n    <string name=\"title_sensors_proximity\">Proximidade (cm)</string>\n    <string name=\"title_radio_gsm\">GSM</string>\n    <string name=\"title_radio_mcc\">MCC</string>\n    <string name=\"title_radio_mnc\">MNC</string>\n    <string name=\"title_radio_cellid\">ID da célula</string>\n    <string name=\"title_radio_lac\">LAC</string>\n    <string name=\"title_radio_psc\">PSC</string>\n    <string name=\"title_radio_asu\">ASU</string>\n    <string name=\"title_radio_cdma\">CDMA</string>\n    <string name=\"title_radio_sid\">SID</string>\n    <string name=\"title_radio_nid\">NID</string>\n    <string name=\"title_radio_bsid\">BSID</string>\n    <string name=\"title_radio_lte\">LTE</string>\n    <string name=\"title_radio_tac\">TAC</string>\n    <string name=\"title_radio_pci\">PCI</string>\n    <string name=\"title_radio_wifi\">WiFi</string>\n    <string name=\"title_radio_mac\">BSSID (MAC)</string>\n    <string name=\"title_radio_channel\">Ch</string>\n    <string name=\"title_radio_level\">dBm</string>\n    <string name=\"title_activity_about\">Sobre</string>\n    <string name=\"about_caption\">Localização, Sensor e Estado da Rede de Rádio</string>\n    <string name=\"about_version\">Versão</string>\n    <string name=\"about_text\">&lt;p&gt;\n\t\tSe alguma coisa não funcionar da forma que deveria, ou se você tem uma ideia para fazer este\n\t\taplicativo ainda maior, por favor informe-a em:\n        &lt;br/&gt;\n        &lt;a href=\"https://github.com/mvglasow/satstat/issues&gt;https://github.com/mvglasow/satstat/issues&lt;/a&gt;\n        &lt;/p&gt;&lt;p&gt;\n        Copyright &#169; 2013 – 2018 Michael von Glasow e contribuidores\n        &lt;br/&gt;\n        Porções &#169; Jonathan Stott, k9mail, mapsforge.org, Polidea\n        &lt;/p&gt;&lt;p&gt;\n        Este programa é um software livre; você pode redistribuí-lo e/ou\n        modificá-lo dentro dos termos da Licença Pública Geral GNU como\n        publicada pela Fundação do Software Livre (FSF); na versão 3 da\n        Licença, ou (na sua opinião) qualquer versão.\n        &lt;/p&gt;&lt;p&gt;\n        Este programa é distribuído na esperança de que possa ser útil,\n        mas SEM NENHUMA GARANTIA; sem uma garantia implícita de ADEQUAÇÃO\n        a qualquer MERCADO ou APLICAÇÃO EM PARTICULAR. Veja a\n        Licença Pública Geral GNU para maiores detalhes.\n        &lt;/p&gt;&lt;p&gt;\n        Você deve ter recebido uma cópia da Licença Pública Geral GNU junto\n        com este programa. Se não, veja\n        &lt;a href=\"http://www.gnu.org/licenses/\"&gt;http://www.gnu.org/licenses/&lt;/a&gt;.\n        &lt;/p&gt;&lt;p&gt;\n        Dados e informações de mapa fornecidos por MapQuest, \n        &lt;a href=\"http://www.openstreetmap.org/copyright\"&gt;OpenStreetMap&lt;/a&gt; \n        e contribuidores, \n        &lt;a href=\"http://wiki.openstreetmap.org/wiki/Legal_FAQ#3a._I_would_like_to_use_OpenStreetMap_maps._How_should_I_credit_you.3F\"&gt;ODbL&lt;/a&gt;.\n        &lt;/p&gt;&lt;p&gt;\n        O código-fonte para este aplicativo está em:\n        &lt;br/&gt;\n        &lt;a href=\"https://github.com/mvglasow/satstat&gt;https://github.com/mvglasow/satstat&lt;/a&gt;</string>\n    <string name=\"unit_degree\">°</string>\n    <string name=\"unit_km_h\">&#x00a0;km/h</string>\n    <string name=\"unit_mph\">&#x00a0;mph</string>\n    <string name=\"unit_meter\">&#x00a0;m</string>\n    <string name=\"unit_feet\">&#x00a0;ft</string>\n    <string name=\"pref_notify\">Arrumar notificação do GPS</string>\n    <string name=\"pref_notify_summary\">Notifique-me quando um aplicativo obter minha localização do GPS</string>\n    <string name=\"pref_search\">Notificação de busca do GPS</string>\n    <string name=\"pref_search_summary\">Notifique-me quando um aplicativo estiver procurando pela minha localização</string>\n    <string name=\"title_activity_settings\">Configurações</string>\n    <string name=\"notify_nolocation_title\">Busca por localização</string>\n    <string name=\"notify_nolocation_body\">Toque para ver o estado</string>\n    <string name=\"pref_notifications\">Notificações</string>\n    <string name=\"pref_agps\">Atualização de dados AGPS</string>\n    <string name=\"pref_wifi\">Ao conectar no WiFi</string>\n    <string name=\"pref_wifi_summary\">Atualizar dados AGPS quando meu dispositivo se conectar a uma rede WiFi</string>\n    <string name=\"pref_freq\">Frequência de atualização</string>\n\n    <string-array name=\"prefs_freq_keys\">\n        <item>A cada hora</item>\n        <item>Uma vez por dia</item>\n        <item>Uma vez a cada 2 dias</item>\n        <item>Uma vez a cada 3 dias</item>\n        <item>Uma vez a cada 4 dias</item>\n        <item>Uma vez a cada 5 dias</item>\n        <item>Uma vez a cada 6 dias</item>\n        <item>Uma vez por semana</item>\n    </string-array>\n\n    <string name=\"pref_lastupdate\">Última atualização</string>\n    <string name=\"pref_lastupdate_summary\">%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS</string>\n    <string name=\"pref_networks\">Redes</string>\n    <string name=\"pref_networks_summary\">Atualizar dados AGPS quando meu dispositivo se conectar a uma das redes selecionadas</string>\n    <string name=\"pref_map\">Mapa</string>\n    <string name=\"pref_loc_prov\">Fontes de Localização</string>\n    <string name=\"pref_loc_prov_summary\">Exibir fontes de localização selecionadas no mapa</string>\n    <string name=\"pref_data\">Exibição de dados</string>\n    <string name=\"pref_unit\">Usar unidades métricas</string>\n    <string name=\"pref_unit_summary\">Se não checado, unidades imperiais serão utilizadas (exceto em visualização de sensor)</string>\n    <string name=\"pref_coord\">Coordenadas</string>\n\n    <string-array name=\"prefs_coord_keys\">\n        <item>Decimal graus</item>\n        <item>Graus, minutos</item>\n        <item>Graus, minutos, segundos</item>\n        <item>MGRS</item>\n        <item>UTM</item>\n    </string-array>\n\n    <string name=\"pref_utc\">Exibir hora do GPS em UTC</string>\n    <string name=\"pref_utc_summary\">Exibir hora corrigida do GPS em UTC em vez do horário local</string>\n    <string name=\"pref_cid\">Dividir identificação de células UMTS/LTE</string>\n    <string name=\"pref_cid_summary\">Dividir identificação de células em RNCID/CID ou eNodeB/sector ID</string>\n    <string name=\"title_activity_legend\">Legenda</string>\n    <string name=\"title_legend_gps\">GPS</string>\n    <string name=\"title_legend_gps_used\">Satélite usado na correção</string>\n    <string name=\"title_legend_gps_unused\">Satélite não usado na correção</string>\n    <string name=\"title_legend_sensors\">Sensores</string>\n    <string name=\"title_legend_sensors_high\">Precisão alta</string>\n    <string name=\"title_legend_sensors_medium\">Precisão média</string>\n    <string name=\"title_legend_sensors_low\">Precisão baixa</string>\n    <string name=\"title_legend_sensors_unreliable\">Não confiável</string>\n    <string name=\"title_legend_radio\">Rádio</string>\n    <string name=\"title_legend_radio_2g\">Célula 2G</string>\n    <string name=\"title_legend_radio_3g\">Célula 3G</string>\n    <string name=\"title_legend_radio_4g\">Célula 4G</string>\n    <string name=\"title_legend_radio_open\">WiFi aberto</string>\n    <string name=\"title_legend_radio_wep\">Segurança WEP</string>\n    <string name=\"title_legend_radio_psk\">Segurança WPA-PSK (pessoal)</string>\n    <string name=\"title_legend_radio_eap\">Segurança WPA-EAP (empresarial)</string>\n    <string name=\"title_legend_radio_adhoc\">WiFi Ad-hoc</string>\n    <string name=\"title_legend_radio_unknown\">Tipo de WiFi desconhecido</string>\n    <string name=\"title_legend_map\">Mapa</string>\n    <string name=\"title_legend_map_prov\">Localização do provedor %s</string>\n    <string name=\"title_legend_map_stale\">Localização velha de qualquer provedor</string>\n    <string name=\"title_nmea_001_032\">GPS</string>\n    <string name=\"title_nmea_033_054\">SBAS</string>\n    <string name=\"title_nmea_055_064\">55–64</string>\n    <string name=\"title_nmea_065_088\">GLONASS</string>\n    <string name=\"title_nmea_097_192\">97–192</string>\n    <string name=\"title_nmea_193_195\">QZSS</string>\n    <string name=\"title_nmea_201_235\">Beidou</string>\n    <string name=\"pref_wifi_sort\">Ordenar redes WiFi</string>\n\n    <string-array name=\"prefs_wifi_sort_keys\">\n        <item>Por BSSID</item>\n        <item>Por nome</item>\n        <item>Por canal</item>\n        <item>Por intensidade do sinal</item>\n    </string-array>\n\n    <string name=\"pref_map_offline\">Usar mapa offline</string>\n    <string name=\"pref_map_offline_summary\">Renderizar mapas no dispositivo (requer baixar um mapa offline)</string>\n    <string name=\"pref_map_path\">Pasta de mapas offline</string>\n    <string name=\"map_path_title\">Caminho para a pasta de mapas offline:</string>\n    <string name=\"map_path_fileman\">Para navegar para a pasta de mapas offline, você precisa instalar um gerenciador de arquivos suportado.</string>\n    <string name=\"map_path_cm_filemanager\">CM File Manager</string>\n    <string name=\"map_path_oi_filemanager\">OI File Manager</string>\n    <string name=\"action_ok\">OK</string>\n    <string name=\"action_cancel\">Cancelar</string>\n    <string name=\"pref_map_download\">Baixar mapa offline</string>\n    <string name=\"pref_map_purge\">Limpar cache de ladrilos de mapa</string>\n    <string name=\"pref_map_purge_summary\">Forçar todos os ladrilhos a serem buscados ou renderizados novamente, útil para solução de problemas</string>\n    <string name=\"status_map_purged\">Cache de ladrilhos de mapas limpo</string>\n    <string name=\"title_activity_map_download\">Baixar mapa offline</string>\n    <string name=\"status_folder_empty\">A pasta está vazia</string>\n    <string name=\"status_already_downloading\">Um download para um mapa de mesmo nome já está em progresso</string>\n    <string name=\"confirm_download\">Parece que você já possui uma cópia atualizada deste mapa. Você quer baixá-lo mesmo assim?</string>\n    <string name=\"action_yes\">Sim</string>\n    <string name=\"action_no\">Não</string>\n    <string name=\"status_downloads_completed\">Todos os downloads foram completados</string>\n    <string name=\"status_perm_map_download\">Para baixar mapas offline, você deve permitir o acesso a seus arquivos</string>\n    <string name=\"status_perm_location\">Nenhuma informação de localização ou rádio será mostrada a menos que você autorize o acesso à localização do dispositivo</string>\n    <string name=\"status_perm_refresh_agps\">Para recarregar os dados AGPS, você deve permitir o acesso à localização do dispositivo</string>\n    <string name=\"status_perm_offline_map\">Para usar mapas offline, você deve permitir o acesso a seus arquivos</string>\n    <string name=\"notify_perm_title\">SatStat está pedindo permissões</string>\n    <string name=\"notify_perm_body\">Toque para permitir ou negar</string>\n    <string name=\"pref_cid2\">Mostrar ambos formatos de ID de célula</string>\n    <string name=\"pref_cid2_summary\">Mostrar tanto a forma dividida quanto a não dividida, com a selecionada aparecendo no topo</string>\n\n</resources>\n"
  },
  {
    "path": "res/values-sw600dp/dimens.xml",
    "content": "<resources>\n\n    <!--\n         Customize dimensions originally defined in res/values/dimens.xml (such as\n         screen margins) for sw600dp devices (e.g. 7\" tablets) here.\n    -->\n\n</resources>\n"
  },
  {
    "path": "res/values-sw720dp-land/dimens.xml",
    "content": "<resources>\n\n    <!--\n         Customize dimensions originally defined in res/values/dimens.xml (such as\n         screen margins) for sw720dp devices (e.g. 10\" tablets) in landscape here.\n    -->\n    <dimen name=\"activity_horizontal_margin\">128dp</dimen>\n\n</resources>\n"
  },
  {
    "path": "res/values-v21/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    \n    <!-- Application theme. -->\n    <style name=\"AppTheme\" parent=\"AppBaseTheme\">\n        <!-- API 21 theme customizations can go here. -->\n        <item name=\"android:listSeparatorTextViewStyle\">@style/listSeparatorTextViewStyle</item>\n    </style>\n\n    <style name=\"listSeparatorTextViewStyle\">\n        <item name=\"android:paddingTop\">16dip</item>\n        <item name=\"android:textAppearance\">@android:style/TextAppearance.Material.Body2</item>\n        <item name=\"android:textColor\">?android:attr/colorAccent</item>\n        <item name=\"android:layout_marginBottom\">8dip</item>\n    </style>\n    \n</resources>\n"
  },
  {
    "path": "res/xml/preferences.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\" >\n\n    <PreferenceCategory android:title=\"@string/pref_data\" >\n        <CheckBoxPreference\n            android:defaultValue=\"true\"\n            android:key=\"pref_unit_type\"\n            android:summary=\"@string/pref_unit_summary\"\n            android:title=\"@string/pref_unit\" />\n        <CheckBoxPreference\n            android:defaultValue=\"false\"\n            android:key=\"pref_knots\"\n            android:summary=\"@string/pref_knots_summary\"\n            android:title=\"@string/pref_knots\" />\n\n        <ListPreference\n            android:defaultValue=\"0\"\n            android:entries=\"@array/prefs_coord_keys\"\n            android:entryValues=\"@array/prefs_coord_values\"\n            android:key=\"pref_coord\"\n            android:summary=\"%s\"\n            android:title=\"@string/pref_coord\" />\n\n        <CheckBoxPreference\n            android:defaultValue=\"false\"\n            android:key=\"pref_utc\"\n            android:summary=\"@string/pref_utc_summary\"\n            android:title=\"@string/pref_utc\" />\n        <CheckBoxPreference\n            android:defaultValue=\"false\"\n            android:key=\"pref_cid\"\n            android:summary=\"@string/pref_cid_summary\"\n            android:title=\"@string/pref_cid\" />\n        <CheckBoxPreference\n            android:defaultValue=\"false\"\n            android:key=\"pref_cid2\"\n            android:summary=\"@string/pref_cid2_summary\"\n            android:title=\"@string/pref_cid2\" />\n\n        <ListPreference\n            android:defaultValue=\"0\"\n            android:entries=\"@array/prefs_wifi_sort_keys\"\n            android:entryValues=\"@array/prefs_wifi_sort_values\"\n            android:key=\"pref_wifi_sort\"\n            android:summary=\"%s\"\n            android:title=\"@string/pref_wifi_sort\" />\n    </PreferenceCategory>\n    <PreferenceCategory android:title=\"@string/pref_map\" >\n        <!--\n        Due to an apparent bug in API 17 and lower (possibly also 18),\n        setting android:summary on a MultiSelectListPreference (or descendant)\n        will cause a NPE upon initialization, hence we need to do this programatically.\n         -->\n        <com.vonglasow.michael.satstat.widgets.LocProviderPreference\n            android:key=\"pref_loc_prov\"\n            android:title=\"@string/pref_loc_prov\" />\n\n        <CheckBoxPreference\n            android:defaultValue=\"false\"\n            android:key=\"pref_map_offline\"\n            android:summary=\"@string/pref_map_offline_summary\"\n            android:title=\"@string/pref_map_offline\" />\n\n        <Preference\n            android:defaultValue=\"unset\"\n            android:key=\"pref_map_path\"\n            android:title=\"@string/pref_map_path\" />\n        <Preference\n            android:key=\"pref_map_download\"\n            android:summary=\"@string/pref_map_download_summary\"\n            android:title=\"@string/pref_map_download\" />\n        <Preference\n            android:key=\"pref_map_purge\"\n            android:summary=\"@string/pref_map_purge_summary\"\n            android:title=\"@string/pref_map_purge\" />\n    </PreferenceCategory>\n    <PreferenceCategory android:title=\"@string/pref_notifications\" >\n        <CheckBoxPreference\n            android:defaultValue=\"false\"\n            android:key=\"pref_notify_fix\"\n            android:summary=\"@string/pref_notify_summary\"\n            android:title=\"@string/pref_notify\" />\n        <CheckBoxPreference\n            android:defaultValue=\"false\"\n            android:key=\"pref_notify_search\"\n            android:summary=\"@string/pref_search_summary\"\n            android:title=\"@string/pref_search\" />\n    </PreferenceCategory>\n    <PreferenceCategory\n        android:key=\"pref_agps\"\n        android:title=\"@string/pref_agps\" >\n        <com.vonglasow.michael.satstat.widgets.NetworkTypePreference\n            android:key=\"pref_update_networks\"\n            android:summary=\"@string/pref_networks_summary\"\n            android:title=\"@string/pref_networks\" />\n\n        <ListPreference\n            android:defaultValue=\"1\"\n            android:entries=\"@array/prefs_freq_keys\"\n            android:entryValues=\"@array/prefs_freq_values\"\n            android:key=\"pref_update_freq\"\n            android:summary=\"%s\"\n            android:title=\"@string/pref_freq\" />\n\n        <Preference\n            android:defaultValue=\"0\"\n            android:key=\"pref_update_last\"\n            android:title=\"@string/pref_lastupdate\" />\n    </PreferenceCategory>\n\n</PreferenceScreen>"
  },
  {
    "path": "setbuild.sh",
    "content": "#!/bin/bash\ngit update-index --assume-unchanged res/raw/build.txt\necho git-`git log -1 --pretty=format:%h``git diff-index --quiet HEAD || echo -dirty` > res/raw/build.txt\n"
  },
  {
    "path": "src/com/hzi/UTM.java",
    "content": "package com.hzi;\n\nimport android.content.Context;\nimport android.content.res.Resources;\n\nimport com.vonglasow.michael.satstat.R;\n\nimport java.lang.Math;\n\n/**\n * Created by HZI on 3/3/15.\n * <p/>\n * based on http://robotics.ai.uiuc.edu/~hyoon24/LatLongUTMconversion.py\n */\npublic class UTM {\n    // Test function\n    public static double hzi01(double x) {\n        return (x / 10);\n    }\n\n    public static String lat_lon_to_utm(double Lat, double Long, Context c) {\n\n        double deg2rad = Math.PI / 180.0;\n        double rad2deg = 180.0 / Math.PI;\n\n        // Parameters for WGS-84\n        double a = 6378137.0;\n        double eccSquared = 0.00669438;\n        double k0 = 0.9996;\n\n        double LongTemp = (Long + 180) - (int) ((Long + 180) / 360) * 360 - 180;\n        int ZoneNumber = ((int) (LongTemp + 180) / 6) + 1;\n\n        double LatRad = Lat * deg2rad;\n        double LongRad = LongTemp * deg2rad;\n\n        if (Lat >= 56.0 && Lat < 64.0 && LongTemp >= 3.0 && LongTemp < 12.0) {\n            ZoneNumber = 32;\n        }\n\n        // Special zones for Svalbard\n        if (Lat >= 72.0 && Lat < 84.0)\n            if (LongTemp >= 0.0 && LongTemp < 9.0)\n                ZoneNumber = 31;\n            else if (LongTemp >= 9.0 && LongTemp < 21.0) ZoneNumber = 33;\n            else if (LongTemp >= 21.0 && LongTemp < 33.0) ZoneNumber = 35;\n            else if (LongTemp >= 33.0 && LongTemp < 42.0) ZoneNumber = 37;\n\n        double LongOrigin = (ZoneNumber - 1) * 6 - 180 + 3;\n        double LongOriginRad = LongOrigin * deg2rad;\n\n        double eccPrimeSquared = (eccSquared) / (1 - eccSquared);\n        double N = a / Math.sqrt(1 - eccSquared * Math.sin(LatRad) * Math.sin(LatRad));\n        double T = Math.tan(LatRad) * Math.tan(LatRad);\n        double C = eccPrimeSquared * Math.cos(LatRad) * Math.cos(LatRad);\n        double A = Math.cos(LatRad) * (LongRad - LongOriginRad);\n\n        double M = a * ((1 - eccSquared / 4\n                - 3 * eccSquared * eccSquared / 64\n                - 5 * eccSquared * eccSquared * eccSquared / 256) * LatRad\n                - (3 * eccSquared / 8\n                + 3 * eccSquared * eccSquared / 32\n                + 45 * eccSquared * eccSquared * eccSquared / 1024) * Math.sin(2 * LatRad)\n                + (15 * eccSquared * eccSquared / 256 + 45 * eccSquared * eccSquared * eccSquared / 1024) * Math.sin(4 * LatRad)\n                - (35 * eccSquared * eccSquared * eccSquared / 3072) * Math.sin(6 * LatRad));\n\n        double UTMEasting = (k0 * N * (A + (1 - T + C) * A * A * A / 6\n                + (5 - 18 * T + T * T + 72 * C - 58 * eccPrimeSquared) * A * A * A * A * A / 120)\n                + 500000.0);\n\n        double UTMNorthing = (k0 * (M + N * Math.tan(LatRad) * (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24\n                + (61\n                - 58 * T\n                + T * T\n                + 600 * C\n                - 330 * eccPrimeSquared) * A * A * A * A * A * A / 720)));\n\n        if (Lat > 84 || Lat < -80) {\n            return (c.getString(R.string.utm_outside_latitude_range));\n        } else {\n            if (Lat < 0)\n                UTMNorthing = UTMNorthing + 10000000.0;\n            return (String.format(\"%d / %s / %,d / %,d\", ZoneNumber, ((Lat > 0) ? \"N\" : \"S\"), Math.round(UTMEasting), Math.round(UTMNorthing)));\n        }\n    }\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/Const.java",
    "content": "/*\n * Copyright © 2013–2016 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat;\n\nimport java.io.File;\n\nimport android.net.ConnectivityManager;\nimport android.os.Environment;\n\n/**\n * Constants used throughout the application.\n */\npublic class Const {\n\n\t/**\n\t * Milliseconds per day\n\t */\n\tpublic static final long MILLIS_PER_DAY = 86400000;\n\t\n\t\n\t/**\n\t * Earth circumference in meters\n\t */\n\tpublic static final double EARTH_CIRCUMFERENCE = 40000000; // meters\n\t\n\t\n\t/* Intents */\n\tpublic static final String GPS_ENABLED_CHANGE = \"android.location.GPS_ENABLED_CHANGE\";\n\tpublic static final String GPS_FIX_CHANGE = \"android.location.GPS_FIX_CHANGE\";\n\tpublic static final String AGPS_DATA_EXPIRED = \"com.vonglasow.michael.satstat.AGPS_DATA_EXPIRED\";\n\tpublic static final String DOWNLOAD_RECEIVER_REGISTERED = \"com.vonglasow.michael.satstat.DOWNLOAD_RECEIVER_REGISTERED\";\n\t\n\t\n\t/**\n\t * Available location provider styles\n\t */\n\tpublic static final String [] LOCATION_PROVIDER_STYLES = {\n\t\t\"location_provider_blue\",\n\t\t\"location_provider_green\",\n\t\t\"location_provider_orange\",\n\t\t\"location_provider_purple\",\n\t\t\"location_provider_red\"\n\t};\n\t\n\t\n\t/**\n\t * Index of the marker drawable in the location provider style\n\t */\n\tpublic static final int STYLE_MARKER = 0;\n\t\n\t\n\t/**\n\t * Index of the stroke color in the location provider style\n\t */\n\tpublic static final int STYLE_STROKE = 1;\n\t\n\t\n\t/**\n\t * Index of the fill color in the location provider style\n\t */\n\tpublic static final int STYLE_FILL = 2;\n\t\n\t\n\t/**\n\t * Blue style: default for network location provider\n\t */\n\tpublic static final String LOCATION_PROVIDER_BLUE = \"location_provider_blue\";\n\t\n\t\n\t/**\n\t * Red style: default for GPS location provider\n\t */\n\tpublic static final String LOCATION_PROVIDER_RED = \"location_provider_red\";\n\t\n\t\n\t/**\n\t * Gray style for inactive location providers\n\t */\n\tpublic static final String LOCATION_PROVIDER_GRAY = \"location_provider_gray\";\n\t\n\t\n\t/* Preference keys */\n\tpublic static final String KEY_PREF_NOTIFY_FIX = \"pref_notify_fix\";\n\tpublic static final String KEY_PREF_NOTIFY_SEARCH = \"pref_notify_search\";\n\tpublic static final String KEY_PREF_UPDATE_WIFI = \"pref_update_wifi\";\n\tpublic static final String KEY_PREF_UPDATE_NETWORKS = \"pref_update_networks\";\n\tpublic static final String KEY_PREF_UPDATE_NETWORKS_WIFI = Integer.toString(ConnectivityManager.TYPE_WIFI);\n\tpublic static final String KEY_PREF_UPDATE_NETWORKS_MOBILE = Integer.toString(ConnectivityManager.TYPE_MOBILE);\n\tpublic static final String KEY_PREF_UPDATE_FREQ = \"pref_update_freq\";\n\tpublic static final String KEY_PREF_UPDATE_LAST = \"pref_update_last\";\n\tpublic static final String KEY_PREF_LOC_PROV = \"pref_loc_prov\";\n\tpublic static final String KEY_PREF_LOC_PROV_STYLE = \"pref_loc_prov_style.\";\n\tpublic static final String KEY_PREF_MAP_LAT = \"pref_map_lat\";\n\tpublic static final String KEY_PREF_MAP_LON = \"pref_map_lon\";\n\tpublic static final String KEY_PREF_MAP_ZOOM = \"pref_map_zoom\";\n\tpublic static final String KEY_PREF_UNIT_TYPE = \"pref_unit_type\";\n\tpublic static final String KEY_PREF_KNOTS = \"pref_knots\";\n\tpublic static final String KEY_PREF_MAP_OFFLINE = \"pref_map_offline\";\n\tpublic static final String KEY_PREF_MAP_PATH = \"pref_map_path\";\n\tpublic static final String KEY_PREF_MAP_CACHED_PATH = \"pref_map_cached_path\";\n\tpublic static final String KEY_PREF_MAP_DOWNLOAD = \"pref_map_download\";\n\tpublic static final String KEY_PREF_MAP_PURGE = \"pref_map_purge\";\n\tpublic static final String KEY_PREF_COORD = \"pref_coord\";\n\tpublic static final int KEY_PREF_COORD_DECIMAL = 0;\n\tpublic static final int KEY_PREF_COORD_MIN = 1;\n\tpublic static final int KEY_PREF_COORD_SEC = 2;\n\tpublic static final int KEY_PREF_COORD_MGRS = 3;\n\tpublic static final int KEY_PREF_COORD_UTM = 4;\n\tpublic static final String KEY_PREF_UTC = \"pref_utc\";\n\tpublic static final String KEY_PREF_CID = \"pref_cid\";\n\tpublic static final String KEY_PREF_CID2 = \"pref_cid2\";\n\tpublic static final String KEY_PREF_WIFI_SORT = \"pref_wifi_sort\";\n\n\n\t/**\n\t * Tile cache name for tiles downloaded from OSM\n\t */\n\tpublic static final String TILE_CACHE_OSM = \"OSM\";\n\t\n\t/**\n\t * Tile servers for Mapquest\n\t */\n\tpublic static final String[] TILE_SERVER_OSM = {\n\t\t\"a.tile.openstreetmap.org\",\n\t\t\"b.tile.openstreetmap.org\",\n\t\t\"c.tile.openstreetmap.org\"\n\t};\n\t\n\t/**\n\t * Tile URL for OSM (anything between server name and zoom level)\n\t */\n\tpublic static final String TILE_URL_OSM = \"\";\n\t\n\t/**\n\t * Tile extension for OSM\n\t */\n\tpublic static final String TILE_EXTENSION_OSM = \"png\";\n\n\n\t/**\n\t * Tile cache name for tiles rendered with internal render theme\n\t */\n\tpublic static final String TILE_CACHE_INTERNAL_RENDER_THEME = \"InternalRenderTheme\";\n\n\n\t/**\n\t * MIME type for HTML\n\t */\n\tpublic static final String CONTENT_TYPE_HTML = \"text/html\";\n\n\n\tpublic static final String MAP_PATH_DEFAULT = new File(Environment.getExternalStorageDirectory(), \"org.mapsforge/maps\").getAbsolutePath();\n\n\n\t/**\n\t * Key for a previously saved state of an {@code Activity} instance\n\t */\n\tpublic static final String KEY_SAVED_INSTANCE_STATE = \"savedInstanceState\";\n\t\n\t/**\n\t * Key for a {@link ResultReceiver}\n\t */\n\tpublic static final String KEY_RESULT_RECEIVER = \"resultReceiver\";\n\t\n\t/**\n\t * Key for an array of permissions\n\t */\n\tpublic static final String KEY_PERMISSIONS = \"permissions\";\n\t\n\t/**\n\t * Key for an array of permission grant results\n\t */\n\tpublic static final String KEY_GRANT_RESULTS = \"grantResults\";\n\t\n\t/**\n\t * Key for a request code\n\t */\n\tpublic static final String KEY_REQUEST_CODE = \"requestCode\";\n\t\n\t/**\n\t * Permission request when launching the map download dialog\n\t */\n\tpublic static final int PERM_REQUEST_MAP_DOWNLOAD = 1;\n\n\t/**\n\t * Permission request when registering phone state listener\n\t */\n\tpublic static final int PERM_REQUEST_PHONE_STATE_LISTENER = 2;\n\n\t/**\n\t * Permission request when refreshing AGPS data\n\t */\n\tpublic static final int PERM_REQUEST_REFRESH_AGPS = 3;\n\n\t/**\n\t * Permission request when requesting location updates\n\t */\n\tpublic static final int PERM_REQUEST_LOCATION_UPDATES = 4;\n\n\t/**\n\t * Permission request when requesting cell information\n\t */\n\tpublic static final int PERM_REQUEST_CELL_INFO = 5;\n\n\t/**\n\t * Permission request on startup (all UI permissions)\n\t */\n\tpublic static final int PERM_REQUEST_STARTUP = 6;\n\n\t/**\n\t * Permission request when using offline map\n\t */\n\tpublic static final int PERM_REQUEST_OFFLINE_MAP = 7;\n\t\n\t/**\n\t * Permission request for location access, sent via notification\n\t */\n\tpublic static final int PERM_REQUEST_LOCATION_NOTIFICATION = 8;\n\n\t/**\n\t * Permission request for location access made while changing settings\n\t */\n\tpublic static final int PERM_REQUEST_LOCATION_PREF = 9;\n\n\t/**\n\t * Highest numerical value currently defined for any {@code PERM_REQUEST} constant\n\t */\n\tpublic static final int PERM_REQUEST_MAX = 9;\n\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/GpsEventReceiver.java",
    "content": "/*\n * Copyright © 2013–2016 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport com.vonglasow.michael.satstat.utils.PermissionHelper;\nimport com.vonglasow.michael.satstat.utils.WifiCapabilities;\n\nimport android.Manifest;\nimport android.app.ActivityManager;\nimport android.app.ActivityManager.RunningServiceInfo;\nimport android.app.AlarmManager;\nimport android.app.PendingIntent;\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.content.pm.PackageManager;\nimport android.location.LocationManager;\nimport android.net.ConnectivityManager;\nimport android.net.NetworkInfo;\nimport android.net.wifi.WifiManager;\nimport android.os.AsyncTask;\nimport android.preference.PreferenceManager;\nimport android.support.v4.content.ContextCompat;\nimport android.util.Log;\nimport android.widget.Toast;\n\npublic class GpsEventReceiver extends BroadcastReceiver {\n\tpublic static final String TAG = GpsEventReceiver.class.getSimpleName();\n\n\t/**\n\t * A dummy intent called when a location update is received.\n\t * <p>\n\t * Some devices will refresh AGPS data only when the GPS is accessed. Thus,\n\t * in order to force an AGPS update, we request location updates from the\n\t * GPS and immediately remove updates again. However, in order to request\n\t * location updates we need to supply either a LocationListener or a\n\t * PendingIntent to which the location updates will be delivered. This\n\t * Intent is used to create that PendingIntent. When this Intent is\n\t * received, it will be ignored.\n\t */\n\tpublic static final String LOCATION_UPDATE_RECEIVED = \"com.vonglasow.michael.satstat.LOCATION_UPDATE_RECEIVED\";\n\tprivate static Intent mAgpsIntent = new Intent(Const.AGPS_DATA_EXPIRED);\n\tprivate static Intent mLocationIntent = new Intent(LOCATION_UPDATE_RECEIVED);\n\t\n\t@Override\n\tpublic void onReceive(Context context, Intent intent) {\n\t\tSharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);\n\t\t\n\t\t// some logic to use the pre-1.7 setting KEY_PREF_UPDATE_WIFI as a\n\t\t// fallback if KEY_PREF_UPDATE_NETWORKS is not set\n\t\tSet<String> fallbackUpdateNetworks = new HashSet<String>();\n\t\tif (sharedPref.getBoolean(Const.KEY_PREF_UPDATE_WIFI, false)) {\n\t\t\tfallbackUpdateNetworks.add(Const.KEY_PREF_UPDATE_NETWORKS_WIFI);\n\t\t}\n\t\tSet<String> updateNetworks = sharedPref.getStringSet(Const.KEY_PREF_UPDATE_NETWORKS, fallbackUpdateNetworks);\n\t\t\n\t\tif (intent.getAction().equals(Const.GPS_ENABLED_CHANGE) || intent.getAction().equals(Const.GPS_ENABLED_CHANGE)) {\n\t\t\t//FIXME: why are we checking for the same intent twice? Should on of them be GPS_FIX_CHANGE?\n\t\t\t// an application has connected to GPS or disconnected from it, check if notification needs updating\n\t\t\tboolean notifyFix = sharedPref.getBoolean(Const.KEY_PREF_NOTIFY_FIX, false);\n\t\t\tboolean notifySearch = sharedPref.getBoolean(Const.KEY_PREF_NOTIFY_SEARCH, false);\n\t\t\tif (notifyFix || notifySearch) {\n\t\t\t\tboolean isRunning = false;\n\t\t\t\tActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);\n\t\t\t\tfor (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {\n\t\t\t\t\tif (PasvLocListenerService.class.getName().equals(service.service.getClassName())) {\n\t\t\t\t\t\tisRunning = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!isRunning) {\n\t\t\t\t\tIntent startServiceIntent = new Intent(context, PasvLocListenerService.class);\n\t\t\t\t\tstartServiceIntent.setAction(intent.getAction());\n\t\t\t\t\tstartServiceIntent.putExtras(intent.getExtras());\n\t\t\t\t\tcontext.startService(startServiceIntent);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION) \n\t\t\t\t&& updateNetworks.contains(Const.KEY_PREF_UPDATE_NETWORKS_WIFI)) {\n\t\t\t// change in WiFi connectivity, check if we are connected and need to refresh AGPS\n\t\t\t//FIXME: KEY_PREF_UPDATE_WIFI as fallback only\n\t\t\tNetworkInfo netinfo = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);\n\t\t\tif (netinfo == null) return;\n\t\t\tif (!netinfo.isConnected()) return;\n\t\t\t//Toast.makeText(context, \"WiFi is connected\", Toast.LENGTH_SHORT).show();\n\t\t\tLog.i(this.getClass().getSimpleName(), \"WiFi is connected\");\n\t\t\trefreshAgps(context, true, false);\n\t\t} else if ((intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION )) ||\n\t\t\t\t(intent.getAction().equals(Const.AGPS_DATA_EXPIRED))) {\n\t\t\t// change in network connectivity or AGPS expiration timer fired\n\t\t\tboolean isAgpsExpired = false;\n\t\t\tif (intent.getAction().equals(Const.AGPS_DATA_EXPIRED)) {\n\t\t\t\tLog.i(this.getClass().getSimpleName(), \"AGPS data expired, checking available networks\");\n\t\t\t\tisAgpsExpired = true;\n\t\t\t}\n\t\t\tNetworkInfo netinfo = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();\n\t\t\tif (netinfo == null) return;\n\t\t\tif (!netinfo.isConnected()) return;\n\t\t\tString type;\n\t\t\tif ((netinfo.getType() < ConnectivityManager.TYPE_MOBILE_MMS) || (netinfo.getType() > ConnectivityManager.TYPE_MOBILE_HIPRI)) {\n\t\t\t\ttype = Integer.toString(netinfo.getType());\n\t\t\t} else {\n\t\t\t\t// specific mobile data connections will be treated as TYPE_MOBILE\n\t\t\t\ttype = Const.KEY_PREF_UPDATE_NETWORKS_MOBILE;\n\t\t\t}\n\t\t\tif (!updateNetworks.contains(type)) return;\n\t\t\tif (!isAgpsExpired)\n\t\t\t\tLog.i(this.getClass().getSimpleName(), \"Network of type \" + netinfo.getTypeName() + \" is connected\");\n\t\t\t// Enforce the update interval if we were called by a network event\n\t\t\t// but not if we were called by a timer, because in that case the\n\t\t\t// check has already been done. (I am somewhat paranoid and don't\n\t\t\t// count on alarms not going off a few milliseconds too early.)\n\t\t\trefreshAgps(context, !isAgpsExpired, false);\n\t\t}\n\t}\n\t\n\t/**\n\t * Refreshes AGPS data if necessary.\n\t * \n\t * This method requests a refresh of the AGPS data. It optionally does so\n\t * only after checking when the AGPS data was last refreshed and\n\t * determining if it is stale by adding the refresh interval specified in\n\t * the user preferences and comparing the result against the current time.\n\t * If the result is less than current time, AGPS data is considered stale\n\t * and a refresh is requested.\n\t * \n\t * @param context A {@link Context} to be passed to {@link LocationManager}.\n\t * @param enforceInterval If true, prevents updates when the interval has\n\t * not yet expired. If false, updates are permitted at any time. This is to\n\t * prevent race conditions if alarms fire off too early.\n\t * @param wantFeedback Whether to display a toast informing the user about\n\t * the success of the operation.\n\t */\n\tpublic static void refreshAgps(Context context, boolean enforceInterval, boolean wantFeedback) {\n\t\tSharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);\n\t\tlong last = sharedPref.getLong(Const.KEY_PREF_UPDATE_LAST, 0);\n\t\tlong freqDays = Long.parseLong(sharedPref.getString(Const.KEY_PREF_UPDATE_FREQ, \"0\"));\n\t\tlong now = System.currentTimeMillis();\n\t\tif (enforceInterval && (last + freqDays * Const.MILLIS_PER_DAY > now)) return;\n\t\t//Log.d(GpsEventReceiver.class.getSimpleName(), String.format(\"refreshAgps, enforceInterval: %b, wantFeedback: %b\", enforceInterval, wantFeedback));\n\t\t\n\t\tif (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)\n\t\t\tnew AgpsUpdateTask(wantFeedback).execute(context, mAgpsIntent, sharedPref, freqDays * Const.MILLIS_PER_DAY);\n\t\telse {\n\t\t\tLog.i(TAG, \"Requesting permissions to update AGPS data\");\n\t\t\tPermissionHelper.requestPermissions((SatStatApplication) (context.getApplicationContext()),\n\t\t\t\t\tnew String[]{Manifest.permission.ACCESS_FINE_LOCATION},\n\t\t\t\t\tConst.PERM_REQUEST_LOCATION_NOTIFICATION,\n\t\t\t\t\tcontext.getString(R.string.notify_perm_title),\n\t\t\t\t\tcontext.getString(R.string.notify_perm_body),\n\t\t\t\t\tR.drawable.ic_security);\n\t\t}\n\t}\n\t\n\tprivate static class AgpsUpdateTask extends AsyncTask<Object, Void, Integer> {\n\t\tContext mContext;\n\t\tboolean mWantFeedback = false;\n\t\t\n\t\tpublic AgpsUpdateTask(boolean wantFeedback) {\n\t\t\tsuper();\n\t\t\tmWantFeedback = wantFeedback;\n\t\t}\n\n\t\t/**\n\t\t * @param args[0] A {@link Context} for connecting to the various system services\n\t\t * @param args[1] The {@link Intent} to raise when the next update is due\n\t\t * @param args[2] A {@link SharedPreferences} instance in which the timestamp of the update will be stored\n\t\t * @param args[3] The update frequency, of type {@link Long}, in milliseconds\n\t\t */\n\t\t@Override\n\t\tprotected Integer doInBackground(Object... args) {\n\t\t\tmContext = (Context) args[0];\n\t\t\tIntent agpsIntent = (Intent) args[1];\n\t\t\tSharedPreferences sharedPref = (SharedPreferences) args[2];\n\t\t\tlong freqMillis = (Long) args[3];\n\t\t\t\n\t\t\tint nc = WifiCapabilities.getNetworkConnectivity();\n\t\t\tif (nc == WifiCapabilities.NETWORK_CAPTIVE_PORTAL) {\n\t\t\t\t// portale cattivo che non ci permette di scaricare i dati AGPS\n\t\t\t\tLog.i(GpsEventReceiver.class.getSimpleName(), \"Captive portal detected, cannot update AGPS data\");\n\t\t\t\treturn nc;\n\t\t\t} else if (nc == WifiCapabilities.NETWORK_ERROR) {\n\t\t\t\tLog.i(GpsEventReceiver.class.getSimpleName(), \"No network available, cannot update AGPS data\");\n\t\t\t\treturn nc;\n\t\t\t}\n\t\t\t\n\t\t\tAlarmManager alm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);\n\t\t\tPendingIntent pi = PendingIntent.getBroadcast(mContext, 0, agpsIntent, PendingIntent.FLAG_UPDATE_CURRENT);\n\t\t\talm.cancel(pi);\n\n\t\t\tLocationManager locman = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);\n\t\t\tList<String> allProviders = locman.getAllProviders();\n\t\t\tPendingIntent tempIntent = PendingIntent.getBroadcast(mContext, 0, mLocationIntent, PendingIntent.FLAG_UPDATE_CURRENT);\n\t\t\tLog.i(GpsEventReceiver.class.getSimpleName(), \"Requesting AGPS data update\");\n\t\t\ttry {\n\t\t\t\tif (allProviders.contains(LocationManager.GPS_PROVIDER))\n\t\t\t\t\tlocman.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, tempIntent);\n\t\t\t\tlocman.sendExtraCommand(\"gps\", \"force_xtra_injection\", null);\n\t\t\t\tlocman.sendExtraCommand(\"gps\", \"force_time_injection\", null);\n\t\t\t\tlocman.removeUpdates(tempIntent);\n\t\t\t\t\n\t\t\t\tSharedPreferences.Editor spEditor = sharedPref.edit();\n\t\t\t\tspEditor.putLong(Const.KEY_PREF_UPDATE_LAST, System.currentTimeMillis());\n\t\t\t\tspEditor.commit();\n\t\t\t} catch (SecurityException e) {\n\t\t\t\tLog.w(GpsEventReceiver.class.getSimpleName(), \"Permissions not granted, cannot update AGPS data\");\n\t\t\t}\n\t\t\t\n\t\t\tif (freqMillis > 0) {\n\t\t\t\t// if an update interval is set, prepare an alarm to trigger a new\n\t\t\t\t// update when it elapses (if no interval is set, do nothing as we\n\t\t\t\t// cannot determine a point in time for re-running the update)\n\t\t\t\tlong next = System.currentTimeMillis() + freqMillis;\n\t\t\t\talm.set(AlarmManager.RTC, next, pi);\n\t\t\t\tLog.i(GpsEventReceiver.class.getSimpleName(), String.format(\"Next update due %1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS (after %2$d ms)\", next, freqMillis));\n\t\t\t}\n\n\t\t\treturn nc;\n\t\t}\n\t\t\n\t\t@Override\n\t\tprotected void onPostExecute(Integer result) {\n\t\t\tif ((mContext == null) || !mWantFeedback) return;\n\t\t\tString message = \"\";\n\t\t\tswitch (result) {\n\t\t\tcase WifiCapabilities.NETWORK_AVAILABLE:\n\t\t\t\tmessage = mContext.getString(R.string.status_agps);\n\t\t\t\tbreak;\n\t\t\tcase WifiCapabilities.NETWORK_CAPTIVE_PORTAL:\n\t\t\t\tmessage = mContext.getString(R.string.status_agps_captive);\n\t\t\t\tbreak;\n\t\t\tcase WifiCapabilities.NETWORK_ERROR:\n\t\t\t\tmessage = mContext.getString(R.string.status_agps_error);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tToast.makeText(mContext, message, Toast.LENGTH_SHORT).show();\n\t\t}\n\n\t}\n\n\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/PasvLocListenerService.java",
    "content": "/*\n * Copyright © 2013–2016 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat;\n\nimport com.vonglasow.michael.satstat.ui.MainActivity;\nimport com.vonglasow.michael.satstat.utils.PermissionHelper;\n\nimport uk.me.jstott.jcoord.LatLng;\nimport uk.me.jstott.jcoord.MGRSRef;\nimport android.Manifest;\nimport android.app.Notification;\nimport android.app.PendingIntent;\nimport android.app.Service;\nimport android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback;\nimport android.support.v4.app.TaskStackBuilder;\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.content.SharedPreferences;\nimport android.content.SharedPreferences.OnSharedPreferenceChangeListener;\nimport android.content.pm.PackageManager;\nimport android.location.GpsSatellite;\nimport android.location.GpsStatus;\nimport android.location.Location;\nimport android.location.LocationListener;\nimport android.location.LocationManager;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.preference.PreferenceManager;\nimport android.support.v4.app.NotificationCompat;\nimport android.support.v4.content.ContextCompat;\nimport android.util.Log;\nimport com.hzi.UTM;\n\npublic class PasvLocListenerService extends Service implements GpsStatus.Listener, LocationListener, OnSharedPreferenceChangeListener, OnRequestPermissionsResultCallback {\n\n\t// The unique ID for the notification\n\tprivate static final int ONGOING_NOTIFICATION = 1;\n\t\n\t// GPS status values\n\tprivate static final int GPS_INACTIVE = 0;\n\tprivate static final int GPS_SEARCH = 1;\n\tprivate static final int GPS_FIX = 2;\n\t\n\tprivate int mStatus = GPS_INACTIVE;\n\t\n\tprivate boolean prefUnitType = true;\n\tprivate boolean prefKnots = false;\n\tprivate int prefCoord = Const.KEY_PREF_COORD_DECIMAL;\n\tprivate boolean mNotifyFix = false;\n\tprivate boolean mNotifySearch = false;\n\n\tprivate LocationManager mLocationManager;\n\tprivate NotificationCompat.Builder mBuilder;\n\tprivate SharedPreferences mSharedPreferences;\n\tprivate BroadcastReceiver mGpsStatusReceiver = new BroadcastReceiver() {\n\t\t@Override\n\t\tpublic void onReceive(Context c, Intent intent) {\n\t\t\tif (intent == null) return;\n\t\t\tif (intent.getAction().equals(Const.GPS_ENABLED_CHANGE) && !intent.getBooleanExtra(\"enabled\", true)) {\n\t\t\t\t// GPS_ENABLED_CHANGE, enabled=false: GPS disabled, dismiss notification\n\t\t\t\tmStatus = GPS_INACTIVE;\n\t\t\t\tstopForeground(true);\n\t\t\t} else if (intent.getAction().equals(Const.GPS_FIX_CHANGE) && intent.getBooleanExtra(\"enabled\", false)) {\n\t\t\t\t// GPS_FIX_CHANGE, enabled=true: GPS got fix, will be taken care of in onLocationChanged\n\t\t\t\tmStatus = GPS_FIX;\n\t\t\t} else {\n\t\t\t\t// GPS_ENABLED_CHANGE, enabled=true: GPS enabled\n\t\t\t\t// GPS_FIX_CHANGE, enabled=false: GPS lost fix\n\t\t\t\tmStatus = GPS_SEARCH;\n\t\t\t\tshowStatusNoLocation();\n\t\t\t}\n\t\t}\n\t};\n\n\t@Override\n\tpublic IBinder onBind(Intent arg0) {\n\t\treturn null;\n\t}\n\n\t@Override    \n\tpublic void onCreate() {\n\t\tsuper.onCreate(); //do we need that here?\n\n\t\tmLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);\n\t\tmSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);\n\t\tmSharedPreferences.registerOnSharedPreferenceChangeListener(this);\n\t\tmNotifyFix = mSharedPreferences.getBoolean(Const.KEY_PREF_NOTIFY_FIX, mNotifyFix);\n\t\tmNotifySearch = mSharedPreferences.getBoolean(Const.KEY_PREF_NOTIFY_SEARCH, mNotifySearch);\n\t\tif (mNotifyFix || mNotifySearch)\n\t\t\trequestPermissions();\n\n\t\tregisterReceiver(mGpsStatusReceiver, new IntentFilter(Const.GPS_ENABLED_CHANGE));\n\t\tregisterReceiver(mGpsStatusReceiver, new IntentFilter(Const.GPS_FIX_CHANGE));\n\t}\n\n\t@Override\n\tpublic void onDestroy() {\n\t\tstopForeground(true);\n\t\tunregisterReceiver(mGpsStatusReceiver);\n\t\tmLocationManager.removeUpdates(this);\n    \tmLocationManager.removeGpsStatusListener(this);\n\t\tmSharedPreferences.unregisterOnSharedPreferenceChangeListener(this);\n\t}\n\n\t@Override\n\tpublic void onGpsStatusChanged(int event) {\n\t\tGpsStatus status = mLocationManager.getGpsStatus(null);\n\t\tint satsUsed = 0;\n\t\tIterable<GpsSatellite> sats = status.getSatellites();\n\t\tfor (GpsSatellite sat : sats) {\n\t\t\tif (sat.usedInFix()) {\n\t\t\t\tsatsUsed++;\n\t\t\t}\n\t\t}\n\t\tif (satsUsed == 0) {\n\t\t\tif (mStatus != GPS_INACTIVE)\n\t\t\t\tmStatus = GPS_SEARCH;\n\t\t\tshowStatusNoLocation();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onLocationChanged(Location location) {\n\t\tif (!location.getProvider().equals(LocationManager.GPS_PROVIDER)) return;\n\t\tif (mNotifyFix && (mStatus != GPS_INACTIVE)) {\n\t\t\tmStatus = GPS_FIX;\n\t\t\tGpsStatus status = mLocationManager.getGpsStatus(null);\n\t\t\tint satsInView = 0;\n\t\t\tint satsUsed = 0;\n\t\t\tIterable<GpsSatellite> sats = status.getSatellites();\n\t\t\tfor (GpsSatellite sat : sats) {\n\t\t\t\tsatsInView++;\n\t\t\t\tif (sat.usedInFix()) {\n\t\t\t\t\tsatsUsed++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tdouble lat = Math.abs(location.getLatitude());\n\t\t\tdouble lon = Math.abs(location.getLongitude());\n\t\t\tString ns = (location.getLatitude() > 0)?\n\t\t\t\t\tgetString(R.string.value_N):\n\t\t\t\t\t\t(location.getLatitude() < 0)?\n\t\t\t\t\t\t\t\tgetString(R.string.value_S):\"\";\n\t\t\tString ew = (location.getLongitude() > 0)?\n\t\t\t\t\tgetString(R.string.value_E):\n\t\t\t\t\t\t(location.getLongitude() < 0)?\n\t\t\t\t\t\t\t\tgetString(R.string.value_W):\"\";\n\t\t\tString title = \"\";\n\t\t\tif (prefCoord == Const.KEY_PREF_COORD_DECIMAL) {\n\t\t\t\ttitle = String.format(\"%.5f%s%s %.5f%s%s\",\n\t\t\t\t\t\tlat, getString(R.string.unit_degree), ns,\n\t\t\t\t\t\tlon, getString(R.string.unit_degree), ew);\n\t\t\t} else if (prefCoord == Const.KEY_PREF_COORD_MIN) {\n\t\t\t\tdouble decY = lat;\n\t\t\t\tdouble degY = (int) decY;\n\t\t\t\tdouble minY = Math.abs(60.0 * (decY - degY));\n\t\t\t\tdouble decX = lon;\n\t\t\t\tdouble degX = (int) decX;\n\t\t\t\tdouble minX = Math.abs(60.0 * (decX - degX));\n\t\t\t\ttitle = String.format(\"%.0f%s %.3f' %s %.0f%s %.3f' %s\",\n\t\t\t\t\t\tdegY, getString(R.string.unit_degree), minY + /*rounding*/ 0.0005, ns,\n\t\t\t\t\t\tdegX, getString(R.string.unit_degree), minX + /*rounding*/ 0.0005, ew);\n\t\t\t} else if (prefCoord == Const.KEY_PREF_COORD_SEC) {\n\t\t\t\tdouble decY = lat;\n\t\t\t\tdouble degY = (int) decY;\n\t\t\t\tdouble tmp = 60.0 * (decY - degY);\n\t\t\t\tdouble minY = (int) Math.abs(tmp);\n\t\t\t\tdouble secY = Math.abs(60.0 * (tmp - minY));\n\t\t\t\tdouble decX = lon;\n\t\t\t\tdouble degX = (int) decX;\n\t\t\t\ttmp = 60.0 * (decX - degX);\n\t\t\t\tdouble minX = (int) Math.abs(tmp);\n\t\t\t\tdouble secX = Math.abs(60.0 * (tmp - minX));\n\t\t\t\ttitle = String.format(\"%.0f%s %.0f' %.1f\\\" %s %.0f%s %.0f' %.1f\\\" %s\",\n\t\t\t\t\t\tdegY, getString(R.string.unit_degree), minY, secY + /*rounding*/ 0.05, ns,\n\t\t\t\t\t\tdegX, getString(R.string.unit_degree), minX, secX + /*rounding*/ 0.05, ew);\n\t\t\t} else if (prefCoord == Const.KEY_PREF_COORD_MGRS) {\n\t\t\t\ttitle = new LatLng(location.getLatitude(), location.getLongitude()).toMGRSRef().toString(MGRSRef.PRECISION_1M);\n\t\t\t} else if (prefCoord == Const.KEY_PREF_COORD_UTM) {\n                title = UTM.lat_lon_to_utm(location.getLatitude(), location.getLongitude(), this.getApplicationContext());\n            }\n\n\t\t\tString text = \"\";\n\t\t\tif (location.hasAltitude()) {\n\t\t\t\ttext = text + String.format(\"%.0f%s\",\n\t\t\t\t\t\t(location.getAltitude() * (prefUnitType ? 1 : 3.28084)),\n\t\t\t\t\t\tgetString(((prefUnitType) ? R.string.unit_meter : R.string.unit_feet)));\n\t\t\t}\n\t\t\tif (location.hasSpeed()) {\n\t\t\t\ttext = text + (text.equals(\"\")?\"\":\", \") + String.format(\"%.0f%s\",\n\t\t\t\t\t\t(location.getSpeed() * (prefKnots ? 1.943844 : prefUnitType ? 3.6 : 2.23694)),\n\t\t\t\t\t\tgetString(((prefKnots) ? R.string.unit_kn : (prefUnitType) ? R.string.unit_km_h : R.string.unit_mph)));\n\t\t\t}\n\t\t\tif (location.hasAccuracy()) {\n\t\t\t\ttext = text + (text.equals(\"\")?\"\":\", \") + String.format(\"\\u03b5 = %.0f%s\",\n\t\t\t\t\t\t(location.getAccuracy() * (prefUnitType ? 1 : 3.28084)),\n\t\t\t\t\t\tgetString(((prefUnitType) ? R.string.unit_meter : R.string.unit_feet)));\n\t\t\t}\n\t\t\ttext = text + (text.equals(\"\")?\"\":\", \") + String.format(\"%d/%d\",\n\t\t\t\t\tsatsUsed,\n\t\t\t\t\tsatsInView);\n\t\t\ttext = text + (text.equals(\"\")?\"\":\",\\n\") + String.format(\"TTFF %d s\",\n\t\t\t\t\tstatus.getTimeToFirstFix() / 1000);\n\t\t\tmBuilder.setSmallIcon(R.drawable.ic_stat_notify_location);\n\t\t\tmBuilder.setContentTitle(title);\n\t\t\tmBuilder.setContentText(text);\n\t\t\tmBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(text));\n\t\n\t\t\tstartForeground(ONGOING_NOTIFICATION, mBuilder.build());\n\t\t} else {\n\t\t\tstopForeground(true);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onProviderDisabled(String provider) {\n\t\t// nop\n\n\t}\n\n\t@Override\n\tpublic void onProviderEnabled(String provider) {\n\t\t// nop\n\n\t}\n\n\t@Override\n\tpublic void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {\n\t\tboolean isGranted = false;\n\t\tfor (int i = 0; i < grantResults.length; i++)\n\t\t\tif (permissions[i].equals(Manifest.permission.ACCESS_FINE_LOCATION) && (grantResults[i] == PackageManager.PERMISSION_GRANTED))\n\t\t\t\tisGranted = true;\n\t\tif (isGranted) {\n\t\t\trequestLocationUpdates();\n\t\t\tif (mNotifySearch && (mStatus != GPS_INACTIVE))\n\t\t\t\tshowStatusNoLocation();\n\t\t}\n\t\telse\n\t\t\tLog.w(\"PasvLocListenerService\", \"ACCESS_FINE_LOCATION permission not granted. Location notifications will not be available.\");\n\t}\n\n\t@Override\n\tpublic void onSharedPreferenceChanged(SharedPreferences sharedPreferences,\n\t\t\tString key) {\n\t\tif (key.equals(Const.KEY_PREF_NOTIFY_FIX) || key.equals(Const.KEY_PREF_NOTIFY_SEARCH)) {\n\t\t\tmNotifyFix = sharedPreferences.getBoolean(Const.KEY_PREF_NOTIFY_FIX, mNotifyFix);\n\t\t\tmNotifySearch = sharedPreferences.getBoolean(Const.KEY_PREF_NOTIFY_SEARCH, mNotifySearch);\n\t\t\tif (!(mNotifyFix || mNotifySearch)) {\n\t\t\t\tstopSelf();\n\t\t\t} else\n\t\t\t\trequestPermissions();\n\t\t} else if (key.equals(Const.KEY_PREF_UNIT_TYPE)) {\n\t\t\tprefUnitType = sharedPreferences.getBoolean(Const.KEY_PREF_UNIT_TYPE, prefUnitType);\n\t\t} else if (key.equals(Const.KEY_PREF_KNOTS)) {\n\t\t\tprefKnots = sharedPreferences.getBoolean(Const.KEY_PREF_KNOTS, prefKnots);\n\t\t} else if (key.equals(Const.KEY_PREF_COORD)) {\n\t\t\tprefCoord = Integer.valueOf(sharedPreferences.getString(Const.KEY_PREF_COORD, Integer.toString(prefCoord)));\n\t\t}\n\t}\n\n\t@Override\n\tpublic int onStartCommand(Intent intent, int flags, int startId) {\n\t\tprefUnitType = mSharedPreferences.getBoolean(Const.KEY_PREF_UNIT_TYPE, prefUnitType);\n\t\tprefKnots = mSharedPreferences.getBoolean(Const.KEY_PREF_KNOTS, prefKnots);\n\t\tprefCoord = Integer.valueOf(mSharedPreferences.getString(Const.KEY_PREF_COORD, Integer.toString(prefCoord)));\n\t\tmNotifyFix = mSharedPreferences.getBoolean(Const.KEY_PREF_NOTIFY_FIX, mNotifyFix);\n\t\tmNotifySearch = mSharedPreferences.getBoolean(Const.KEY_PREF_NOTIFY_SEARCH, mNotifySearch);\n\n\t\tif (mLocationManager.getAllProviders().indexOf(LocationManager.PASSIVE_PROVIDER) >= 0) {\n\t\t\tif (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)\n\t\t\t\trequestLocationUpdates();\n\t\t\telse\n\t\t\t\tLog.w(\"PasvLocListenerService\", \"ACCESS_FINE_LOCATION permission not granted. Data display will not be available.\");\n\t\t} else {\n\t\t\tLog.w(\"PasvLocListenerService\", \"No passive location provider found. Data display will not be available.\");\n\t\t}\n\n        mBuilder = new NotificationCompat.Builder(this)\n\t\t.setSmallIcon(R.drawable.ic_stat_notify_location)\n\t\t.setContentTitle(getString(R.string.value_none))\n\t\t.setContentText(getString(R.string.value_none))\n\t\t.setWhen(0)\n\t\t.setVisibility(Notification.VISIBILITY_PUBLIC);\n\n\t\tIntent mainIntent = new Intent(this, MainActivity.class);\n\n\t\tTaskStackBuilder stackBuilder = TaskStackBuilder.create(this);\n\t\tstackBuilder.addParentStack(MainActivity.class);\n\t\tstackBuilder.addNextIntent(mainIntent);\n\n\t\tPendingIntent mainPendingIntent =\n\t\t\t\tstackBuilder.getPendingIntent(\n\t\t\t\t\t\t0,\n\t\t\t\t\t\tPendingIntent.FLAG_UPDATE_CURRENT\n\t\t\t\t\t\t);\n\n\t\tmBuilder.setContentIntent(mainPendingIntent);\n\t\t\n\t\t// if we were started through a broadcast, mGpsStatusReceiver had\n\t\t// no way of picking it up, so we need to forward it manually\n\t\tmGpsStatusReceiver.onReceive(this, intent);\n\n\t\treturn START_STICKY;\n\t}\n\n\t@Override\n\tpublic void onStatusChanged(String provider, int status, Bundle extras) {\n\t}\n\n\tpublic void showStatusNoLocation() {\n\t\tif (mNotifySearch && (mStatus != GPS_INACTIVE)) {\n\t\t\tif (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {\n\t\t\t\tmBuilder.setSmallIcon(R.drawable.ic_stat_notify_nolocation);\n\t\t\t\tmBuilder.setContentTitle(getString(R.string.notify_nolocation_title));\n\t\t\t\tmBuilder.setContentText(getString(R.string.notify_nolocation_body));\n\t\t\t\tmBuilder.setStyle(null);\n\n\t\t\t\tstartForeground(ONGOING_NOTIFICATION, mBuilder.build());\n\t\t\t} else\n\t\t\t\trequestPermissions();\n\t\t} else {\n\t\t\tstopForeground(true);\n\t\t}\n\t}\n\t\n\tprivate void requestLocationUpdates() {\n\t\tmLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0, this);\n\t\tmLocationManager.addGpsStatusListener(this);\n\t}\n\t\n\tprivate void requestPermissions() {\n\t\tif (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {\n\t\t\tLog.i(\"PasvLocListenerService\", \"ACCESS_FINE_LOCATION permission not granted, asking for it...\");\n\t\t\t\n\t\t\t// TODO proper notification content\n\t\t\tPermissionHelper.requestPermissions(this,\n\t\t\t\t\tnew String[]{Manifest.permission.ACCESS_FINE_LOCATION},\n\t\t\t\t\tConst.PERM_REQUEST_LOCATION_NOTIFICATION,\n\t\t\t\t\tgetString(R.string.notify_perm_title),\n\t\t\t\t\tgetString(R.string.notify_perm_body),\n\t\t\t\t\tR.drawable.ic_security);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/SatStatApplication.java",
    "content": "/*\n * Copyright © 2013–2016 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat;\n\nimport android.Manifest;\nimport android.app.Application;\nimport android.content.Intent;\nimport android.content.pm.PackageManager;\nimport android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback;\nimport android.util.Log;\n\npublic class SatStatApplication extends Application implements OnRequestPermissionsResultCallback {\n\n\t@Override\n\tpublic void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {\n\t\tboolean isGranted = false;\n\t\tfor (int i = 0; i < grantResults.length; i++)\n\t\t\tif (permissions[i].equals(Manifest.permission.ACCESS_FINE_LOCATION) && (grantResults[i] == PackageManager.PERMISSION_GRANTED))\n\t\t\t\tisGranted = true;\n\t\tif (isGranted)\n\t\t\tsendBroadcast(new Intent(Const.AGPS_DATA_EXPIRED));\n\t\telse\n\t\t\tLog.w(\"PasvLocListenerService\", \"ACCESS_FINE_LOCATION permission not granted. AGPS data could not be updated.\");\n\t}\n\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/data/CellTower.java",
    "content": "package com.vonglasow.michael.satstat.data;\n\nimport android.telephony.TelephonyManager;\nimport android.util.Log;\n\npublic abstract class CellTower {\n\tpublic static final int SOURCE_CELL_LOCATION = 1;\n\tpublic static final int SOURCE_NEIGHBORING_CELL_INFO = 2;\n\tpublic static final int SOURCE_CELL_INFO = 4;\n\tpublic static final int UNKNOWN = -1;\n\tpublic static final int DBM_UNKNOWN = 85; // 99 is unknown ASU, hence 99 * 2 - 113\n\t\n\tprotected int dbm = DBM_UNKNOWN;\n\tprotected int generation = 0;\n\tprotected boolean serving = false;\n\tprotected int source = 0;\n\t\n\t/**\n\t * Returns the alternate cell identity in text form.\n\t * <p>\n\t * The alternate cell identity is an alternate identifier, apart from the\n\t * globally unique cell identifier, which can be used to identify the cell.\n\t * <p>\n\t * Subclasses for network families that use alternate identifiers must\n\t * override this method to provide a string in the following form:\n\t * <p>\n\t * {@code network:text-id[-id]*}\n\t * <p>\n\t * {@code network} is a string which uniquely identifies the network family.\n\t * It is followed by a colon and a {@code text} which marks the identifier\n\t * as an alternate identifier, a dash and a sequence of {@code id}s in\n\t * hierarchical order (top to bottom), separated by dashes. Leading zeroes\n\t * are stripped from {@code id}s. The {@code id} structure is specific to\n\t * the network family.\n\t * <p>\n\t * Network families that do not use alternate identifiers should inherit\n\t * the default implementation, which returns {@code null}. \n\t */\n\tpublic String getAltText() {\n\t\treturn null;\n\t}\n\n\tpublic int getDbm() {\n\t\treturn dbm;\n\t}\n\n\tpublic int getGeneration() {\n\t\treturn generation;\n\t}\n\t\n\t/**\n\t * Returns the network generation of a phone network type.\n\t * @param networkType The network type as returned by {@link TelephonyManager.getNetworkType}\n     * @return 2, 3 or 4 for 2G, 3G or 4G; 0 for unknown\n\t */\n\tpublic static int getGenerationFromNetworkType(int networkType) {\n\t\tswitch (networkType) {\n\t\tcase TelephonyManager.NETWORK_TYPE_CDMA:\n\t\tcase TelephonyManager.NETWORK_TYPE_EDGE:\n\t\tcase TelephonyManager.NETWORK_TYPE_GPRS:\n\t\tcase TelephonyManager.NETWORK_TYPE_IDEN:\n\t\t\treturn 2;\n\t\tcase TelephonyManager.NETWORK_TYPE_1xRTT:\n\t\tcase TelephonyManager.NETWORK_TYPE_EHRPD:\n\t\tcase TelephonyManager.NETWORK_TYPE_EVDO_0:\n\t\tcase TelephonyManager.NETWORK_TYPE_EVDO_A:\n\t\tcase TelephonyManager.NETWORK_TYPE_EVDO_B:\n\t\tcase TelephonyManager.NETWORK_TYPE_HSDPA:\n\t\tcase TelephonyManager.NETWORK_TYPE_HSPA:\n\t\tcase TelephonyManager.NETWORK_TYPE_HSPAP:\n\t\tcase TelephonyManager.NETWORK_TYPE_HSUPA:\n\t\tcase TelephonyManager.NETWORK_TYPE_UMTS:\n\t\t\treturn 3;\n\t\tcase TelephonyManager.NETWORK_TYPE_LTE:\n\t\t\treturn 4;\n\t\tdefault:\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t/**\n\t * Returns the cell identity in text form.\n\t * <p>\n\t * Subclasses must override this method to provide a string in the following form:\n\t * <p>\n\t * {@code network:id[-id]*}\n\t * <p>\n\t * {@code network} is a string which uniquely identifies the network family.\n\t * It is followed by a colon and a sequence of {@code id}s in hierarchical\n\t * order (top to bottom), separated by dashes. Leading zeroes are stripped\n\t * from {@code id}s. The {@code id} structure is specific to the network \n\t * family. \n\t */\n\tpublic abstract String getText();\n\t\n\t/**\n\t * Whether the cell was included in the last update from any of the sources.\n\t * <p>\n\t * When an update is received from a source, cells that were received in an\n\t * earlier update from the same source have the flag for that source reset\n\t * but are still kept in the list until the next update. Such cells should\n\t * be considered stale and not be displayed in any list of active cells. \n\t * @return {@code true} if the cell has its flag for at least one source set, {@code false} if not\n\t */\n\tpublic boolean hasSource() {\n\t\treturn (source >= 0);\n\t}\n\n\tpublic boolean isCellInfo() {\n\t\treturn ((source & SOURCE_CELL_INFO) == SOURCE_CELL_INFO);\n\t}\n\t\n\tpublic boolean isCellLocation() {\n\t\treturn ((source & SOURCE_CELL_LOCATION) == SOURCE_CELL_LOCATION);\n\t}\n\t\n\tpublic boolean isNeighboringCellInfo() {\n\t\treturn ((source & SOURCE_NEIGHBORING_CELL_INFO) == SOURCE_NEIGHBORING_CELL_INFO);\n\t}\n\t\n\t/**\n\t * Whether the device is currently registered with this cell.\n\t * <p>\n\t * If the cell was updated through a {@link android.telephony.CellLocation},\n\t * this method will always return {@code true}.\n\t */\n\tpublic boolean isServing() {\n\t\treturn (serving || ((this.source & SOURCE_CELL_LOCATION) != 0));\n\t}\n\t\n\t/**\n\t * Determines a \"loose match\" for two parts of a cell ID.\n\t * \n\t * A \"loose match\" will return true if one of its two arguments is {@link #UNKNOWN}, or if both\n\t * arguments are truly equal.\n\t * \n\t * Any part of a cell identification (e.g. MCC, MNC, any area ID, cell ID, scrambling code) can\n\t * be compared in this manner as long as it assigns a value of {@link #UNKNOWN} to values which\n\t * are not known, and only to those.\n\t * \n\t * @param l\n\t * @param r\n\t * @return True for a match, false otherwise\n\t */\n\tpublic static boolean matches(int l, int r) {\n\t\tif ((l == UNKNOWN) || (r == UNKNOWN))\n\t\t\treturn true;\n\t\treturn (l == r);\n\t}\n\n\tpublic void setCellInfo(boolean value) {\n\t\tif (value)\n\t\t\tthis.source = this.source | SOURCE_CELL_INFO;\n\t\telse\n\t\t\tthis.source = this.source & ~SOURCE_CELL_INFO;\n\t}\n\n\tpublic void setCellLocation(boolean value) {\n\t\tif (value)\n\t\t\tthis.source = this.source | SOURCE_CELL_LOCATION;\n\t\telse\n\t\t\tthis.source = this.source & ~SOURCE_CELL_LOCATION;\n\t}\n\n\tpublic void setDbm(int dbm) {\n\t\tthis.dbm = dbm;\n\t}\n\n\tpublic void setGeneration(int generation) {\n\t\tif (this instanceof CellTowerLte)\n\t\t\tLog.d(this.getClass().getSimpleName(), String.format(\"Setting network type to %d for cell %s (%s)\", generation, this.getText(), this.getAltText()));\n\t\tthis.generation = generation;\n\t}\n\n\tpublic void setNeighboringCellInfo(boolean value) {\n\t\tif (value)\n\t\t\tthis.source = this.source | SOURCE_NEIGHBORING_CELL_INFO;\n\t\telse\n\t\t\tthis.source = this.source & ~SOURCE_NEIGHBORING_CELL_INFO;\n\t}\n\n    /**\n     * Sets the network generation based on the phone network type.\n     * <p>\n     * The value set here cannot be retrieved directly, but subsequent calls to\n     * {@link #getGeneration()} will return the corresponding generation.\n     * @param networkType The network type as returned by {@link TelephonyManager.getNetworkType}\n     */\n\tpublic void setNetworkType(int networkType) {\n\t\tif (this instanceof CellTowerLte)\n\t\t\tLog.d(this.getClass().getSimpleName(), String.format(\"Changing network type for cell %s (%s)\", this.getText(), this.getAltText()));\n\t\tthis.generation = getGenerationFromNetworkType(networkType);\n\t}\n\t\n\tpublic void setServing(boolean serving) {\n\t\tthis.serving = serving;\n\t}\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/data/CellTowerCdma.java",
    "content": "package com.vonglasow.michael.satstat.data;\n\npublic class CellTowerCdma extends CellTower {\n\tpublic static final String FAMILY = \"cdma\";\n\t\n\tprivate int bsid;\n\tprivate int nid;\n\tprivate int sid;\n\t\n\tpublic CellTowerCdma(int sid, int nid, int bsid) {\n\t\tsuper();\n\t\tthis.setSid(sid);\n\t\tthis.setNid(nid);\n\t\tthis.setBsid(bsid);\n\t}\n\t\n\tpublic int getBsid() {\n\t\treturn bsid;\n\t}\n\t\n\tpublic int getNid() {\n\t\treturn nid;\n\t}\n\t\n\tpublic int getSid() {\n\t\treturn sid;\n\t}\n\t\n\t/**\n\t * Returns the cell identity in text form.\n\t * <p>\n\t * For CDMA-like networks this string has the following form:\n\t * <p>\n\t * {@code cdma:sid-nid-bsid}\n\t * <p>\n\t * The first is a string which uniquely identifies the network family.\n\t * It is followed by a colon and a sequence of System ID, Network ID and \n\t * Base Station ID, separated by dashes and with no leading zeroes. \n\t */\n\t@Override\n\tpublic String getText() {\n\t\treturn getText(sid, nid, bsid);\n\t}\n\t\n\t/**\n\t * Converts a SID/NID/BSID tuple to an identity string, or {@code null}\n\t * if BSID is invalid.\n\t */\n\tpublic static String getText(int sid, int nid, int bsid) {\n\t\tint iSid = ((sid == -1) || (sid == Integer.MAX_VALUE)) ? CellTower.UNKNOWN : sid;\n\t\tint iNid = ((nid == -1) || (nid == Integer.MAX_VALUE)) ? CellTower.UNKNOWN : nid;\n\t\tif ((bsid == -1) || (bsid == Integer.MAX_VALUE))\n\t\t\treturn null;\n\t\telse\n\t\t\treturn String.format(\"%s:%d-%d-%d\", FAMILY, iSid, iNid, bsid);\n\t}\n\t\n\tpublic void setBsid(int bsid) {\n\t\tif ((bsid != Integer.MAX_VALUE) && (bsid != -1))\n\t\t\tthis.bsid = bsid;\n\t\telse\n\t\t\tthis.bsid = CellTower.UNKNOWN;\n\t}\n\t\n\tpublic void setNid(int nid) {\n\t\tif ((nid != Integer.MAX_VALUE) && (nid != -1))\n\t\t\tthis.nid = nid;\n\t\telse\n\t\t\tthis.nid = CellTower.UNKNOWN;\n\t}\n\t\n\tpublic void setSid(int sid) {\n\t\tif ((sid != Integer.MAX_VALUE) && (sid != -1))\n\t\t\tthis.sid = sid;\n\t\telse\n\t\t\tthis.sid = CellTower.UNKNOWN;\n\t}\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/data/CellTowerGsm.java",
    "content": "package com.vonglasow.michael.satstat.data;\n\npublic class CellTowerGsm extends CellTower {\n\tpublic static final String ALT_ID = \"pci\";\n\tpublic static final String FAMILY = \"gsm\";\n\tpublic static final int MAX_2G_CID = 65535;\n\t\n\tprivate int cid;\n\tprivate int lac;\n\tprivate int mcc;\n\tprivate int mnc;\n\tprivate int psc;\n\t\n\tpublic CellTowerGsm(int mcc, int mnc, int lac, int cid, int psc) {\n\t\tsuper();\n\t\tthis.setMcc(mcc);\n\t\tthis.setMnc(mnc);\n\t\tthis.setLac(lac);\n\t\tthis.setCid(cid);\n\t\tthis.setPsc(psc);\n\t}\n\t\n\t/**\n\t * Returns the alternate cell identity in text form.\n\t * <p>\n\t * For UMTS networks this string has the following form:\n\t * <p>\n\t * {@code gsm:psc-nnn}\n\t * <p>\n\t * The first is a string which uniquely identifies the network family.\n\t * It is followed by a colon, the string {@code psc} to denote an alternate\n\t * cell identity, a dash and the primary scrambling code (PSC) with no\n\t * leading zeroes.\n\t * <p> \n\t * A result is only returned for UMTS (3G) cells with a valid PSC. In all\n\t * other cases, {@code null} is returned. \n\t */\n\tpublic String getAltText() {\n\t\treturn getAltText(this.psc);\n\t}\n\n\t/**\n\t * Converts a PSC to an alternate identity string, or {@code null} if the\n\t * PSC is invalid. \n\t */\n\tpublic static String getAltText(int psc) {\n\t\tif ((psc == CellTower.UNKNOWN) || (psc == Integer.MAX_VALUE))\n\t\t\treturn null;\n\t\treturn String.format(\"%s:%s-%d\", FAMILY, ALT_ID, psc);\n\t}\n\t\n\tpublic int getCid() {\n\t\treturn this.cid;\n\t}\n\t\n\tpublic int getLac() {\n\t\treturn this.lac;\n\t}\n\t\n\tpublic int getMcc() {\n\t\treturn this.mcc;\n\t}\n\t\n\tpublic int getMnc() {\n\t\treturn this.mnc;\n\t}\n\n\tpublic int getPsc() {\n\t\treturn this.psc;\n\t}\n\n\t/**\n\t * Returns the cell identity in text form.\n\t * <p>\n\t * For GSM-like networks this string has the following form:\n\t * <p>\n\t * {@code gsm:mcc-mnc-lac-cid}\n\t * <p>\n\t * The first is a string which uniquely identifies the network family.\n\t * It is followed by a colon and a sequence of country code, network code,\n\t * Local Area Code and Cell ID, separated by dashes and with no leading\n\t * zeroes. \n\t */\n\t@Override\n\tpublic String getText() {\n\t\treturn getText(mcc, mnc, lac, cid);\n\t}\n\t\n\t/**\n\t * Converts a MCC/MNC/LAC/CID tuple to an identity string, or\n\t * {@code null} if CID is invalid.\n\t */\n\tpublic static String getText(int mcc, int mnc, int lac, int cid) {\n\t\tint iMcc = ((mcc == -1) || (mcc == Integer.MAX_VALUE)) ? CellTower.UNKNOWN : mcc;\n\t\tint iMnc = ((mnc == -1) || (mnc == Integer.MAX_VALUE)) ? CellTower.UNKNOWN : mnc;\n\t\tint iLac = ((lac == -1) || (lac == Integer.MAX_VALUE)) ? CellTower.UNKNOWN : lac;\n\t\tif ((cid == -1) || (cid == Integer.MAX_VALUE))\n\t\t\treturn null;\n\t\telse\n\t\t\treturn String.format(\"%s:%d-%d-%d-%d\", FAMILY, iMcc, iMnc, iLac, cid);\n\t}\n\t\n\t/**\n\t * Sets signal strength dBm based on ASU.\n\t * <p>\n\t * ASU can be converted into dBm with the formula:\n\t * {@code dBm = -113 + 2 * asu}. The reporting range is from -113 dBm to\n\t * -51 dBm (0 to 31). Values outside this range will be ignored. Refer to\n\t * 3GPP TS 27.007 (Ver 10.3.0) Sec 8.69\n\t */\n\t// or 3GPP TS 27.007 8.5\n\tpublic void setAsu(int asu){\n\t\tif ((asu >= 0) || (asu <= 31))\n\t\t\tthis.setDbm(-113 + 2 * asu);\n\t}\n\t\n\tpublic void setCid(int cid) {\n\t\tif ((cid != Integer.MAX_VALUE) && (cid != -1))\n\t\t\tthis.cid = cid;\n\t\telse\n\t\t\tthis.cid = CellTower.UNKNOWN;\n\t\tif (this.cid > MAX_2G_CID)\n\t\t\tthis.generation = 3;\n\t}\n\t\n\t/**\n\t * Sets signal strength dBm based on CPICH RSCP.\n\t * <p>\n\t * RSCP is Received Signal Code Power. This value can be converted into dBm\n\t * with the formula: {@code dBm = rscp - 116}. The reporting range for\n\t * CPICH RSCP is from -120 dBm to -25 dBm (-5 to 91, with the two extremes\n\t * indicating any RSCP outside the reporting range). Values outside this\n\t * range will be ignored. Refer to 3GPP TS 25.133 (Ver 10.2.0) 9.1.1.3.\n\t * <p>\n\t * Note: It seems that at least on some devices\n\t * {@link android.telephony.NeighboringCellInfo#getRssi()} returns RSCP in\n\t * dBm rather than the index specified in the document. This method\n\t * attempts to guess the unit and set signal strength to the correct value\n\t * in either case. See\n\t * http://stackoverflow.com/questions/26620378/android-neighboringcellinfo-getrssi-returning-weird-data-for-umts-cells\n\t */\n\tpublic void setCpichRscp(int rscp){\n\t\tif ((rscp >= -5) && (rscp <= 91))\n\t\t\tthis.setDbm(rscp - 116);\n\t\t\telse if ((rscp >= -121) && (rscp <=-25))\n\t\t\t\tthis.setDbm(rscp);\n\t}\n\t\n\tpublic void setLac(int lac) {\n\t\tif ((lac != Integer.MAX_VALUE) && (lac != -1))\n\t\t\tthis.lac = lac;\n\t\telse\n\t\t\tthis.lac = CellTower.UNKNOWN;\n\t}\n\t\n\tpublic void setMcc(int mcc) {\n\t\tif ((mcc != Integer.MAX_VALUE) && (mcc != -1))\n\t\t\tthis.mcc = mcc;\n\t\telse\n\t\t\tthis.mcc = CellTower.UNKNOWN;\n\t}\n\t\n\tpublic void setMnc(int mnc) {\n\t\tif ((mcc != Integer.MAX_VALUE) && (mcc != -1))\n\t\t\tthis.mnc = mnc;\n\t\telse\n\t\t\tthis.mnc = CellTower.UNKNOWN;\n\t}\n\t\n\tpublic void setPsc(int psc) {\n\t\tif ((psc != Integer.MAX_VALUE) && (psc != -1)) {\n\t\t\tthis.psc = psc;\n\t\t\tthis.generation = 3;\n\t\t} else\n\t\t\tthis.psc = CellTower.UNKNOWN;\n\t}\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/data/CellTowerList.java",
    "content": "package com.vonglasow.michael.satstat.data;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic abstract class CellTowerList<T extends CellTower> extends HashMap<String, T> {\n\t/**\n\t * Returns all entries in the list.\n\t * <p>\n\t * This method returns all entries in the list, with duplicates eliminated.\n\t * It is preferred over {@link #values()}, which may return duplicates.\n\t * @return\n\t */\n\tpublic Set<T> getAll() {\n\t\tSet<T> result = new HashSet<T>(this.values());\n\t\treturn result;\n\t}\n\t\n\t/**\n\t * Removes cells of the specified source.\n\t * <p>\n\t * This method clears the flags corresponding to {@code source} in the\n\t * internal source field of all entries, and removes entries whose source\n\t * field is null. Call this method prior to adding new data from a source,\n\t * to tell the list that any cell information previously supplied by this\n\t * source is no longer current.\n\t * @param source Any combination of \n\t * {@link com.michael.vonglasow.satstat.data.CellTower#SOURCE_CELL_LOCATION},\n\t * {@link com.michael.vonglasow.satstat.data.CellTower#SOURCE_NEIGHBORING_CELL_INFO}\n\t * or {@link com.michael.vonglasow.satstat.data.CellTower#SOURCE_CELL_INFO}.\n\t */\n\tpublic void removeSource(int source) {\n\t\tArrayList<String> toDelete = new ArrayList<String>();\n\t\tfor (String entry : this.keySet()) {\n\t\t\tCellTower ct = this.get(entry);\n\t\t\tct.source = ct.source & ~source;\n\t\t\tif (ct.source == 0)\n\t\t\t\ttoDelete.add(entry);\n\t\t}\n\t\tfor (String entry : toDelete)\n\t\t\tthis.remove(entry);\n\t}\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/data/CellTowerListCdma.java",
    "content": "package com.vonglasow.michael.satstat.data;\n\nimport java.util.List;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\nimport android.telephony.CellIdentityCdma;\nimport android.telephony.CellInfo;\nimport android.telephony.CellInfoCdma;\nimport android.telephony.cdma.CdmaCellLocation;\n\npublic class CellTowerListCdma extends CellTowerList<CellTowerCdma> {\n\t/**\n\t * Returns the cell tower with the specified data, or {@code null} if it is not in the list. \n\t */\n\tpublic CellTowerCdma get(int sid, int nid, int bsid) {\n\t\tString entry = CellTowerCdma.getText(sid, nid, bsid);\n\t\tif (entry == null)\n\t\t\treturn null;\n\t\telse\n\t\t\treturn this.get(entry);\n\t}\n\t\n\t/**\n\t * Adds or updates a cell tower.\n\t * <p>\n\t * If the cell tower is already in the list, it is replaced; if not, a new\n\t * entry is created.\n\t * <p>\n\t * This method will set the cell's identity data. After this call,\n\t * {@link #isServing()} will return {@code true} for this cell. \n\t * @return The new or updated entry.\n\t */\n\tpublic CellTowerCdma update(CdmaCellLocation location) {\n\t\tCellTowerCdma result = this.get(location.getSystemId(), location.getNetworkId(), location.getBaseStationId());\n\t\tif (result == null) {\n\t\t\tresult = new CellTowerCdma(location.getSystemId(), location.getNetworkId(), location.getBaseStationId());\n\t\t\tthis.put(result.getText(), result);\n\t\t}\n\t\tresult.setCellLocation(true);\n\t\treturn result;\n\t}\n\t\n\t/**\n\t * Adds or updates a cell tower.\n\t * <p>\n\t * If the cell tower is already in the list, it is replaced; if not, a new\n\t * entry is created.\n\t * <p>\n\t * This method will set the cell's identity data, its signal strength and\n\t * whether it is the currently serving cell. If the API level is 18 or \n\t * higher, it will also set the generation.\n\t * @return The new or updated entry.\n\t */\n\t@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\n\tpublic CellTowerCdma update(CellInfoCdma cell) {\n\t\tif (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) \n\t\t\treturn null;\n\t\tCellIdentityCdma cid = cell.getCellIdentity();\n\t\tCellTowerCdma result = this.get(cid.getSystemId(), cid.getNetworkId(), cid.getBasestationId());\n\t\tif (result == null) {\n\t\t\tresult = new CellTowerCdma(cid.getSystemId(), cid.getNetworkId(), cid.getBasestationId());\n\t\t\tthis.put(result.getText(), result);\n\t\t}\n\t\tresult.setCellInfo(true);\n\t\tresult.setDbm(cell.getCellSignalStrength().getDbm());\n\t\tresult.setServing(cell.isRegistered());\n\t\treturn result;\n\t}\n\t\n\t/**\n\t * Adds or updates a list of cell towers.\n\t * <p>\n\t * This method first calls {@link #removeSource(int)} with\n\t * {@link com.vonglasow.michael.satstat.data.CellTower#SOURCE_CELL_INFO} as\n\t * its argument. Then it iterates through all entries in {@code cells} and\n\t * updates each entry that is of type {@link android.telephony.CellInfoCdma}\n\t * by calling {@link #update(CellInfoCdma)}, passing that entry as the\n\t * argument.\n\t */\n\t@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\n\tpublic void updateAll(List<CellInfo> cells) {\n\t\tif (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) \n\t\t\treturn;\n\t\tthis.removeSource(CellTower.SOURCE_CELL_INFO);\n\t\tif (cells == null)\n\t\t\treturn;\n\t\tfor (CellInfo cell : cells)\n\t\t\tif (cell instanceof CellInfoCdma)\n\t\t\t\tthis.update((CellInfoCdma) cell);\n\t}\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/data/CellTowerListGsm.java",
    "content": "package com.vonglasow.michael.satstat.data;\n\nimport java.util.List;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\nimport android.telephony.CellIdentityGsm;\nimport android.telephony.CellIdentityWcdma;\nimport android.telephony.CellInfo;\nimport android.telephony.CellInfoGsm;\nimport android.telephony.CellInfoWcdma;\nimport android.telephony.NeighboringCellInfo;\nimport android.telephony.TelephonyManager;\nimport android.telephony.gsm.GsmCellLocation;\nimport android.util.Log;\n\npublic class CellTowerListGsm extends CellTowerList<CellTowerGsm> {\n\t/**\n\t * Returns the cell tower with the specified data, or {@code null} if it is not in the list. \n\t */\n\tpublic CellTowerGsm get(int psc) {\n\t\tString entry = CellTowerGsm.getAltText(psc);\n\t\tif (entry == null)\n\t\t\treturn null;\n\t\telse\n\t\t\treturn this.get(entry);\n\t}\n\t\n\t/**\n\t * Returns the cell tower with the specified data, or {@code null} if it is not in the list. \n\t */\n\tpublic CellTowerGsm get(int mcc, int mnc, int lac, int cid) {\n\t\tString entry = CellTowerGsm.getText(mcc, mnc, lac, cid);\n\t\tif (entry == null)\n\t\t\treturn null;\n\t\telse\n\t\t\treturn this.get(entry);\n\t}\n\t\n\t/**\n\t * Adds or updates a cell tower.\n\t * <p>\n\t * If the cell tower is already in the list, its data is updated; if not, a\n\t * new entry is created.\n\t * <p>\n\t * This method will set the cell's identity data. After this call,\n\t * {@link #isServing()} will return {@code true} for this cell. \n\t * @param networkOperator The network operator, as returned by {@link android.telephony.TelephonyManager#getNetworkOperator()}.\n\t * @param location The {@link android.telephony.GsmCellLocation}, as returned by {@link android.telephony.TelephonyManager#getCellLocation()}.\n\t * @return The new or updated entry.\n\t */\n\tpublic CellTowerGsm update(String networkOperator, GsmCellLocation location) {\n\t\tint mcc = CellTower.UNKNOWN;\n\t\tint mnc = CellTower.UNKNOWN;\n\t\tif (networkOperator.length() > 3) {\n\t\t\tmcc = Integer.parseInt(networkOperator.substring(0, 3));\n\t\t\tmnc = Integer.parseInt(networkOperator.substring(3));\n\t\t}\n\t\tCellTowerGsm result = null;\n\t\tCellTowerGsm cand = this.get(mcc, mnc, location.getLac(), location.getCid());\n\t\tif ((cand != null) && CellTower.matches(location.getPsc(), cand.getPsc()))\n\t\t\tresult = cand;\n\t\tif (result == null) {\n\t\t\tcand = this.get(location.getPsc());\n\t\t\tif ((cand != null)\n\t\t\t\t\t&& CellTower.matches(mcc, cand.getMcc())\n\t\t\t\t\t&& CellTower.matches(mnc, cand.getMnc())\n\t\t\t\t\t&& CellTower.matches(location.getLac(), cand.getLac())\n\t\t\t\t\t&& CellTower.matches(location.getCid(), cand.getCid()))\n\t\t\t\tresult = cand;\n\t\t}\n\t\tif (result == null)\n\t\t\tresult = new CellTowerGsm(mcc, mnc, location.getLac(), location.getCid(), location.getPsc());\n\t\tif (result.getMcc() == CellTower.UNKNOWN)\n\t\t\tresult.setMcc(mcc);\n\t\tif (result.getMnc() == CellTower.UNKNOWN)\n\t\t\tresult.setMnc(mnc);\n\t\tif (result.getLac() == CellTower.UNKNOWN)\n\t\t\tresult.setLac(location.getLac());\n\t\tif (result.getCid() == CellTower.UNKNOWN)\n\t\t\tresult.setCid(location.getCid());\n\t\tif (result.getPsc() == CellTower.UNKNOWN)\n\t\t\tresult.setPsc(location.getPsc());\n\t\tthis.put(result.getText(), result);\n\t\tthis.put(result.getAltText(), result);\n\t\tif ((result.getText() == null) && (result.getAltText() == null))\n\t\t\tLog.d(this.getClass().getSimpleName(), String.format(\"Added %d G cell with no data from GsmCellLocation\", result.getGeneration()));\n\t\tresult.setCellLocation(true);\n\t\treturn result;\n\t}\n\t\n\t/**\n\t * Adds or updates a cell tower.\n\t * <p>\n\t * If the cell tower is already in the list, its data is updated; if not, a\n\t * new entry is created. Cells whose network type is not a flavor of GSM or\n\t * UMTS will be rejected.\n\t * <p>\n\t * This method will set the cell's identity data, generation and its signal\n\t * strength. \n\t * @return The new or updated entry, or {@code null} if the cell was rejected\n\t */\n\tpublic CellTowerGsm update(String networkOperator, NeighboringCellInfo cell) {\n\t\tint mcc = CellTower.UNKNOWN;\n\t\tint mnc = CellTower.UNKNOWN;\n\t\tif (networkOperator.length() > 3) {\n\t\t\tmcc = Integer.parseInt(networkOperator.substring(0, 3));\n\t\t\tmnc = Integer.parseInt(networkOperator.substring(3));\n\t\t}\n\t\tCellTowerGsm result = null;\n\t\tCellTowerGsm cand = this.get(mcc, mnc, cell.getLac(), cell.getCid());\n\t\tif ((cand != null) && CellTower.matches(cell.getPsc(), cand.getPsc()))\n\t\t\tresult = cand;\n\n\t\tif (result == null) {\n\t\t\tcand = this.get(cell.getPsc());\n\t\t\tif ((cand != null)\n\t\t\t\t\t&& CellTower.matches(mcc, cand.getMcc())\n\t\t\t\t\t&& CellTower.matches(mnc, cand.getMnc())\n\t\t\t\t\t&& CellTower.matches(cell.getLac(), cand.getLac())\n\t\t\t\t\t&& CellTower.matches(cell.getCid(), cand.getCid()))\n\t\t\t\tresult = cand;\n\t\t}\n\t\tif (result == null)\n\t\t\tresult = new CellTowerGsm(mcc, mnc, cell.getLac(), cell.getCid(), cell.getPsc());\n\t\tresult.setNeighboringCellInfo(true);\n\t\tint networkType = cell.getNetworkType();\n\t\tswitch (networkType) {\n\t\t\tcase TelephonyManager.NETWORK_TYPE_UMTS:\n\t\t\tcase TelephonyManager.NETWORK_TYPE_HSDPA:\n\t\t\tcase TelephonyManager.NETWORK_TYPE_HSUPA:\n\t\t\tcase TelephonyManager.NETWORK_TYPE_HSPA:\n\t\t\t\t/*\n\t\t\t\t * for details see TS 25.133 section 9.1.1.3\n\t\t\t\t * http://www.3gpp.org/DynaReport/25133.htm\n\t\t\t\t */\n\t\t\t\tresult.setCpichRscp(cell.getRssi());\n\t\t\t\tbreak;\n\t\t\tcase TelephonyManager.NETWORK_TYPE_EDGE:\n\t\t\tcase TelephonyManager.NETWORK_TYPE_GPRS:\n\t\t\t\tresult.setAsu(cell.getRssi());\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t// not a GSM or UMTS cell, return\n\t\t\t\treturn null;\n\t\t\t\t// result.setDbm(CellTower.DBM_UNKNOWN);\n\t\t\t\t// not needed because this is the default value; setting it\n\t\t\t\t// here might overwrite valid data obtained from a different\n\t\t\t\t// source\n\t\t}\n\t\tresult.setNetworkType(networkType);\n\t\tif (result.getMcc() == CellTower.UNKNOWN)\n\t\t\tresult.setMcc(mcc);\n\t\tif (result.getMnc() == CellTower.UNKNOWN)\n\t\t\tresult.setMnc(mnc);\n\t\tif (result.getLac() == CellTower.UNKNOWN)\n\t\t\tresult.setLac(cell.getLac());\n\t\tif (result.getCid() == CellTower.UNKNOWN)\n\t\t\tresult.setCid(cell.getCid());\n\t\tif (result.getPsc() == CellTower.UNKNOWN)\n\t\t\tresult.setPsc(cell.getPsc());\n\t\tthis.put(result.getText(), result);\n\t\tthis.put(result.getAltText(), result);\n\t\tif ((result.getText() == null) && (result.getAltText() == null))\n\t\t\tLog.d(this.getClass().getSimpleName(), String.format(\"Added %d G cell with no data from NeighboringCellInfo\", result.getGeneration()));\n\t\treturn result;\n\t}\n\t\n\t/**\n\t * Adds or updates a cell tower.\n\t * <p>\n\t * If the cell tower is already in the list, its data is updated; if not, a\n\t * new entry is created.\n\t * <p>\n\t * This method will set the cell's identity data, its signal strength and\n\t * whether it is the currently serving cell. If the API level is 18 or \n\t * higher, it will also set the generation.\n\t * @return The new or updated entry.\n\t */\n\t@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\n\tpublic CellTowerGsm update(CellInfoGsm cell) {\n\t\tif (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) \n\t\t\treturn null;\n\t\tCellIdentityGsm cid = cell.getCellIdentity();\n\t\tCellTowerGsm result = null;\n\t\tCellTowerGsm cand = this.get(cid.getMcc(), cid.getMnc(), cid.getLac(), cid.getCid());\n\t\tif ((cand != null) && CellTower.matches(cid.getPsc(), cand.getPsc()))\n\t\t\tresult = cand;\n\t\tif (result == null) {\n\t\t\tcand = this.get(cid.getPsc());\n\t\t\tif ((cand != null)\n\t\t\t\t\t&& ((cid.getMcc() == Integer.MAX_VALUE) || CellTower.matches(cid.getMcc(), cand.getMcc()))\n\t\t\t\t\t&& ((cid.getMnc() == Integer.MAX_VALUE) || CellTower.matches(cid.getMnc(), cand.getMnc()))\n\t\t\t\t\t&& ((cid.getLac() == Integer.MAX_VALUE) || CellTower.matches(cid.getLac(), cand.getLac()))\n\t\t\t\t\t&& ((cid.getCid() == Integer.MAX_VALUE) ||CellTower.matches(cid.getCid(), cand.getCid())))\n\t\t\t\tresult = cand;\n\t\t}\n\t\tif (result == null)\n\t\t\tresult = new CellTowerGsm(cid.getMcc(), cid.getMnc(), cid.getLac(), cid.getCid(), cid.getPsc());\n\t\tif (result.getMcc() == CellTower.UNKNOWN)\n\t\t\tresult.setMcc(cid.getMcc());\n\t\tif (result.getMnc() == CellTower.UNKNOWN)\n\t\t\tresult.setMnc(cid.getMnc());\n\t\tif (result.getLac() == CellTower.UNKNOWN)\n\t\t\tresult.setLac(cid.getLac());\n\t\tif (result.getCid() == CellTower.UNKNOWN)\n\t\t\tresult.setCid(cid.getCid());\n\t\tif (result.getPsc() == CellTower.UNKNOWN)\n\t\t\tresult.setPsc(cid.getPsc());\n\t\tthis.put(result.getText(), result);\n\t\tthis.put(result.getAltText(), result);\n\t\tresult.setCellInfo(true);\n\t\tresult.setDbm(cell.getCellSignalStrength().getDbm());\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)\n\t\t\tresult.setGeneration(2);\n\t\tresult.setServing(cell.isRegistered());\n\t\tif ((result.getText() == null) && (result.getAltText() == null))\n\t\t\tLog.d(this.getClass().getSimpleName(), String.format(\"Added %d G cell with no data from CellInfoGsm\", result.getGeneration()));\n\t\treturn result;\n\t}\n\t\n\t/**\n\t * Adds or updates a cell tower.\n\t * <p>\n\t * If the cell tower is already in the list, its data is updated; if not, a\n\t * new entry is created.\n\t * <p>\n\t * This method will set the cell's identity data and generation, its signal \n\t * strength and whether it is the currently serving cell. \n\t * @return The new or updated entry.\n\t */\n\t@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)\n\tpublic CellTowerGsm update(CellInfoWcdma cell) {\n\t\tif (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) \n\t\t\treturn null;\n\t\tCellIdentityWcdma cid = cell.getCellIdentity();\n\t\tCellTowerGsm result = null;\n\t\tCellTowerGsm cand = this.get(cid.getMcc(), cid.getMnc(), cid.getLac(), cid.getCid());\n\t\tif ((cand != null) && CellTower.matches(cid.getPsc(), cand.getPsc()))\n\t\t\tresult = cand;\n\t\tif (result == null) {\n\t\t\tcand = this.get(cid.getPsc());\n\t\t\tif ((cand != null)\n\t\t\t\t\t&& ((cid.getMcc() == Integer.MAX_VALUE) || CellTower.matches(cid.getMcc(), cand.getMcc()))\n\t\t\t\t\t&& ((cid.getMnc() == Integer.MAX_VALUE) || CellTower.matches(cid.getMnc(), cand.getMnc()))\n\t\t\t\t\t&& ((cid.getLac() == Integer.MAX_VALUE) || CellTower.matches(cid.getLac(), cand.getLac()))\n\t\t\t\t\t&& ((cid.getCid() == Integer.MAX_VALUE) ||CellTower.matches(cid.getCid(), cand.getCid())))\n\t\t\t\tresult = cand;\n\t\t}\n\t\tif (result == null)\n\t\t\tresult = new CellTowerGsm(cid.getMcc(), cid.getMnc(), cid.getLac(), cid.getCid(), cid.getPsc());\n\t\tif (result.getMcc() == CellTower.UNKNOWN)\n\t\t\tresult.setMcc(cid.getMcc());\n\t\tif (result.getMnc() == CellTower.UNKNOWN)\n\t\t\tresult.setMnc(cid.getMnc());\n\t\tif (result.getLac() == CellTower.UNKNOWN)\n\t\t\tresult.setLac(cid.getLac());\n\t\tif (result.getCid() == CellTower.UNKNOWN)\n\t\t\tresult.setCid(cid.getCid());\n\t\tif (result.getPsc() == CellTower.UNKNOWN)\n\t\t\tresult.setPsc(cid.getPsc());\n\t\tthis.put(result.getText(), result);\n\t\tthis.put(result.getAltText(), result);\n\t\tresult.setCellInfo(true);\n\t\tresult.setDbm(cell.getCellSignalStrength().getDbm());\n\t\tresult.setGeneration(3);\n\t\tresult.setServing(cell.isRegistered());\n\t\tif ((result.getText() == null) && (result.getAltText() == null))\n\t\t\tLog.d(this.getClass().getSimpleName(), String.format(\"Added %d G cell with no data from CellInfoWcdma\", result.getGeneration()));\n\t\treturn result;\n\t}\n\t\n\t/**\n\t * Adds or updates a list of cell towers.\n\t * <p>\n\t * This method first calls {@link #removeSource(int)} with\n\t * {@link com.vonglasow.michael.satstat.data.CellTower#SOURCE_CELL_INFO} as\n\t * its argument. Then it iterates through all entries in {@code cells} and\n\t * updates each entry that is of type {@link android.telephony.CellInfoGsm}\n\t * or {@link android.telephony.CellInfoWcdma} by calling\n\t * {@link #update(CellInfoGsm)} or {@link #update(CellInfoWcdma)}\n\t * (depending on type), passing that entry as the argument.\n\t */\n\tpublic void updateAll(List<CellInfo> cells) {\n\t\tif (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) \n\t\t\treturn;\n\t\tthis.removeSource(CellTower.SOURCE_CELL_INFO);\n\t\tif (cells == null)\n\t\t\treturn;\n\t\tfor (CellInfo cell : cells)\n\t\t\tif (cell instanceof CellInfoGsm)\n\t\t\t\tthis.update((CellInfoGsm) cell);\n\t\t\telse if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)\n\t\t\t\tif (cell instanceof CellInfoWcdma)\n\t\t\t\t\tthis.update((CellInfoWcdma) cell);\n\t}\n\t\n\t/**\n\t * Adds or updates a list of cell towers.\n\t * <p>\n\t * This method first calls {@link #removeSource(int)} with\n\t * {@link com.vonglasow.michael.satstat.data.CellTower#SOURCE_NEIGHBORING_CELL_INFO}\n\t * as its argument. Then it iterates through all entries in {@code cells}\n\t * and updates each entry by calling {@link #update(NeighboringCellInfo)},\n\t * passing that entry as the argument.\n\t */\n\tpublic void updateAll(String networkOperator, List<NeighboringCellInfo> cells) {\n\t\tthis.removeSource(CellTower.SOURCE_NEIGHBORING_CELL_INFO);\n\t\tif (cells != null)\n\t\t\tfor (NeighboringCellInfo cell : cells)\n\t\t\t\tthis.update(networkOperator, cell);\n\t}\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/data/CellTowerListLte.java",
    "content": "package com.vonglasow.michael.satstat.data;\n\nimport java.util.List;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\nimport android.telephony.CellIdentityLte;\nimport android.telephony.CellInfo;\nimport android.telephony.CellInfoLte;\nimport android.telephony.NeighboringCellInfo;\nimport android.telephony.TelephonyManager;\nimport android.telephony.gsm.GsmCellLocation;\nimport android.util.Log;\n\npublic class CellTowerListLte extends CellTowerList<CellTowerLte> {\n\t/**\n\t * Returns the cell tower with the specified data, or {@code null} if it is not in the list. \n\t */\n\tpublic CellTowerLte get(int pci) {\n\t\tString entry = CellTowerLte.getAltText(pci);\n\t\tif (entry == null)\n\t\t\treturn null;\n\t\telse\n\t\t\treturn this.get(entry);\n\t}\n\t\n\t/**\n\t * Returns the cell tower with the specified data, or {@code null} if it is not in the list. \n\t */\n\tpublic CellTowerLte get(int mcc, int mnc, int tac, int ci) {\n\t\tString entry = CellTowerLte.getText(mcc, mnc, tac, ci);\n\t\tif (entry == null)\n\t\t\treturn null;\n\t\telse\n\t\t\treturn this.get(entry);\n\t}\n\t\n\t/**\n\t * Adds or updates a cell tower.\n\t * <p>\n\t * If the cell tower is already in the list, its data is updated; if not, a\n\t * new entry is created.\n\t * <p>\n\t * This method will set the cell's identity data. After this call,\n\t * {@link #isServing()} will return {@code true} for this cell.\n\t * @param networkOperator The network operator, as returned by {@link android.telephony.TelephonyManager#getNetworkOperator()}.\n\t * @param location The {@link android.telephony.GsmCellLocation}, as returned by {@link android.telephony.TelephonyManager#getCellLocation()}.\n\t * @return The new or updated entry.\n\t */\n\tpublic CellTowerLte update(String networkOperator, GsmCellLocation location) {\n\t\tint mcc = CellTower.UNKNOWN;\n\t\tint mnc = CellTower.UNKNOWN;\n\t\tif (networkOperator.length() > 3) {\n\t\t\tmcc = Integer.parseInt(networkOperator.substring(0, 3));\n\t\t\tmnc = Integer.parseInt(networkOperator.substring(3));\n\t\t}\n\t\tCellTowerLte result = null;\n\t\tCellTowerLte cand = this.get(mcc, mnc, location.getLac(), location.getCid());\n\t\tif ((cand != null) && CellTower.matches(location.getPsc(), cand.getPci()))\n\t\t\tresult = cand;\n\t\tif (result == null) {\n\t\t\tcand = this.get(location.getPsc());\n\t\t\tif ((cand != null)\n\t\t\t\t\t&& CellTower.matches(mcc, cand.getMcc())\n\t\t\t\t\t&& CellTower.matches(mnc, cand.getMnc())\n\t\t\t\t\t&& CellTower.matches(location.getLac(), cand.getTac())\n\t\t\t\t\t&& CellTower.matches(location.getCid(), cand.getCi()))\n\t\t\t\tresult = cand;\n\t\t}\n\t\tif (result == null)\n\t\t\tresult = new CellTowerLte(mcc, mnc, location.getLac(), location.getCid(), location.getPsc());\n\t\tif (result.getMcc() == CellTower.UNKNOWN)\n\t\t\tresult.setMcc(mcc);\n\t\tif (result.getMnc() == CellTower.UNKNOWN)\n\t\t\tresult.setMnc(mnc);\n\t\tif (result.getTac() == CellTower.UNKNOWN)\n\t\t\tresult.setTac(location.getLac());\n\t\tif (result.getCi() == CellTower.UNKNOWN)\n\t\t\tresult.setCi(location.getCid());\n\t\tif (result.getPci() == CellTower.UNKNOWN)\n\t\t\tresult.setPci(location.getPsc());\n\t\tthis.put(result.getText(), result);\n\t\tthis.put(result.getAltText(), result);\n\t\tresult.setCellLocation(true);\n\t\tLog.d(this.getClass().getSimpleName(), String.format(\"Added GsmCellLocation for %s, %d G\", result.getText(), result.getGeneration()));\n\t\treturn result;\n\t}\n\t\n\t/**\n\t * Adds or updates a cell tower.\n\t * <p>\n\t * If the cell tower is already in the list, its data is updated; if not, a\n\t * new entry is created. Cells whose network type is not LTE will be\n\t * rejected.\n\t * <p>\n\t * This method will set the cell's identity data, generation and its signal\n\t * strength.\n\t * @return The new or updated entry, or {@code null} if the cell was rejected\n\t */\n\tpublic CellTowerLte update(String networkOperator, NeighboringCellInfo cell) {\n\t\tint mcc = CellTower.UNKNOWN;\n\t\tint mnc = CellTower.UNKNOWN;\n\t\tif (networkOperator.length() > 3) {\n\t\t\tmcc = Integer.parseInt(networkOperator.substring(0, 3));\n\t\t\tmnc = Integer.parseInt(networkOperator.substring(3));\n\t\t}\n\t\tCellTowerLte result = null;\n\t\tCellTowerLte cand = this.get(mcc, mnc, cell.getLac(), cell.getCid());\n\t\tif ((cand != null) && CellTower.matches(cell.getPsc(), cand.getPci()))\n\t\t\tresult = cand;\n\n\t\tif (result == null) {\n\t\t\tcand = this.get(cell.getPsc());\n\t\t\tif ((cand != null)\n\t\t\t\t\t&& CellTower.matches(mcc, cand.getMcc())\n\t\t\t\t\t&& CellTower.matches(mnc, cand.getMnc())\n\t\t\t\t\t&& CellTower.matches(cell.getLac(), cand.getTac())\n\t\t\t\t\t&& CellTower.matches(cell.getCid(), cand.getCi()))\n\t\t\t\tresult = cand;\n\t\t}\n\t\tif (result == null)\n\t\t\tresult = new CellTowerLte(mcc, mnc, cell.getLac(), cell.getCid(), cell.getPsc());\n\t\tresult.setNeighboringCellInfo(true);\n\t\tint networkType = cell.getNetworkType();\n\t\tswitch (networkType) {\n\t\tcase TelephonyManager.NETWORK_TYPE_LTE:\n\t\t\t\tresult.setAsu(cell.getRssi());\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t// not an LTE cell, return\n\t\t\t\treturn null;\n\t\t}\n\t\tresult.setNetworkType(networkType);\n\t\tif (result.getMcc() == CellTower.UNKNOWN)\n\t\t\tresult.setMcc(mcc);\n\t\tif (result.getMnc() == CellTower.UNKNOWN)\n\t\t\tresult.setMnc(mnc);\n\t\tif (result.getTac() == CellTower.UNKNOWN)\n\t\t\tresult.setTac(cell.getLac());\n\t\tif (result.getCi() == CellTower.UNKNOWN)\n\t\t\tresult.setCi(cell.getCid());\n\t\tif (result.getPci() == CellTower.UNKNOWN)\n\t\t\tresult.setPci(cell.getPsc());\n\t\tthis.put(result.getText(), result);\n\t\tthis.put(result.getAltText(), result);\n\t\tLog.d(this.getClass().getSimpleName(), String.format(\"Added NeighboringCellInfo for %s, %d G, %d dBm\",\n\t\t\t\tresult.getText(),\n\t\t\t\tresult.getGeneration(),\n\t\t\t\tresult.getDbm()));\n\t\treturn result;\n\t}\n\t\n\t/**\n\t * Adds or updates a cell tower.\n\t * <p>\n\t * If the cell tower is already in the list, its data is updated; if not, a\n\t * new entry is created.\n\t * <p>\n\t * This method will set the cell's identity data, its signal strength and\n\t * whether it is the currently serving cell. \n\t * @return The new or updated entry.\n\t */\n\t@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\n\tpublic CellTowerLte update(CellInfoLte cell) {\n\t\tif (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) \n\t\t\treturn null;\n\t\tCellIdentityLte cid = cell.getCellIdentity();\n\t\tCellTowerLte result = null;\n\t\tCellTowerLte cand = this.get(cid.getMcc(), cid.getMnc(), cid.getTac(), cid.getCi());\n\t\tif ((cand != null) && CellTower.matches(cid.getPci(), cand.getPci()))\n\t\t\tresult = cand;\n\n\t\tif (result == null) {\n\t\t\tcand = this.get(cid.getPci());\n\t\t\tif ((cand != null)\n\t\t\t\t\t&& ((cid.getMcc() == Integer.MAX_VALUE) || CellTower.matches(cid.getMcc(), cand.getMcc()))\n\t\t\t\t\t&& ((cid.getMnc() == Integer.MAX_VALUE) || CellTower.matches(cid.getMnc(), cand.getMnc()))\n\t\t\t\t\t&& ((cid.getTac() == Integer.MAX_VALUE) || CellTower.matches(cid.getTac(), cand.getTac()))\n\t\t\t\t\t&& ((cid.getCi() == Integer.MAX_VALUE) ||CellTower.matches(cid.getCi(), cand.getCi())))\n\t\t\t\tresult = cand;\n\t\t}\n\t\tif (result == null)\n\t\t\tresult = new CellTowerLte(cid.getMcc(), cid.getMnc(), cid.getTac(), cid.getCi(), cid.getPci());\n\t\tif (result.getMcc() == CellTower.UNKNOWN)\n\t\t\tresult.setMcc(cid.getMcc());\n\t\tif (result.getMnc() == CellTower.UNKNOWN)\n\t\t\tresult.setMnc(cid.getMnc());\n\t\tif (result.getTac() == CellTower.UNKNOWN)\n\t\t\tresult.setTac(cid.getTac());\n\t\tif (result.getCi() == CellTower.UNKNOWN)\n\t\t\tresult.setCi(cid.getCi());\n\t\tif (result.getPci() == CellTower.UNKNOWN)\n\t\t\tresult.setPci(cid.getPci());\n\t\tthis.put(result.getText(), result);\n\t\tthis.put(result.getAltText(), result);\n\t\tresult.setCellInfo(true);\n\t\tresult.setDbm(cell.getCellSignalStrength().getDbm());\n\t\tresult.setServing(cell.isRegistered());\n\t\tLog.d(this.getClass().getSimpleName(), String.format(\"Added CellInfoLte for %s, %d G, %d dBm\",\n\t\t\t\tresult.getText(),\n\t\t\t\tresult.getGeneration(),\n\t\t\t\tresult.getDbm()));\n\t\treturn result;\n\t}\n\t\n\t/**\n\t * Adds or updates a list of cell towers.\n\t * <p>\n\t * This method first calls {@link #removeSource(int)} with\n\t * {@link com.vonglasow.michael.satstat.data.CellTower#SOURCE_CELL_INFO} as\n\t * its argument. Then it iterates through all entries in {@code cells} and\n\t * updates each entry that is of type {@link android.telephony.CellInfoLte}\n\t * by calling {@link #update(CellInfoLte)}, passing that entry as the\n\t * argument.\n\t */\n\t@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\n\tpublic void updateAll(List<CellInfo> cells) {\n\t\tif (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) \n\t\t\treturn;\n\t\tthis.removeSource(CellTower.SOURCE_CELL_INFO);\n\t\tif (cells == null)\n\t\t\treturn;\n\t\tfor (CellInfo cell : cells)\n\t\t\tif (cell instanceof CellInfoLte)\n\t\t\t\tthis.update((CellInfoLte) cell);\n\t}\n\t\n\t/**\n\t * Adds or updates a list of cell towers.\n\t * <p>\n\t * This method first calls {@link #removeSource(int)} with\n\t * {@link com.vonglasow.michael.satstat.data.CellTower#SOURCE_NEIGHBORING_CELL_INFO}\n\t * as its argument. Then it iterates through all entries in {@code cells}\n\t * and updates each entry by calling {@link #update(NeighboringCellInfo)},\n\t * passing that entry as the argument.\n\t */\n\tpublic void updateAll(String networkOperator, List<NeighboringCellInfo> cells) {\n\t\tthis.removeSource(CellTower.SOURCE_NEIGHBORING_CELL_INFO);\n\t\tif (cells != null)\n\t\t\tfor (NeighboringCellInfo cell : cells)\n\t\t\t\tthis.update(networkOperator, cell);\n\t}\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/data/CellTowerLte.java",
    "content": "package com.vonglasow.michael.satstat.data;\n\npublic class CellTowerLte extends CellTower {\n\tpublic static final String ALT_ID = \"pci\";\n\tpublic static final String FAMILY = \"lte\";\n\t\n\tprivate int ci;\n\tprivate int tac;\n\tprivate int mcc;\n\tprivate int mnc;\n\tprivate int pci;\n\t\n\tpublic CellTowerLte(int mcc, int mnc, int tac, int ci, int pci) {\n\t\tsuper();\n\t\tthis.setMcc(mcc);\n\t\tthis.setMnc(mnc);\n\t\tthis.setTac(tac);\n\t\tthis.setCi(ci);\n\t\tthis.setPci(pci);\n\t\tthis.generation = 4;\n\t}\n\t\n\t/**\n\t * Returns the alternate cell identity in text form.\n\t * <p>\n\t * For UMTS networks this string has the following form:\n\t * <p>\n\t * {@code lte:pci-nnn}\n\t * <p>\n\t * The first is a string which uniquely identifies the network family.\n\t * It is followed by a colon, the string {@code pci} to denote an alternate\n\t * cell identity, a dash and the physical cell identity (PCI) with no\n\t * leading zeroes.\n\t * <p> \n\t * A result is only returned for cells with a valid PCI. In all other\n\t * cases, {@code null} is returned. \n\t */\n\tpublic String getAltText() {\n\t\treturn getAltText(this.pci);\n\t}\n\n\t/**\n\t * Converts a PSC to an alternate identity string, or {@code null} if the\n\t * PSC is invalid. \n\t */\n\tpublic static String getAltText(int pci) {\n\t\tif ((pci == CellTower.UNKNOWN) || (pci == Integer.MAX_VALUE))\n\t\t\treturn null;\n\t\treturn String.format(\"%s:%s-%d\", FAMILY, ALT_ID, pci);\n\t}\n\t\n\tpublic int getCi() {\n\t\treturn this.ci;\n\t}\n\t\n\tpublic int getTac() {\n\t\treturn this.tac;\n\t}\n\t\n\tpublic int getMcc() {\n\t\treturn this.mcc;\n\t}\n\t\n\tpublic int getMnc() {\n\t\treturn this.mnc;\n\t}\n\t\n\tpublic int getPci() {\n\t\treturn this.pci;\n\t}\n\n\t/**\n\t * Returns the cell identity in text form.\n\t * <p>\n\t * For LTE networks this string has the following form:\n\t * <p>\n\t * {@code lte:mcc-mnc-tac-ci}\n\t * <p>\n\t * The first is a string which uniquely identifies the network family.\n\t * It is followed by a colon and a sequence of country code, network code,\n\t * Tracking Area Code and Cell ID, separated by dashes and with no leading\n\t * zeroes. \n\t */\n\t@Override\n\tpublic String getText() {\n\t\treturn getText(mcc, mnc, tac, ci);\n\t}\n\t\n\t/**\n\t * Converts a MCC/MNC/TAC/CI tuple to an identity string, or\n\t * {@code null} if CI is invalid.\n\t */\n\tpublic static String getText(int mcc, int mnc, int tac, int ci) {\n\t\tint iMcc = ((mcc == -1) || (mcc == Integer.MAX_VALUE)) ? CellTower.UNKNOWN : mcc;\n\t\tint iMnc = ((mnc == -1) || (mnc == Integer.MAX_VALUE)) ? CellTower.UNKNOWN : mnc;\n\t\tint iTac = ((tac == -1) || (tac == Integer.MAX_VALUE)) ? CellTower.UNKNOWN : tac;\n\t\tif ((ci == -1) || (ci == Integer.MAX_VALUE))\n\t\t\treturn null;\n\t\telse\n\t\t\treturn String.format(\"%s:%d-%d-%d-%d\", FAMILY, iMcc, iMnc, iTac, ci);\n\t}\n\t\n\t/**\n\t * Sets signal strength dBm based on ASU.\n\t * <p>\n\t * ASU can be converted into dBm with the formula:\n\t * {@code dBm = -113 + 2 * asu}. The reporting range is from -113 dBm to\n\t * -51 dBm (0 to 31). Values outside this range will be ignored. Refer to\n\t * 3GPP TS 27.007 (Ver 10.3.0) Sec 8.69\n\t */\n\t// or 3GPP TS 27.007 8.5\n\tpublic void setAsu(int asu){\n\t\tif ((asu >= 0) || (asu <= 31))\n\t\t\tthis.setDbm(-113 + 2 * asu);\n\t}\n\t\n\tpublic void setCi(int ci) {\n\t\tif (ci != Integer.MAX_VALUE)\n\t\t\tthis.ci = ci;\n\t\telse\n\t\t\tthis.ci = CellTower.UNKNOWN;\n\t}\n\t\n\tpublic void setTac(int tac) {\n\t\tif (tac != Integer.MAX_VALUE)\n\t\t\tthis.tac = tac;\n\t\telse\n\t\t\tthis.tac = CellTower.UNKNOWN;\n\t}\n\t\n\tpublic void setMcc(int mcc) {\n\t\tif (mcc != Integer.MAX_VALUE)\n\t\t\tthis.mcc = mcc;\n\t\telse\n\t\t\tthis.mcc = CellTower.UNKNOWN;\n\t}\n\t\n\tpublic void setMnc(int mnc) {\n\t\tif (mnc != Integer.MAX_VALUE)\n\t\t\tthis.mnc = mnc;\n\t\telse\n\t\t\tthis.mnc = CellTower.UNKNOWN;\n\t}\n\t\n\tpublic void setPci(int pci) {\n\t\tif ((pci != Integer.MAX_VALUE) && (pci != -1)) {\n\t\t\tthis.pci = pci;\n\t\t} else\n\t\t\tthis.pci = CellTower.UNKNOWN;\n\t}\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/ui/AboutActivity.java",
    "content": "/*\n * Copyright © 2013–2016 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat.ui;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport android.os.Bundle;\nimport android.support.v7.app.ActionBar;\nimport android.support.v7.app.AppCompatActivity;\nimport android.text.Html;\nimport android.view.MenuItem;\nimport android.widget.TextView;\nimport android.content.pm.PackageManager;\n\nimport com.vonglasow.michael.satstat.R;\n\npublic class AboutActivity extends AppCompatActivity {\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.activity_about);\n\n\t\tActionBar actionBar = getSupportActionBar();\n\t\tactionBar.setDisplayHomeAsUpEnabled(true);\n\n\t\tTextView aboutBuild = (TextView) findViewById(R.id.aboutBuild);\n\t\t\n\t\tInputStream buildInStream = getResources().openRawResource(R.raw.build);\n\t\tByteArrayOutputStream buildOutStream = new ByteArrayOutputStream();\n\t\t\n\t\tint i;\n\t\ttry {\n\t\t\ti = buildInStream.read();\n\t\t\twhile (i != -1) {\n\t\t\t\tif (i >= 32) buildOutStream.write(i);\n\t\t\t\ti = buildInStream.read();\n\t\t\t}\n\t\t\tbuildInStream.close();\n\t\t} catch (IOException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\ttry {\n\t\t\taboutBuild.setText(String.format(\"%s %s (%s)\", this.getString(R.string.about_version), getPackageManager().getPackageInfo(getPackageName(), 0).versionName, buildOutStream.toString()));\n\t\t} catch(PackageManager.NameNotFoundException e) {\n\t\t\taboutBuild.setText(buildOutStream.toString());\n\t\t}\n\t\t\n\t\tTextView aboutText = (TextView) findViewById(R.id.aboutText);\n\t\taboutText.setText(Html.fromHtml(this.getString(R.string.about_text)));\n\t}\n\t\n\t@Override\n\tpublic boolean onOptionsItemSelected(MenuItem item) {\n\t\tswitch (item.getItemId()) {\n\t\tcase android.R.id.home:\n\t\t\tonBackPressed();\n\t\t\treturn true;\n\t\t}\n\n\t\treturn super.onOptionsItemSelected(item);\n\t}\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/ui/GpsSectionFragment.java",
    "content": "/*\n * Copyright © 2013–2016 Michael von Glasow.\n *\n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat.ui;\n\nimport java.text.DateFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.Locale;\nimport java.util.TimeZone;\n\nimport uk.me.jstott.jcoord.LatLng;\nimport uk.me.jstott.jcoord.MGRSRef;\nimport com.hzi.UTM;\nimport android.hardware.GeomagneticField;\nimport android.hardware.Sensor;\nimport android.hardware.SensorEvent;\nimport android.hardware.SensorManager;\nimport android.location.GpsSatellite;\nimport android.location.GpsStatus;\nimport android.location.Location;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.view.Gravity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewGroup.LayoutParams;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\n\nimport com.vonglasow.michael.satstat.Const;\nimport com.vonglasow.michael.satstat.R;\nimport com.vonglasow.michael.satstat.widgets.GpsSnrView;\nimport com.vonglasow.michael.satstat.widgets.GpsStatusView;\n\n/**\n * The fragment which displays GPS data.\n */\npublic class GpsSectionFragment extends Fragment {\n\tpublic static final String TAG = \"GpsSectionFragment\";\n\t/**\n\t * The fragment argument representing the section number for this\n\t * fragment.\n\t */\n\tpublic static final String ARG_SECTION_NUMBER = \"section_number\";\n\n\tprivate MainActivity mainActivity = null;\n\n\tprivate DateFormat df;\n\n\tprivate LinearLayout gpsRootLayout;\n\tprivate GpsStatusView gpsStatusView;\n\tprivate GpsSnrView gpsSnrView;\n\tprivate LinearLayout gpsLatLayout;\n\tprivate TextView gpsLat;\n\tprivate LinearLayout gpsLonLayout;\n\tprivate TextView gpsLon;\n\tprivate LinearLayout gpsCoordLayout;\n\tprivate TextView gpsCoord;\n\tprivate TextView orDeclination;\n\tprivate TextView gpsSpeed;\n\tprivate TextView gpsSpeedUnit;\n\tprivate TextView gpsAlt;\n\tprivate TextView gpsAltUnit;\n\tprivate TextView gpsTime;\n\tprivate TextView gpsBearing;\n\tprivate TextView gpsAccuracy;\n\tprivate TextView gpsAccuracyUnit;\n\tprivate TextView gpsOrientation;\n\tprivate TextView gpsSats;\n\tprivate TextView gpsTtff;\n\n\t/*\n\t * Last known gravity and magnetic field vector\n\t */\n\tprivate float[] gravity;\n\tprivate float[] geomagnetic;\n\n\t/* Whether the orientation sensor returns valid data */\n\tprivate boolean hasOrientation = false;\n\n\n\tpublic GpsSectionFragment() {\n\t}\n\n\n\t@Override\n\tpublic View onCreateView(LayoutInflater inflater, ViewGroup container,\n\t\t\tBundle savedInstanceState) {\n\t\tmainActivity = (MainActivity) this.getContext();\n\t\tView rootView = inflater.inflate(R.layout.fragment_main_gps, container, false);\n\n\t\t// Initialize controls\n\t\tgpsRootLayout = (LinearLayout) rootView.findViewById(R.id.gpsRootLayout);\n\t\tgpsSnrView = (GpsSnrView) rootView.findViewById(R.id.gpsSnrView);\n\t\tgpsStatusView = new GpsStatusView(rootView.getContext());\n\t\tLinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);\n\t\tparams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL;\n\t\tparams.weight = 1;\n\t\tgpsRootLayout.addView(gpsStatusView, 0, params);\n\t\tgpsLatLayout = (LinearLayout) rootView.findViewById(R.id.gpsLatLayout);\n\t\tgpsLat = (TextView) rootView.findViewById(R.id.gpsLat);\n\t\tgpsLonLayout = (LinearLayout) rootView.findViewById(R.id.gpsLonLayout);\n\t\tgpsLon = (TextView) rootView.findViewById(R.id.gpsLon);\n\t\tgpsCoordLayout = (LinearLayout) rootView.findViewById(R.id.gpsCoordLayout);\n\t\tgpsCoord = (TextView) rootView.findViewById(R.id.gpsCoord);\n\t\torDeclination = (TextView) rootView.findViewById(R.id.orDeclination);\n\t\tgpsSpeed = (TextView) rootView.findViewById(R.id.gpsSpeed);\n\t\tgpsSpeedUnit = (TextView) rootView.findViewById(R.id.gpsSpeedUnit);\n\t\tgpsAlt = (TextView) rootView.findViewById(R.id.gpsAlt);\n\t\tgpsAltUnit = (TextView) rootView.findViewById(R.id.gpsAltUnit);\n\t\tgpsTime = (TextView) rootView.findViewById(R.id.gpsTime);\n\t\tgpsBearing = (TextView) rootView.findViewById(R.id.gpsBearing);\n\t\tgpsAccuracy = (TextView) rootView.findViewById(R.id.gpsAccuracy);\n\t\tgpsAccuracyUnit = (TextView) rootView.findViewById(R.id.gpsAccuracyUnit);\n\t\tgpsOrientation = (TextView) rootView.findViewById(R.id.gpsOrientation);\n\t\tgpsSats = (TextView) rootView.findViewById(R.id.gpsSats);\n\t\tgpsTtff = (TextView) rootView.findViewById(R.id.gpsTtff);\n\n\t\tdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\", Locale.ROOT);\n\n\t\tmainActivity.gpsSectionFragment = this;\n\n\t\treturn rootView;\n\t}\n\n\n\t@Override\n\tpublic void onDestroyView() {\n\t\tsuper.onDestroyView();\n\t\tif (mainActivity.gpsSectionFragment == this)\n\t\t\tmainActivity.gpsSectionFragment = null;\n\t}\n\n\n\t/**\n\t * Called by {@link MainActivity} when the status of the GPS changes. Updates GPS display.\n\t */\n\tpublic void onGpsStatusChanged(GpsStatus status, int satsInView, int satsUsed, Iterable<GpsSatellite> sats) {\n\t\tgpsSats.setText(String.valueOf(satsUsed) + \"/\" + String.valueOf(satsInView));\n\t\tgpsTtff.setText(String.valueOf(status.getTimeToFirstFix() / 1000));\n\t\tgpsStatusView.showSats(sats);\n\t\tgpsSnrView.showSats(sats);\n\t}\n\n\n\t/**\n\t * Called by {@link MainActivity} when a new location is found by the GPS location provider.\n\t * Stores the location and updates GPS display and map view.\n\t */\n\tpublic void onLocationChanged(Location location) {\n\t\tif (location.hasAccuracy()) {\n\t\t\tFloat getAcc = (float) 0.0;\n\t\t\tif(mainActivity.prefUnitType) {\n\t\t\t\tgetAcc = (float)(location.getAccuracy());\n\t\t\t} else {\n\t\t\t\tgetAcc = (float)(location.getAccuracy() * (float) 3.28084);\n\t\t\t}\n\t\t\tgpsAccuracy.setText(String.format(\"%.0f\", getAcc));\n\t\t\tgpsAccuracyUnit.setText(getString(((mainActivity.prefUnitType) ? R.string.unit_meter : R.string.unit_feet)));\n\t\t} else {\n\t\t\tgpsAccuracy.setText(getString(R.string.value_none));\n\t\t\tgpsAccuracyUnit.setText(\"\");\n\t\t}\n\n\t\tif (mainActivity.prefCoord == Const.KEY_PREF_COORD_DECIMAL) {\n\t\t\tgpsCoordLayout.setVisibility(View.GONE);\n\t\t\tgpsLatLayout.setVisibility(View.VISIBLE);\n\t\t\tgpsLonLayout.setVisibility(View.VISIBLE);\n\t\t\tgpsLat.setText(String.format(\"%.5f%s\", location.getLatitude(), getString(R.string.unit_degree)));\n\t\t\tgpsLon.setText(String.format(\"%.5f%s\", location.getLongitude(), getString(R.string.unit_degree)));\n\t\t} else if (mainActivity.prefCoord == Const.KEY_PREF_COORD_MIN) {\n\t\t\tgpsCoordLayout.setVisibility(View.GONE);\n\t\t\tgpsLatLayout.setVisibility(View.VISIBLE);\n\t\t\tgpsLonLayout.setVisibility(View.VISIBLE);\n\t\t\tdouble dec = location.getLatitude();\n\t\t\tdouble deg = (int) dec;\n\t\t\tdouble min = Math.abs(60.0 * (dec - deg));\n\t\t\tgpsLat.setText(String.format(\"%.0f%s %.3f'\", deg, getString(R.string.unit_degree), min + /*rounding*/ 0.0005));\n\t\t\tdec = location.getLongitude();\n\t\t\tdeg = (int) dec;\n\t\t\tmin = Math.abs(60.0 * (dec - deg));\n\t\t\tgpsLon.setText(String.format(\"%.0f%s %.3f'\", deg, getString(R.string.unit_degree), min + /*rounding*/ 0.0005));\n\t\t} else if (mainActivity.prefCoord == Const.KEY_PREF_COORD_SEC) {\n\t\t\tgpsCoordLayout.setVisibility(View.GONE);\n\t\t\tgpsLatLayout.setVisibility(View.VISIBLE);\n\t\t\tgpsLonLayout.setVisibility(View.VISIBLE);\n\n\t\t\tdouble dec = location.getLatitude();\n\t\t\tdouble deg = (int) dec;\n\t\t\tdouble tmp = Math.abs(60.0 * (dec - deg));\n\t\t\tdouble min = (int) tmp;\n\t\t\tdouble sec = 60.0 * (tmp - min);\n\t\t\tgpsLat.setText(String.format(\"%.0f%s %.0f' %.1f\\\"\", deg, getString(R.string.unit_degree), min, sec + /*rounding*/ 0.05));\n\n\t\t\tdec = location.getLongitude();\n\t\t\tdeg = (int) dec;\n\t\t\ttmp = Math.abs(60.0 * (dec - deg));\n\t\t\tmin = (int) tmp;\n\t\t\tsec = 60.0 * (tmp - min);\n\t\t\tgpsLon.setText(String.format(\"%.0f%s %.0f' %.1f\\\"\", deg, getString(R.string.unit_degree), min, sec + /*rounding*/ 0.05));\n\t\t} else if (mainActivity.prefCoord == Const.KEY_PREF_COORD_MGRS) {\n\t\t\tgpsLatLayout.setVisibility(View.GONE);\n\t\t\tgpsLonLayout.setVisibility(View.GONE);\n\t\t\tgpsCoordLayout.setVisibility(View.VISIBLE);\n\t\t\tgpsCoord.setText(new LatLng(location.getLatitude(), location.getLongitude()).toMGRSRef().toString(MGRSRef.PRECISION_1M));\n\t\t} else if (mainActivity.prefCoord == Const.KEY_PREF_COORD_UTM) {\n\t\t\tgpsLatLayout.setVisibility(View.GONE);\n\t\t\tgpsLonLayout.setVisibility(View.GONE);\n\t\t\tgpsCoordLayout.setVisibility(View.VISIBLE);\n\t\t\tgpsCoord.setText(UTM.lat_lon_to_utm(location.getLatitude(), location.getLongitude(), this.getContext()));\n\t\t}\n\n\t\tif (mainActivity.prefUtc)\n\t\t\tdf.setTimeZone(TimeZone.getTimeZone(\"UTC\"));\n\t\telse\n\t\t\tdf.setTimeZone(TimeZone.getDefault());\n\t\tgpsTime.setText(df.format(new Date(location.getTime())));\n\n\t\tif (location.hasAltitude()) {\n\t\t\tFloat getAltitude = (float) 0.0;\n\t\t\tif(mainActivity.prefUnitType) {\n\t\t\t\tgetAltitude = (float)(location.getAltitude());\n\t\t\t} else {\n\t\t\t\tgetAltitude = (float)(location.getAltitude() * (float) 3.28084);\n\t\t\t}\n\t\t\tgpsAlt.setText(String.format(\"%.0f\", getAltitude));\n\t\t\tgpsAltUnit.setText(getString(((mainActivity.prefUnitType) ? R.string.unit_meter : R.string.unit_feet)));\n\t\t\torDeclination.setText(String.format(\"%.0f%s\", new GeomagneticField(\n\t\t\t\t\t(float) location.getLatitude(),\n\t\t\t\t\t(float) location.getLongitude(),\n\t\t\t\t\t(float) (getAltitude),\n\t\t\t\t\tlocation.getTime()\n\t\t\t\t\t).getDeclination(), getString(R.string.unit_degree)));\n\t\t} else {\n\t\t\tgpsAlt.setText(getString(R.string.value_none));\n\t\t\tgpsAltUnit.setText(\"\");\n\t\t\torDeclination.setText(getString(R.string.value_none));\n\t\t}\n\n\t\tif (location.hasBearing()) {\n\t\t\tgpsBearing.setText(String.format(\"%.0f%s\", location.getBearing(), getString(R.string.unit_degree)));\n\t\t\tgpsOrientation.setText(MainActivity.formatOrientation(this.getContext(), location.getBearing()));\n\t\t} else {\n\t\t\tgpsBearing.setText(getString(R.string.value_none));\n\t\t\tgpsOrientation.setText(getString(R.string.value_none));\n\t\t}\n\n\t\tif (location.hasSpeed()) {\n\t\t\tFloat getSpeed = (float) 0.0;\n\t\t\tif (mainActivity.prefKnots) {\n\t\t\t\tgetSpeed = (float)(location.getSpeed() * 1.943844f);\n\t\t\t} else if (mainActivity.prefUnitType) {\n\t\t\t\tgetSpeed = (float)(location.getSpeed() * 3.6f);\n\t\t\t} else {\n\t\t\t\tgetSpeed = (float)(location.getSpeed() * 2.23694f);\n\t\t\t}\n\t\t\tgpsSpeed.setText(String.format(\"%.0f\", getSpeed));\n\t\t\tgpsSpeedUnit.setText(getString(((mainActivity.prefKnots) ? R.string.unit_kn : (mainActivity.prefUnitType) ? R.string.unit_km_h : R.string.unit_mph)));\n\t\t} else {\n\t\t\tgpsSpeed.setText(getString(R.string.value_none));\n\t\t\tgpsSpeedUnit.setText(\"\");\n\t\t}\n\n\t\t// note: getting number of sats in fix by looking for \"satellites\"\n\t\t// in location's extras doesn't seem to work, always returns 0 sats\n\t}\n\n\n\t/**\n\t * Called by {@link MainActivity} when a sensor's reading changes.\n\t * Rotates sky plot according to bearing.\n\t *\n\t * If {@code TYPE_ORIENTATION} data is available, preference is given to that value, which\n\t * appeared to be more accurate in tests. Otherwise orientation is obtained from the rotation\n\t * vector of the device, based on {@link TYPE_ACCELEROMETER} and {@code TYPE_MAGNETIC_FIELD}\n\t * sensor data.\n\t */\n\tpublic void onSensorChanged(SensorEvent event) {\n\t\tswitch (event.sensor.getType()) {\n\t\tcase Sensor.TYPE_ACCELEROMETER:\n\t\t\tgravity = event.values.clone();\n\t\t\tbreak;\n\t\tcase Sensor.TYPE_MAGNETIC_FIELD:\n\t\t\tgeomagnetic = event.values.clone();\n\t\t\tbreak;\n\t\tcase Sensor.TYPE_ORIENTATION:\n\t\t\tif (event.values[0] != 0) {\n\t\t\t\thasOrientation = true;\n\t\t\t\tgpsStatusView.setYaw(event.values[0]);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tif ((gravity != null) && (geomagnetic != null) && !hasOrientation) {\n\t\t\tfloat[] ypr = new float[3];\n\t\t\tfloat[] r = new float[16];\n\t\t\tfloat[] i = new float[16];\n\t\t\tSensorManager.getRotationMatrix(r, i, gravity, geomagnetic);\n\t\t\typr = SensorManager.getOrientation(r, ypr);\n\t\t\tgpsStatusView.setYaw((float) Math.toDegrees(ypr[0]));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/ui/LegendActivity.java",
    "content": "/*\n * Copyright © 2013–2016 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat.ui;\n\nimport java.util.Map;\n\nimport com.vonglasow.michael.satstat.Const;\nimport com.vonglasow.michael.satstat.R;\nimport android.content.SharedPreferences;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.graphics.drawable.Drawable;\nimport android.os.Bundle;\nimport android.preference.PreferenceManager;\nimport android.support.v7.app.AppCompatActivity;\nimport android.util.Log;\nimport android.view.Gravity;\nimport android.view.MenuItem;\nimport android.view.ViewGroup.LayoutParams;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\nimport android.widget.ImageView.ScaleType;\n\npublic class LegendActivity extends AppCompatActivity {\n\t/*\n\t * Index into style arrays\n\t */\n\tprivate static final int STYLE_MARKER = 0;\n\n\tprivate LinearLayout legendMapContainer;\n\n\tprotected void addLocationProvider(String title, String styleName) {\n\t\tResources res = this.getBaseContext().getResources();\n\t\tTypedArray style = res.obtainTypedArray(res.getIdentifier(styleName, \"array\", this.getBaseContext().getPackageName()));\n\t\tDrawable drawable = style.getDrawable(STYLE_MARKER);\n\t\tstyle.recycle();\n\n\t\tLinearLayout lpLayout = new LinearLayout(legendMapContainer.getContext());\n\t\tlpLayout.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));\n\t\tlpLayout.setOrientation(LinearLayout.HORIZONTAL);\n\t\tlpLayout.setWeightSum(22);\n\t\tlpLayout.setMeasureWithLargestChildEnabled(false);\n\n\t\tImageView lpMarker = new ImageView(legendMapContainer.getContext());\n\t\tLinearLayout.LayoutParams lpMarkerParams = new LinearLayout.LayoutParams(0, getResources().getDimensionPixelSize(R.dimen.legend_rowheight), 3);\n\t\tint margin = getResources().getDimensionPixelSize(R.dimen.bitmap_padding);\n\t\tlpMarkerParams.gravity = Gravity.CENTER;\n\t\tlpMarker.setLayoutParams(lpMarkerParams);\n\t\tlpMarker.setPadding(margin, 0, margin, 0);\n\t\tlpMarker.setImageDrawable(drawable);\n\t\tlpMarker.setScaleType(ScaleType.CENTER);\n\t\tlpLayout.addView(lpMarker);\n\n\t\tTextView lpDesc = new TextView(legendMapContainer.getContext());\n\t\tLinearLayout.LayoutParams lpDescParams = new LinearLayout.LayoutParams(0, LayoutParams.WRAP_CONTENT, 19);\n\t\tlpDescParams.gravity = Gravity.CENTER_VERTICAL;\n\t\tlpDesc.setLayoutParams(lpDescParams);\n\t\tlpDesc.setGravity(Gravity.CENTER_VERTICAL);\n\t\tlpDesc.setTextAppearance(this, R.style.TextAppearance_AppCompat_Medium);\n\t\tlpDesc.setText(title);\n\t\tlpLayout.addView(lpDesc);\n\n\t\tlegendMapContainer.addView(lpLayout);\n\t}\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.activity_legend);\n\n\t\tlegendMapContainer = (LinearLayout) findViewById(R.id.legendMapContainer);\n\n\t\tSharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this.getBaseContext());\n\t\tMap<String, ?> allPrefs = sharedPref.getAll();\n\n\t\tfor (String key: allPrefs.keySet())\n\t\t\tif (key.startsWith(Const.KEY_PREF_LOC_PROV_STYLE)) {\n\t\t\t\tString provName = key.substring(Const.KEY_PREF_LOC_PROV_STYLE.length());\n\t\t\t\tString styleName = \"\";\n\t\t\t\ttry {\n\t\t\t\t\tstyleName = sharedPref.getString(key, styleName);\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tLog.w(this.getClass().getSimpleName(), String.format(\"Cannot retrieve preference %s\", key));\n\t\t\t\t}\n\t\t\t\tif (styleName != \"\")\n\t\t\t\t\taddLocationProvider(String.format(getString(R.string.title_legend_map_prov, provName)), styleName);\n\t\t\t}\n\n\t\taddLocationProvider(getString(R.string.title_legend_map_stale), Const.LOCATION_PROVIDER_GRAY);\n\t}\n\t\n\t@Override\n\tpublic boolean onOptionsItemSelected(MenuItem item) {\n\t\tswitch (item.getItemId()) {\n\t\tcase android.R.id.home:\n\t\t\tonBackPressed();\n\t\t\treturn true;\n\t\t}\n\n\t\treturn super.onOptionsItemSelected(item);\n\t}\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/ui/MainActivity.java",
    "content": "/*\n * Copyright © 2013–2016 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat.ui;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.PrintStream;\nimport java.lang.Thread.UncaughtExceptionHandler;\nimport java.text.DateFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Set;\nimport java.util.TimeZone;\n\nimport org.mapsforge.map.android.graphics.AndroidGraphicFactory;\n\nimport android.Manifest;\nimport android.annotation.TargetApi;\nimport android.app.NotificationManager;\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.content.SharedPreferences;\nimport android.content.SharedPreferences.OnSharedPreferenceChangeListener;\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.PackageManager;\nimport android.content.res.Configuration;\nimport android.graphics.drawable.Drawable;\nimport android.hardware.Sensor;\nimport android.hardware.SensorEvent;\nimport android.hardware.SensorEventListener;\nimport android.hardware.SensorManager;\nimport android.location.GpsSatellite;\nimport android.location.GpsStatus;\nimport android.location.Location;\nimport android.location.LocationListener;\nimport android.location.LocationManager;\nimport android.net.ConnectivityManager;\nimport android.net.Uri;\nimport android.net.wifi.WifiManager;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.preference.PreferenceManager;\nimport android.support.design.widget.TabLayout;\nimport android.support.v4.app.ActivityCompat;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.app.FragmentPagerAdapter;\nimport android.support.v4.content.ContextCompat;\nimport android.support.v4.view.ViewPager;\nimport android.support.v7.app.ActionBar;\nimport android.support.v7.app.AppCompatActivity;\nimport android.support.v7.view.ContextThemeWrapper;\nimport android.telephony.CellInfo;\nimport android.telephony.CellLocation;\nimport android.telephony.PhoneStateListener;\nimport static android.telephony.PhoneStateListener.LISTEN_CELL_INFO;\nimport static android.telephony.PhoneStateListener.LISTEN_CELL_LOCATION;\nimport static android.telephony.PhoneStateListener.LISTEN_DATA_CONNECTION_STATE;\nimport static android.telephony.PhoneStateListener.LISTEN_NONE;\nimport static android.telephony.PhoneStateListener.LISTEN_SIGNAL_STRENGTHS;\nimport android.telephony.SignalStrength;\nimport android.telephony.TelephonyManager;\nimport android.util.Log;\nimport android.view.Menu;\nimport android.view.MenuItem;\nimport android.view.Surface;\nimport android.view.ViewGroup.LayoutParams;\nimport android.view.WindowManager;\nimport android.widget.LinearLayout;\nimport android.widget.Toast;\n\nimport com.vonglasow.michael.satstat.Const;\nimport com.vonglasow.michael.satstat.GpsEventReceiver;\nimport com.vonglasow.michael.satstat.R;\nimport com.vonglasow.michael.satstat.data.CellTower;\nimport com.vonglasow.michael.satstat.data.CellTowerList;\n\npublic class MainActivity extends AppCompatActivity implements GpsStatus.Listener, LocationListener, OnSharedPreferenceChangeListener, SensorEventListener {\n\tprivate static final String TAG = MainActivity.class.getSimpleName();\n\n    /**\n     * The {@link android.support.v4.view.PagerAdapter} that will provide\n     * fragments for each of the sections. We use a\n     * {@link android.support.v4.app.FragmentPagerAdapter} derivative, which\n     * will keep every loaded fragment in memory. If this becomes too memory\n     * intensive, it may be best to switch to a\n     * {@link android.support.v4.app.FragmentStatePagerAdapter}.\n     */\n    SectionsPagerAdapter mSectionsPagerAdapter;\n\n    /**\n     * The {@link ViewPager} that will host the section contents.\n     */\n    ViewPager mViewPager;\n    \n    /**\n     * The tab view to switch between the fragments of the MainView.\n     */\n    TabLayout mTabLayout;\n    \n    /**\n     * Whether the activity is stopped. \n     */\n    boolean isStopped;\n    \n    /**\n     * Whether we are running on a wide-screen device\n     */\n    boolean isWideScreen;\n\n    /**\n     * The rate in microseconds at which we would like to receive updates from the sensors.\n     * \n     * This is chosen to meet the mission of SatStat, which is to let the human know the values reported\n     * and evaluate if the sensors are working. At very high rates, the numbers cannot be read, and at\n     * very slow rates, it is hard to tell if the updates would work at faster rates. We chose 100 ms as\n     * a reasonable tradeoff.\n     */\n\tprivate static final int iSensorRate = 100000;\n\n\tGpsSectionFragment gpsSectionFragment = null;\n\tSensorSectionFragment sensorSectionFragment = null;\n\tRadioSectionFragment radioSectionFragment = null;\n\tMapSectionFragment mapSectionFragment = null;\n\t\n\tTelephonyManager telephonyManager;\n\tConnectivityManager connectivityManager;\n\tWifiManager wifiManager;\n\tLocationManager locationManager;\n\tSensorManager sensorManager;\n\t\n\tboolean[] permsRequested = new boolean[Const.PERM_REQUEST_MAX + 1];\n\n\tprivate Sensor mOrSensor;\n\tprivate Sensor mAccSensor;\n\tprivate Sensor mGyroSensor;\n\tprivate Sensor mMagSensor;\n\tprivate Sensor mLightSensor;\n\tprivate Sensor mProximitySensor;\n\tprivate Sensor mPressureSensor;\n\tprivate Sensor mHumiditySensor;\n\tprivate Sensor mTempSensor;\n\t\n\tprivate long mOrLast = 0;\n\tprivate long mAccLast = 0;\n\tprivate long mGyroLast = 0;\n\tprivate long mMagLast = 0;\n\tprivate long mLightLast = 0;\n\tprivate long mProximityLast = 0;\n\tprivate long mPressureLast = 0;\n\tprivate long mHumidityLast = 0;\n\tprivate long mTempLast = 0;\n\n\t/**\n\t * Converts screen rotation to orientation for devices with a naturally tall screen.\n\t */\n\tprivate final static Integer OR_FROM_ROT_TALL[] = {\n\t\tActivityInfo.SCREEN_ORIENTATION_PORTRAIT,\n\t\tActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,\n\t\tActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT,\n\t\tActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE};\n\n\t/**\n\t * Converts screen rotation to orientation for devices with a naturally wide screen.\n\t */\n\tprivate final static Integer OR_FROM_ROT_WIDE[] = {\n\t\tActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,\n\t\tActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT,\n\t\tActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE,\n\t\tActivityInfo.SCREEN_ORIENTATION_PORTRAIT};\n\n\tSharedPreferences mSharedPreferences;\n\t\n\tboolean prefUnitType = true;\n\tboolean prefKnots = false;\n\tint prefCoord = Const.KEY_PREF_COORD_DECIMAL;\n\tboolean prefUtc = false;\n\tboolean prefCid = false;\n\tboolean prefCid2 = false;\n\tint prefWifiSort = 0;\n\tboolean prefMapOffline = false;\n\tString prefMapPath = Const.MAP_PATH_DEFAULT;\n\n\t/** \n\t * The {@link PhoneStateListener} for getting radio network updates \n\t */\n\tprivate final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {\n\t\t@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\n\t \tpublic void onCellInfoChanged(List<CellInfo> cellInfo) {\n\t\t\tif (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) \n\t\t\t\treturn;\n\t\t\tif (radioSectionFragment != null)\n\t\t\t\tradioSectionFragment.updateCellData(null, null, cellInfo);\n\t \t}\n\t \t\n\t\tpublic void onCellLocationChanged (CellLocation location) {\n\t\t\tif (radioSectionFragment != null)\n\t\t\t\tradioSectionFragment.updateCellData(location, null, null);\n\t\t}\n\t\t\n\t\tpublic void onDataConnectionStateChanged (int state, int networkType) {\n\t\t\tif (radioSectionFragment != null)\n\t\t\t\tradioSectionFragment.onNetworkTypeChanged(networkType);\n\t\t}\n\t\t\n\t\tpublic void onSignalStrengthsChanged (SignalStrength signalStrength) {\n\t\t\tif (radioSectionFragment != null)\n\t\t\t\tradioSectionFragment.updateCellData(null, signalStrength, null);\n\t\t}\n\t};\n\t\n\t/** \n\t * The {@link BroadcastReceiver} for getting radio network updates \n\t */\n\tprivate final BroadcastReceiver mWifiScanReceiver = new BroadcastReceiver() {\n\t\t@Override\n\t\tpublic void onReceive(Context c, Intent intent) {\n\t\t\tif (intent.getAction() == WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) {\n\t\t\t\tif (radioSectionFragment != null) {\n\t\t\t\t\tradioSectionFragment.scanResults = wifiManager.getScanResults();\n\t\t\t\t\tradioSectionFragment.refreshWifiResults();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t//something has changed about WiFi setup, rescan\n\t\t\t\twifiManager.startScan();\n\t\t\t}\n\t\t}\n\t};\n\t\n\tprivate Thread.UncaughtExceptionHandler defaultUEH;\n\t\n\t\n    /**\n     * Converts a bearing (in degrees) into a directional name.\n     */\n    public static String formatOrientation(Context context, float bearing) {\n\t\treturn \n\t\t\t(bearing < 11.25) ? context.getString(R.string.value_N) :\n\t\t\t\t(bearing < 33.75) ? context.getString(R.string.value_NNE) :\n\t\t\t\t\t(bearing < 56.25) ? context.getString(R.string.value_NE) :\n\t\t\t\t\t\t(bearing < 78.75) ? context.getString(R.string.value_ENE) :\n\t\t\t\t\t\t\t(bearing < 101.25) ? context.getString(R.string.value_E) :\n\t\t\t\t\t\t\t\t(bearing < 123.75) ? context.getString(R.string.value_ESE) :\n\t\t\t\t\t\t\t\t\t(bearing < 146.25) ? context.getString(R.string.value_SE) :\n\t\t\t\t\t\t\t\t\t\t(bearing < 168.75) ? context.getString(R.string.value_SSE) :\n\t\t\t\t\t\t\t\t\t\t\t(bearing < 191.25) ? context.getString(R.string.value_S) :\n\t\t\t\t\t\t\t\t\t\t\t\t(bearing < 213.75) ? context.getString(R.string.value_SSW) :\n\t\t\t\t\t\t\t\t\t\t\t\t\t(bearing < 236.25) ? context.getString(R.string.value_SW) :\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t(bearing < 258.75) ? context.getString(R.string.value_WSW) :\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(bearing < 280.25) ? context.getString(R.string.value_W) :\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(bearing < 302.75) ? context.getString(R.string.value_WNW) :\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(bearing < 325.25) ? context.getString(R.string.value_NW) :\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(bearing < 347.75) ? context.getString(R.string.value_NNW) :\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcontext.getString(R.string.value_N);\n    }\n\t\n    \n\t/**\n\t * Returns the serving cell.\n\t * <p>\n\t * This method iterates through the cell tower lists passed in\n\t * {@code lists} and looks for any entries marked as the serving cell.\n\t *  \n\t * @param lists An array of {@link com.vonglasow.michael.satstat.data.CellTowerList}\n\t * instances\n\t * @return The serving cell, if one is found, or {@code null} if none is\n\t * found. If multiple serving cells are found in {@code lists}, no\n\t * assertion is made which cell will be returned, or even that results\n\t * will be consistent between calls.\n\t */\n\tpublic static CellTower getServingCell(CellTowerList[] lists) {\n\t\tfor (CellTowerList<CellTower> towers : lists) {\n\t\t\tfor (CellTower cell : towers.getAll())\n\t\t\t\tif (cell.hasSource() && cell.isServing())\n\t\t\t\t\treturn cell;\n\t\t}\n\t\treturn null;\n\t}\n    \n\n    /**\n     * Called when a sensor's accuracy has changed. Does nothing.\n     */\n    public void onAccuracyChanged(Sensor sensor, int accuracy) {}\n    \n\t@Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        \n        defaultUEH = Thread.getDefaultUncaughtExceptionHandler();\n        \n        Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {\n        \tpublic void uncaughtException(Thread t, Throwable e) {\n        \t\tContext c = getApplicationContext();\n        \t\tFile dumpDir = c.getExternalFilesDir(null);\n        \t\tDateFormat fmt = new SimpleDateFormat(\"yyyy-MM-dd-HH-mm-ss\", Locale.ROOT);\n        \t\tfmt.setTimeZone(TimeZone.getTimeZone(\"UTC\"));\n        \t\tString fileName = String.format(\"satstat-%s.log\", fmt.format(new Date(System.currentTimeMillis())));\n\n        \t\tFile dumpFile = new File (dumpDir, fileName);\n        \t\tPrintStream s;\n        \t\ttry {\n        \t\t\tInputStream buildInStream = getResources().openRawResource(R.raw.build);\n        \t\t\ts = new PrintStream(dumpFile);\n        \t\t\ts.append(\"SatStat build: \");\n        \t\t\t\n        \t\t\tint i;\n        \t\t\ttry {\n        \t\t\t\ti = buildInStream.read();\n        \t\t\t\twhile (i != -1) {\n        \t\t\t\t\ts.write(i);\n        \t\t\t\t\ti = buildInStream.read();\n        \t\t\t\t}\n        \t\t\t\tbuildInStream.close();\n        \t\t\t} catch (IOException e1) {\n        \t\t\t\te1.printStackTrace();\n        \t\t\t}\n        \t\t\t\n        \t\t\ts.append(\"\\n\\n\");\n        \t\t\te.printStackTrace(s);\n        \t\t\ts.flush();\n        \t\t\ts.close();\n        \t\t\t\n        \t\t\tIntent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);\n        \t\t\tUri contentUri = Uri.fromFile(dumpFile);\n        \t\t\tmediaScanIntent.setData(contentUri);\n        \t\t\tc.sendBroadcast(mediaScanIntent);\n        \t\t} catch (FileNotFoundException e2) {\n        \t\t\te2.printStackTrace();\n        \t\t}\n        \t\tdefaultUEH.uncaughtException(t, e);\n        \t}\n        });\n        \n\t\tmSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);\n\t\tmSharedPreferences.registerOnSharedPreferenceChangeListener(this);\n\t\tprefUnitType = mSharedPreferences.getBoolean(Const.KEY_PREF_UNIT_TYPE, prefUnitType);\n\t\tprefKnots = mSharedPreferences.getBoolean(Const.KEY_PREF_KNOTS, prefKnots);\n\t\tprefCoord = Integer.valueOf(mSharedPreferences.getString(Const.KEY_PREF_COORD, Integer.toString(prefCoord)));\n\t\tprefUtc = mSharedPreferences.getBoolean(Const.KEY_PREF_UTC, prefUtc);\n\t\tprefCid = mSharedPreferences.getBoolean(Const.KEY_PREF_CID, prefCid);\n\t\tprefCid2 = mSharedPreferences.getBoolean(Const.KEY_PREF_CID2, prefCid2);\n\t\tprefWifiSort = Integer.valueOf(mSharedPreferences.getString(Const.KEY_PREF_WIFI_SORT, Integer.toString(prefWifiSort)));\n\t\tprefMapOffline = mSharedPreferences.getBoolean(Const.KEY_PREF_MAP_OFFLINE, prefMapOffline);\n\t\tprefMapPath = mSharedPreferences.getString(Const.KEY_PREF_MAP_PATH, prefMapPath);\n\n        ActionBar actionBar = getSupportActionBar();\n        \n        setContentView(R.layout.activity_main);\n        \n        // Find out default screen orientation\n        Configuration config = getResources().getConfiguration();\n        WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);\n        int rot = wm.getDefaultDisplay().getRotation();\n        isWideScreen = (config.orientation == Configuration.ORIENTATION_LANDSCAPE &&\n        \t       (rot == Surface.ROTATION_0 || rot == Surface.ROTATION_180) ||\n        \t       config.orientation == Configuration.ORIENTATION_PORTRAIT &&\n        \t       (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270));\n        Log.d(TAG, \"isWideScreen=\" + Boolean.toString(isWideScreen));\n        \n        // Create the adapter that will return a fragment for each of the\n        // primary sections of the app.\n        mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());\n\n        // Set up the ViewPager with the sections adapter.\n        mViewPager = (ViewPager) findViewById(R.id.pager);\n        mViewPager.setAdapter(mSectionsPagerAdapter);\n        \n        Context ctx = new ContextThemeWrapper(getApplication(), R.style.AppTheme);\n        mTabLayout = new TabLayout(ctx);\n        LinearLayout.LayoutParams mTabLayoutParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);\n        mTabLayout.setLayoutParams(mTabLayoutParams);\n        \n        for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {\n        \tTabLayout.Tab newTab = mTabLayout.newTab();\n        \tnewTab.setIcon(mSectionsPagerAdapter.getPageIcon(i));\n        \tmTabLayout.addTab(newTab);\n        }\n\n        actionBar.setDisplayShowCustomEnabled(true);\n        actionBar.setCustomView(mTabLayout);\n\n        mTabLayout.setOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager));\n        mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));\n        \n        // This is needed by the mapsforge library.\n        AndroidGraphicFactory.createInstance(this.getApplication());\n\n        // Get system services for event delivery\n    \tlocationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);\n        sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);\n        mOrSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);        \n        mAccSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);     \n        mGyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); \n        mMagSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); \n        mLightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);\n        mProximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);\n        mPressureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);\n        mHumiditySensor = sensorManager.getDefaultSensor(Sensor.TYPE_RELATIVE_HUMIDITY);\n        mTempSensor = sensorManager.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE);\n        telephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);\n        connectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);\n        wifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);\n    }\n\t\n\t\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        // Inflate the menu; this adds items to the action bar if it is present.\n        getMenuInflater().inflate(R.menu.main, menu);\n        \n        return true;\n    }\n    \n    @Override\n    protected void onDestroy() {\n\t\tmSharedPreferences.unregisterOnSharedPreferenceChangeListener(this);\n\t\tsuper.onDestroy();\n    }\n    \n    /**\n     * Called when the status of the GPS changes. Updates GPS display.\n     */\n    public void onGpsStatusChanged (int event) {\n\t\tGpsStatus status = locationManager.getGpsStatus(null);\n\t\tint satsInView = 0;\n\t\tint satsUsed = 0;\n\t\tIterable<GpsSatellite> sats = status.getSatellites();\n\t\tfor (GpsSatellite sat : sats) {\n\t\t\tsatsInView++;\n\t\t\tif (sat.usedInFix()) {\n\t\t\t\tsatsUsed++;\n\t\t\t}\n\t\t}\n\n\t\tif (gpsSectionFragment != null) {\n    \t\tgpsSectionFragment.onGpsStatusChanged(status, satsInView, satsUsed, sats);\n    \t}\n    \t\n\t\tif (mapSectionFragment != null) {\n\t\t\tmapSectionFragment.onGpsStatusChanged(status, satsInView, satsUsed, sats);\n\t\t}\n    }\n    \n    /**\n     * Called when a new location is found by a registered location provider.\n     * Stores the location and updates GPS display and map view.\n     */\n    public void onLocationChanged(Location location) {\n    \t// update map view\n\t\tif (mapSectionFragment != null) {\n\t\t\tmapSectionFragment.onLocationChanged(location);\n\t\t}\n    \t\n    \t// update GPS view\n    \tif ((location.getProvider().equals(LocationManager.GPS_PROVIDER)) && (gpsSectionFragment != null)) {\n    \t\tgpsSectionFragment.onLocationChanged(location);\n    \t}\n    }\n    \n    /**\n     * Called when a menu item is selected, and triggers the appropriate action.\n     */\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n    \tint itemId = item.getItemId();\n\t\tif (itemId == R.id.action_agps) {\n\t\t\tLog.i(TAG, \"User requested AGPS data update\");\n\t\t\tif (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)\n\t\t\t\tGpsEventReceiver.refreshAgps(this, false, true);\n\t\t\telse\n\t\t\t\tActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, Const.PERM_REQUEST_REFRESH_AGPS);\n\t\t\treturn true;\n\t\t} else if (itemId == R.id.action_settings) {\n\t\t\tstartActivity(new Intent(this, SettingsActivity.class));\n\t\t\treturn true;\n\t\t} else if (itemId == R.id.action_legend) {\n\t\t\tstartActivity(new Intent(this, LegendActivity.class));\n\t\t\treturn true;\n\t\t} else if (itemId == R.id.action_about) {\n\t\t\tstartActivity(new Intent(this, AboutActivity.class));\n\t\t\treturn true;\n\t\t} else {\n\t\t\treturn super.onOptionsItemSelected(item);\n\t\t}\n    }\n    \n    /**\n     * Called when a location provider is disabled. Does nothing.\n     */\n    public void onProviderDisabled(String provider) {}\n\n    /**\n     * Called when a location provider is enabled. Does nothing.\n     */\n    public void onProviderEnabled(String provider) {}\n    \n    @Override\n    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {\n    \tStringBuilder messageBuilder = new StringBuilder();\n    \tfor (int i = 0; i < grantResults.length; i++)\n    \t\tif (permissions[i].equals(Manifest.permission.ACCESS_FINE_LOCATION)) {\n    \t\t\tif (grantResults[i] == PackageManager.PERMISSION_GRANTED) {\n    \t\t\t\tNotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);\n    \t\t\t\tnotificationManager.cancel(Const.PERM_REQUEST_LOCATION_NOTIFICATION);\n\n    \t\t\t\tif (permsRequested[Const.PERM_REQUEST_PHONE_STATE_LISTENER]) {\n    \t\t\t\t\tregisterPhoneStateListener();\n    \t\t\t\t\tpermsRequested[Const.PERM_REQUEST_PHONE_STATE_LISTENER] = false;\n    \t\t\t\t}\n    \t\t\t\tif (permsRequested[Const.PERM_REQUEST_LOCATION_UPDATES]) {\n    \t\t\t\t\trequestLocationUpdates();\n    \t\t\t\t\tpermsRequested[Const.PERM_REQUEST_LOCATION_UPDATES] = false;\n    \t\t\t\t}\n    \t\t\t\tif (permsRequested[Const.PERM_REQUEST_CELL_INFO]) {\n    \t\t\t\t\tif (radioSectionFragment != null)\n    \t\t\t\t\t\tradioSectionFragment.updateCellData(null, null, null);\n    \t\t\t\t\tpermsRequested[Const.PERM_REQUEST_CELL_INFO] = false;\n    \t\t\t\t}\n    \t\t\t\tif (requestCode == Const.PERM_REQUEST_REFRESH_AGPS)\n    \t\t\t\t\tGpsEventReceiver.refreshAgps(this, false, true);\n    \t\t\t} else if (requestCode == Const.PERM_REQUEST_REFRESH_AGPS) {\n    \t\t\t\tif (messageBuilder.length() > 0)\n    \t\t\t\t\tmessageBuilder.append(\"\\n\");\n    \t\t\t\tmessageBuilder.append(getString(R.string.status_perm_refresh_agps));\n    \t\t\t\tLog.w(TAG, \"Location permission not granted, cannot update AGPS data\");\n    \t\t\t} else {\n    \t\t\t\tif (messageBuilder.length() > 0)\n    \t\t\t\t\tmessageBuilder.append(\"\\n\");\n    \t\t\t\tmessageBuilder.append(getString(R.string.status_perm_location));\n    \t\t\t\tLog.w(TAG, \"ACCESS_FINE_LOCATION permission not granted. Location and cell info will not be available.\");\n    \t\t\t} // if grantResults[i]\n    \t\t} else if (permissions[i].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {\n    \t\t\tif (grantResults[i] == PackageManager.PERMISSION_GRANTED) {\n    \t\t\t\tif (permsRequested[Const.PERM_REQUEST_OFFLINE_MAP] && (mapSectionFragment != null)) {\n    \t\t\t\t\tmapSectionFragment.onMapSourceChanged();\n    \t\t\t\t\tpermsRequested[Const.PERM_REQUEST_OFFLINE_MAP] = false;\n    \t\t\t\t}\n    \t\t\t} else {\n    \t\t\t\tif (messageBuilder.length() > 0)\n    \t\t\t\t\tmessageBuilder.append(\"\\n\");\n    \t\t\t\tmessageBuilder.append(getString(R.string.status_perm_offline_map));\n    \t\t\t\tLog.w(TAG, \"WRITE_EXTERNAL_STORAGE permission not granted. Offline map will not be available.\");\n    \t\t\t}\n    \t\t} // if permissions[i].equals()\n    \t// for i\n    \tString message = messageBuilder.toString();\n    \tif (!message.isEmpty()) {\n    \t\tint length = (message.contains(\"\\n\")) ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT;\n    \t\tToast.makeText(this, message, length).show();\n    \t}\n    }\n\n    /**\n     * Called when a sensor's reading changes. Updates sensor display and rotates sky plot according\n     * to bearing.\n     */\n    public void onSensorChanged(SensorEvent event) {\n\t\t//to enforce sensor rate\n\t\tboolean isRateElapsed = false;\n\t\t\n\t\tswitch (event.sensor.getType()) {\n\t\t\tcase Sensor.TYPE_ACCELEROMETER:\n\t\t\t\tisRateElapsed = (event.timestamp / 1000) - mAccLast >= iSensorRate;\n\t\t\t\t// if Z acceleration is greater than X/Y combined, lock rotation, else unlock\n\t\t\t\tif (Math.pow(event.values[2], 2) > Math.pow(event.values[0], 2) + Math.pow(event.values[1], 2)) {\n\t\t\t\t\t// workaround (SCREEN_ORIENTATION_LOCK is unsupported on API < 18)\n\t\t\t\t\tif (isWideScreen)\n\t\t\t\t\t\tsetRequestedOrientation(OR_FROM_ROT_WIDE[this.getWindowManager().getDefaultDisplay().getRotation()]);\n\t\t\t\t\telse\n\t\t\t\t\t\tsetRequestedOrientation(OR_FROM_ROT_TALL[this.getWindowManager().getDefaultDisplay().getRotation()]);\n\t\t\t\t} else {\n\t\t\t\t\tsetRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase Sensor.TYPE_ORIENTATION:\n\t\t\t\tisRateElapsed = (event.timestamp / 1000) - mOrLast >= iSensorRate;\n\t\t\t\tbreak;\n\t\t\tcase Sensor.TYPE_GYROSCOPE:\n\t\t\t\tisRateElapsed = (event.timestamp / 1000) - mGyroLast >= iSensorRate;\n\t\t\t\tbreak;\n\t\t\tcase Sensor.TYPE_MAGNETIC_FIELD:\n\t\t\t\tisRateElapsed = (event.timestamp / 1000) - mMagLast >= iSensorRate;\n\t\t\t\tbreak;\n\t\t\tcase Sensor.TYPE_LIGHT:\n\t\t\t\tisRateElapsed = (event.timestamp / 1000) - mLightLast >= iSensorRate;\n\t\t\t\tbreak;\n\t\t\tcase Sensor.TYPE_PROXIMITY:\n\t\t\t\tisRateElapsed = (event.timestamp / 1000) - mProximityLast >= iSensorRate;\n\t\t\t\tbreak;\n\t\t\tcase Sensor.TYPE_PRESSURE:\n\t\t\t\tisRateElapsed = (event.timestamp / 1000) - mPressureLast >= iSensorRate;\n\t\t\t\tbreak;\n\t\t\tcase Sensor.TYPE_RELATIVE_HUMIDITY:\n\t\t\t\tisRateElapsed = (event.timestamp / 1000) - mHumidityLast >= iSensorRate;\n\t\t\t\tbreak;\n\t\t\tcase Sensor.TYPE_AMBIENT_TEMPERATURE:\n\t\t\t\tisRateElapsed = (event.timestamp / 1000) - mTempLast >= iSensorRate;\n\t\t\t\tbreak;\n\t\t}\n\t\t\n\t\tif (!isRateElapsed)\n\t\t\treturn;\n\t\t\n\t\tswitch (event.sensor.getType()) {\n\t\tcase Sensor.TYPE_ACCELEROMETER:\n\t\t\tmAccLast = event.timestamp / 1000;\n\t\t\tbreak;\n\t\tcase Sensor.TYPE_ORIENTATION:\n\t\t\tmOrLast = event.timestamp / 1000;\n\t\t\tbreak;\n\t\tcase Sensor.TYPE_GYROSCOPE:\n\t\t\tmGyroLast = event.timestamp / 1000;\n\t\t\tbreak;\n\t\tcase Sensor.TYPE_MAGNETIC_FIELD:\n\t\t\tmMagLast = event.timestamp / 1000;\n\t\t\tbreak;\n\t\tcase Sensor.TYPE_LIGHT:\n\t\t\tmLightLast = event.timestamp / 1000;\n\t\t\tbreak;\n\t\tcase Sensor.TYPE_PROXIMITY:\n\t\t\tmProximityLast = event.timestamp / 1000;\n\t\t\tbreak;\n\t\tcase Sensor.TYPE_PRESSURE:\n\t\t\tmPressureLast = event.timestamp / 1000;\n\t\t\tbreak;\n\t\tcase Sensor.TYPE_RELATIVE_HUMIDITY:\n\t\t\tmHumidityLast = event.timestamp / 1000;\n\t\t\tbreak;\n\t\tcase Sensor.TYPE_AMBIENT_TEMPERATURE:\n\t\t\tmTempLast = event.timestamp / 1000;\n\t\t\tbreak;\n\t\t}\n\n\t\t\n\t\tif (sensorSectionFragment != null) {\n\t\t\tsensorSectionFragment.onSensorChanged(event);\n    \t}\n\t\tif (gpsSectionFragment != null) {\n\t\t\tgpsSectionFragment.onSensorChanged(event);\n\t\t}\n    }\n    \t\n\t/**\n\t * Called when preferences are changed.\n\t * \n\t * This method processes changed to KEY_PREF_LOC_PROV, the list of selected\n\t * location providers. When called, it will unregister for all location \n\t * updates and re-register for updates from the selected location providers.\n\t * (This includes unregistering and immediately re-registering for those\n\t * providers which remain selected – this is due to the fact that Android\n\t * does not support unregistering from a single location provider.) \n\t */\n    @Override\n\tpublic void onSharedPreferenceChanged(SharedPreferences sharedPreferences,\n\t\t\tString key) {\n\t\tif (key.equals(Const.KEY_PREF_LOC_PROV)) {\n\t\t\t// user selected or deselected location providers, refresh list\n\t\t\tregisterLocationProviders();\n\t\t} else if (key.equals(Const.KEY_PREF_UNIT_TYPE)) {\n\t\t\tprefUnitType = sharedPreferences.getBoolean(Const.KEY_PREF_UNIT_TYPE, prefUnitType);\n\t\t} else if (key.equals(Const.KEY_PREF_KNOTS)) {\n\t\t\tprefKnots = sharedPreferences.getBoolean(Const.KEY_PREF_KNOTS, prefKnots);\n\t\t} else if (key.equals(Const.KEY_PREF_COORD)) {\n\t\t\tprefCoord = Integer.valueOf(mSharedPreferences.getString(Const.KEY_PREF_COORD, Integer.toString(prefCoord)));\n\t\t} else if (key.equals(Const.KEY_PREF_UTC)) {\n\t\t\tprefUtc = sharedPreferences.getBoolean(Const.KEY_PREF_UTC, prefUtc);\n\t\t} else if (key.equals(Const.KEY_PREF_CID)) {\n\t\t\tprefCid = sharedPreferences.getBoolean(Const.KEY_PREF_CID, prefCid);\n\t\t} else if (key.equals(Const.KEY_PREF_CID2)) {\n\t\t\tprefCid2 = sharedPreferences.getBoolean(Const.KEY_PREF_CID2, prefCid2);\n\t\t} else if (key.equals(Const.KEY_PREF_WIFI_SORT)) {\n\t\t\tprefWifiSort = Integer.valueOf(mSharedPreferences.getString(Const.KEY_PREF_WIFI_SORT, Integer.toString(prefWifiSort)));\n\t\t} else if (key.equals(Const.KEY_PREF_MAP_OFFLINE)) {\n\t\t\tprefMapOffline = sharedPreferences.getBoolean(Const.KEY_PREF_MAP_OFFLINE, prefMapOffline);\n\t\t\tif (mapSectionFragment != null)\n\t\t\t\tmapSectionFragment.onMapSourceChanged();\n\t\t\tif (prefMapOffline && (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED))\n\t\t\t\tActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, Const.PERM_REQUEST_OFFLINE_MAP);\n\t\t} else if (key.equals(Const.KEY_PREF_MAP_PATH)) {\n\t\t\tprefMapPath = sharedPreferences.getString(Const.KEY_PREF_MAP_PATH, prefMapPath);\n\t\t\tif (mapSectionFragment != null)\n\t\t\t\tmapSectionFragment.onMapSourceChanged();\n\t\t} else if (key.equals(Const.KEY_PREF_MAP_PURGE)) {\n\t\t\tif (sharedPreferences.getBoolean(Const.KEY_PREF_MAP_PURGE, false) && (mapSectionFragment != null))\n\t\t\t\tmapSectionFragment.onMapSourceChanged();\n\t\t}\n\t}\n\n    @Override\n    protected void onStart() {\n        super.onStart();\n        isStopped = false;\n        registerLocationProviders();\n        sensorManager.registerListener(this, mOrSensor, iSensorRate);\n        sensorManager.registerListener(this, mAccSensor, iSensorRate);\n        sensorManager.registerListener(this, mGyroSensor, iSensorRate);\n        sensorManager.registerListener(this, mMagSensor, iSensorRate);\n        sensorManager.registerListener(this, mLightSensor, iSensorRate);\n        sensorManager.registerListener(this, mProximitySensor, iSensorRate);\n        sensorManager.registerListener(this, mPressureSensor, iSensorRate);\n        sensorManager.registerListener(this, mHumiditySensor, iSensorRate);\n        sensorManager.registerListener(this, mTempSensor, iSensorRate);\n        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)\n        \tregisterPhoneStateListener();\n        else\n        \tpermsRequested[Const.PERM_REQUEST_PHONE_STATE_LISTENER] = true;\n        \n        // register for certain WiFi events indicating that new networks may be in range\n        // An access point scan has completed, and results are available.\n        registerReceiver(mWifiScanReceiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));\n        \n        // The state of Wi-Fi connectivity has changed.\n        registerReceiver(mWifiScanReceiver, new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION));\n        \n        // The RSSI (signal strength) has changed.\n        registerReceiver(mWifiScanReceiver, new IntentFilter(WifiManager.RSSI_CHANGED_ACTION));\n        \n        // A connection to the supplicant has been established or the connection to the supplicant has been lost.\n        registerReceiver(mWifiScanReceiver, new IntentFilter(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION));\n\n\t\tpermsRequested[Const.PERM_REQUEST_OFFLINE_MAP] = prefMapOffline;\n\n\t\t/*\n\t\t * Refresh map layers when offline map is selected and we have storage permission\n\t\t * (it might have been granted while we were gone, in which case we wouldn't have the layer)\n\t\t */\n\t\tif (prefMapOffline\n\t\t\t\t&& (mapSectionFragment != null)\n\t\t\t\t&& (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED))\n\t\t\tmapSectionFragment.onMapSourceChanged();\n\n        requestPermissions();\n    }\n\n    /**\n     * Called when a location provider's status changes. Does nothing.\n     */\n    public void onStatusChanged(String provider, int status, Bundle extras) {}\n\n    @Override\n    protected void onStop() {\n    \tisStopped = true;\n    \tlocationManager.removeUpdates(this);\n    \tlocationManager.removeGpsStatusListener(this);\n    \tsensorManager.unregisterListener(this);\n        telephonyManager.listen(mPhoneStateListener, LISTEN_NONE);\n        try {\n        \tunregisterReceiver(mWifiScanReceiver);\n        } catch (IllegalArgumentException e) {\n        \t// sometimes the receiver isn't registered, make sure we don't crash\n        \tLog.d(TAG, \"WifiScanReceiver was never registered, caught exception\");\n        }\n        // we'll just skip that so locations will get invalidated in any case\n        //providerInvalidationHandler.removeCallbacksAndMessages(null);\n        super.onStop();\n    }\n    \n\t/**\n\t * Registers for updates with selected location providers.\n\t */\n\tprotected void registerLocationProviders() {\n\t\tSet<String> providers = new HashSet<String>(mSharedPreferences.getStringSet(Const.KEY_PREF_LOC_PROV, new HashSet<String>(Arrays.asList(new String[] {LocationManager.GPS_PROVIDER, LocationManager.NETWORK_PROVIDER}))));\n\t\tlocationManager.removeUpdates(this);\n\t\t\n\t\tif (mapSectionFragment != null)\n\t\t\tmapSectionFragment.onLocationProvidersChanged(providers);\n\t\t\n\t\tif (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)\n\t\t\trequestLocationUpdates();\n\t\telse\n\t\t\tpermsRequested[Const.PERM_REQUEST_LOCATION_UPDATES] = true;\n\t}\n\t\n\t/**\n\t * Registers the {@link PhoneStateListener} for all necessary events\n\t */\n\tprivate void registerPhoneStateListener() {\n\t\ttelephonyManager.listen(mPhoneStateListener, (LISTEN_CELL_INFO | LISTEN_CELL_LOCATION | LISTEN_DATA_CONNECTION_STATE | LISTEN_SIGNAL_STRENGTHS));\n\t}\n\t\n\t/**\n\t * Requests location updates from the selected location providers.\n\t * \n\t * This method is intended to be called by {@link #registerLocationProviders(Context)} or by\n\t * {@link #onRequestPermissionsResult(int, String[], int[])}, depending on whether permissions need to be\n\t * requested.\n\t */\n\tprivate void requestLocationUpdates() {\n\t\tSet<String> providers = new HashSet<String>(mSharedPreferences.getStringSet(Const.KEY_PREF_LOC_PROV, new HashSet<String>(Arrays.asList(new String[] {LocationManager.GPS_PROVIDER, LocationManager.NETWORK_PROVIDER}))));\n\t\tList<String> allProviders = locationManager.getAllProviders();\n\t\t\n\t\tif (!isStopped) {\n\t\t\tfor (String pr : providers) {\n\t\t\t\tif (allProviders.indexOf(pr) >= 0) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tlocationManager.requestLocationUpdates(pr, 0, 0, this);\n\t\t\t\t\t\tLog.d(TAG, \"Registered with provider: \" + pr);\n\t\t\t\t\t} catch (SecurityException e) {\n\t\t\t\t\t\tLog.w(TAG, \"Permission not granted for \" + pr + \" location provider. Data display will not be available for this provider.\");\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tLog.w(TAG, \"No \" + pr + \" location provider found. Data display will not be available for this provider.\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n        try {\n        \t// if GPS is not selected, request location updates but don't store location\n        \tif ((!providers.contains(LocationManager.GPS_PROVIDER)) && (!isStopped) && (allProviders.indexOf(LocationManager.GPS_PROVIDER) >= 0))\n        \t\tlocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);\n\n        \tlocationManager.addGpsStatusListener(this);\n        } catch (SecurityException e) {\n        \tLog.w(TAG, \"Permission not granted for \" + LocationManager.GPS_PROVIDER + \" location provider. Data display will not be available for this provider.\");\n        }\n\t}\n\t\n\tprivate void requestPermissions() {\n\t\tArrayList<String> perms = new ArrayList<String>();\n\n\t\tif (permsRequested[Const.PERM_REQUEST_PHONE_STATE_LISTENER]\n\t\t\t\t|| permsRequested[Const.PERM_REQUEST_LOCATION_UPDATES]\n\t\t\t\t|| permsRequested[Const.PERM_REQUEST_CELL_INFO]) {\n\t\t\tif (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)\n\t\t\t\tperms.add(Manifest.permission.ACCESS_FINE_LOCATION);\n\t\t}\n\n\t\tif (permsRequested[Const.PERM_REQUEST_OFFLINE_MAP]) {\n\t\t\tif (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)\n\t\t\t\tperms.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);\n\t\t}\n\n\t\tif (perms.size() > 0)\n\t\t\tActivityCompat.requestPermissions(this, perms.toArray(new String[]{}), Const.PERM_REQUEST_STARTUP);\n\t}\n\n    /**\n     * A {@link FragmentPagerAdapter} that returns a fragment corresponding to\n     * one of the sections/tabs/pages.\n     */\n    public class SectionsPagerAdapter extends FragmentPagerAdapter {\n\n        public SectionsPagerAdapter(FragmentManager fm) {\n            super(fm);\n        }\n\n        @Override\n        public Fragment getItem(int position) {\n            // getItem is called to instantiate the fragment for the given page.\n            // Return a DummySectionFragment (defined as a static inner class\n            // below) with the page number as its lone argument.\n        \tFragment fragment;\n            switch (position) {\n            case 0:\n            \tfragment = new GpsSectionFragment();\n                return fragment;\n            case 1:\n            \tfragment = new SensorSectionFragment();\n                return fragment;\n            case 2:\n            \tfragment = new RadioSectionFragment();\n                return fragment;\n            case 3:\n            \tfragment = new MapSectionFragment();\n                return fragment;\n            }\n        return null;\n        }\n\n        @Override\n        public int getCount() {\n            // Show 4 total pages.\n            return 4;\n        }\n\n        public Drawable getPageIcon(int position) {\n            switch (position) {\n                case 0:\n                    return getResources().getDrawable(R.drawable.ic_action_gps);\n                case 1:\n                    return getResources().getDrawable(R.drawable.ic_action_sensor);\n                case 2:\n                    return getResources().getDrawable(R.drawable.ic_action_radio);\n                case 3:\n                    return getResources().getDrawable(R.drawable.ic_action_map);\n            }\n            return null;\n        }\n\n        @Override\n        public CharSequence getPageTitle(int position) {\n            Locale l = Locale.getDefault();\n            switch (position) {\n                case 0:\n                    return getString(R.string.title_section1).toUpperCase(l);\n                case 1:\n                    return getString(R.string.title_section2).toUpperCase(l);\n                case 2:\n                    return getString(R.string.title_section3).toUpperCase(l);\n                case 3:\n                    return getString(R.string.title_section4).toUpperCase(l);\n            }\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/ui/MapDownloadActivity.java",
    "content": "/*\n * Copyright © 2013–2016 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat.ui;\n\nimport java.util.List;\n\nimport pl.polidea.treeview.DownloadTreeStateManager;\nimport pl.polidea.treeview.TreeBuilder;\nimport pl.polidea.treeview.TreeViewList;\n\nimport com.vonglasow.michael.satstat.Const;\nimport com.vonglasow.michael.satstat.R;\nimport com.vonglasow.michael.satstat.utils.DownloadTreeViewAdapter;\nimport com.vonglasow.michael.satstat.utils.RemoteDirListTask;\nimport com.vonglasow.michael.satstat.utils.RemoteDirListListener;\nimport com.vonglasow.michael.satstat.utils.RemoteFile;\n\nimport android.os.Bundle;\nimport android.support.v7.app.ActionBar;\nimport android.support.v7.app.AppCompatActivity;\nimport android.util.Log;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.Button;\nimport android.widget.LinearLayout;\nimport android.widget.ProgressBar;\n\n/**\n * An activity which displays a list of maps available on the download server and lets the user\n * select maps to download.\n */\npublic class MapDownloadActivity extends AppCompatActivity implements RemoteDirListListener {\n\tprivate static final String TAG = MapDownloadActivity.class.getSimpleName();\n\n\t// FTP is also available but we don't support it yet\n\t//public static final String MAP_DOWNLOAD_BASE_URL = \"ftp://ftp-stud.hs-esslingen.de/pub/Mirrors/download.mapsforge.org/maps/\";\n\tpublic static final String MAP_DOWNLOAD_BASE_URL = \"https://ftp-stud.hs-esslingen.de/pub/Mirrors/download.mapsforge.org/maps/v4/\";\n\t// TODO there's also the Mapsforge download server as a fallback\n\n\tprivate static final String STATE_KEY_TREE_MANAGER = \"treeManager\";\n\tprivate static final String STATE_KEY_DOWNLOADS = \"downloads\";\n\n\tRemoteDirListTask dirListTask = null;\n\tProgressBar downloadProgress;\n\tLinearLayout downloadErrorLayout;\n\tButton downloadRetry;\n\tprivate TreeViewList treeView;\n\tprivate DownloadTreeStateManager manager = null;\n\tprivate TreeBuilder<RemoteFile> builder = null;\n\tprivate DownloadTreeViewAdapter treeViewAdapter;\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tBundle state = savedInstanceState;\n\n\t\tif (state == null)\n\t\t\tstate = this.getIntent().getBundleExtra(Const.KEY_SAVED_INSTANCE_STATE);\n\n\t\tsuper.onCreate(state);\n\t\t\n\t\tif (state != null) {\n\t\t\tmanager = (DownloadTreeStateManager) state.getSerializable(STATE_KEY_TREE_MANAGER);\n\t\t}\n\t\tif (manager == null)\n\t\t\tmanager = new DownloadTreeStateManager();\n\t\tbuilder = new TreeBuilder<RemoteFile>(manager);\n\n\t\tsetContentView(R.layout.activity_map_download);\n\n\t\tActionBar actionBar = getSupportActionBar();\n\t\tactionBar.setDisplayHomeAsUpEnabled(true);\n\n\t\tdownloadProgress = (ProgressBar) findViewById(R.id.downloadProgress);\n\t\tdownloadErrorLayout = (LinearLayout) findViewById(R.id.downloadErrorLayout);\n\t\tdownloadRetry = (Button) findViewById(R.id.downloadRetry);\n\t\ttreeView = (TreeViewList) findViewById(R.id.downloadList);\n\t\t/*\n\t\t * FIXME: Android wants the number of distinct layouts, which here is the same as the number of\n\t\t * levels and in theory unlimited. Using more levels than specified here will cause exceptions which\n\t\t * are beyond our control (only system functions in the call stack) and semi-random (creating more\n\t\t * levels than specified will work initially but the code will barf sometime later, e.g. on scroll).\n\t\t * \n\t\t * The maximum number of levels is currently 4 (multilingual/continent/country/region.map),\n\t\t * therefore 5 is safe even if another one level is added. However, if the layout on the server ever\n\t\t * changes and goes beyond that, we'll get semi-random crashes.\n\t\t */\n\t\ttreeViewAdapter = new DownloadTreeViewAdapter(this, manager, 5);\n\t\ttreeView.setAdapter(treeViewAdapter);\n\t\ttreeView.setCollapsible(true);\n\t\ttreeView.setCollapsedDrawable(getResources().getDrawable(R.drawable.ic_expand_more));\n\t\ttreeView.setExpandedDrawable(getResources().getDrawable(R.drawable.ic_expand_less));\n\t\ttreeView.setIndentWidth(24);\n\t\t\n\t\tdownloadErrorLayout.setVisibility(View.GONE);\n\n\t\tOnClickListener clis = new OnClickListener () {\n\t\t\t@Override\n\t\t\tpublic void onClick(View v) {\n\t\t\t\tif (v == downloadRetry) {\n\t\t\t\t\ttreeView.setVisibility(View.GONE);\n\t\t\t\t\tdownloadErrorLayout.setVisibility(View.GONE);\n\t\t\t\t\tdownloadProgress.setVisibility(View.VISIBLE);\n\t\t\t\t\t// get data from server\n\t\t\t\t\tdirListTask = new RemoteDirListTask(MapDownloadActivity.this, null);\n\t\t\t\t\tdirListTask.execute(MAP_DOWNLOAD_BASE_URL);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tdownloadRetry.setOnClickListener(clis);\n\n\t\tList<RemoteFile> topItems = manager.getChildren(null);\n\t\tif ((topItems == null) || (topItems.size() == 0)) {\n\t\t\ttreeView.setVisibility(View.GONE);\n\t\t\tdownloadProgress.setVisibility(View.VISIBLE);\n\t\t\t// get data from server\n\t\t\tdirListTask = new RemoteDirListTask(this, null);\n\t\t\tdirListTask.execute(MAP_DOWNLOAD_BASE_URL);\n\t\t}\n\t\t\n\t\ttreeViewAdapter.registerIntentReceiver();\n\t}\n\n\t@Override\n\tprotected void onDestroy() {\n\t\tif ((dirListTask != null) && (!dirListTask.isCancelled()))\n\t\t\tdirListTask.cancel(true);\n\t\ttreeViewAdapter.releaseIntentReceiver();\n\t\tsuper.onDestroy();\n\t}\n\t\n\t@Override\n\tpublic boolean onOptionsItemSelected(MenuItem item) {\n\t\tswitch (item.getItemId()) {\n\t\tcase android.R.id.home:\n\t\t\tonBackPressed();\n\t\t\treturn true;\n\t\t}\n\n\t\treturn super.onOptionsItemSelected(item);\n\t}\n\n\t@Override\n\tpublic void onRemoteDirListReady(RemoteDirListTask task, RemoteFile[] rfiles) {\n\t\tdownloadProgress.setVisibility(View.GONE);\n\t\tbuilder.clear();\n\t\tif (rfiles != null) {\n\t\t\ttreeView.setVisibility(View.VISIBLE);\n\t\t\tdownloadErrorLayout.setVisibility(View.GONE);\n\t\t\tfor (RemoteFile rf : rfiles)\n\t\t\t\tbuilder.sequentiallyAddNextNode(rf, 0);\n\t\t} else {\n\t\t\ttreeView.setVisibility(View.GONE);\n\t\t\tdownloadErrorLayout.setVisibility(View.VISIBLE);\n\t\t}\n\t}\n\t\n\t@Override\n\tprotected void onStart() {\n\t\tsuper.onStart();\n\t}\n\t\n\t@Override\n\tprotected void onSaveInstanceState(final Bundle outState) {\n\t\toutState.putSerializable(STATE_KEY_TREE_MANAGER, manager);\n\t\tsuper.onSaveInstanceState(outState);\n\t}\n\n\t@Override\n\tprotected void onStop() {\n\t\tif (treeViewAdapter != null) {\n\t\t\tBundle outState = new Bundle();\n\t\t\tthis.onSaveInstanceState(outState);\n\t\t\ttreeViewAdapter.storeInstanceState(outState);\n\t\t}\n\n\t\tsuper.onStop();\n\t}\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/ui/MapSectionFragment.java",
    "content": "/*\n * Copyright © 2013–2016 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat.ui;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.mapsforge.core.graphics.Bitmap;\nimport org.mapsforge.core.graphics.Paint;\nimport org.mapsforge.core.graphics.Style;\nimport org.mapsforge.core.model.BoundingBox;\nimport org.mapsforge.core.model.Dimension;\nimport org.mapsforge.core.model.LatLong;\nimport org.mapsforge.core.model.Point;\nimport org.mapsforge.core.util.LatLongUtils;\nimport org.mapsforge.map.android.graphics.AndroidGraphicFactory;\nimport org.mapsforge.map.android.input.MapZoomControls.Orientation;\nimport org.mapsforge.map.android.util.AndroidUtil;\nimport org.mapsforge.map.android.view.MapView;\nimport org.mapsforge.map.datastore.MultiMapDataStore;\nimport org.mapsforge.map.datastore.MultiMapDataStore.DataPolicy;\nimport org.mapsforge.map.layer.Layer;\nimport org.mapsforge.map.layer.LayerManager;\nimport org.mapsforge.map.layer.Layers;\nimport org.mapsforge.map.layer.cache.TileCache;\nimport org.mapsforge.map.layer.download.TileDownloadLayer;\nimport org.mapsforge.map.layer.download.tilesource.OnlineTileSource;\nimport org.mapsforge.map.layer.overlay.Circle;\nimport org.mapsforge.map.layer.overlay.Marker;\nimport org.mapsforge.map.layer.renderer.TileRendererLayer;\nimport org.mapsforge.map.reader.MapFile;\nimport org.mapsforge.map.reader.header.MapFileException;\nimport org.mapsforge.map.rendertheme.InternalRenderTheme;\nimport org.mapsforge.map.util.MapViewProjection;\n\nimport com.vonglasow.michael.satstat.Const;\nimport com.vonglasow.michael.satstat.R;\n\nimport android.Manifest;\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.content.pm.PackageManager;\nimport android.content.pm.PackageManager.NameNotFoundException;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.graphics.drawable.Drawable;\nimport android.location.GpsSatellite;\nimport android.location.GpsStatus;\nimport android.location.Location;\nimport android.location.LocationManager;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.content.ContextCompat;\nimport android.util.Log;\nimport android.view.GestureDetector;\nimport android.view.LayoutInflater;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.View.OnClickListener;\nimport android.widget.FrameLayout;\nimport android.widget.ImageButton;\nimport android.widget.TextView;\n\n/**\n * The fragment which displays the map view.\n */\npublic class MapSectionFragment extends Fragment {\n\tpublic static final String TAG = \"MapSectionFragment\";\n\t/**\n\t * The fragment argument representing the section number for this\n\t * fragment.\n\t */\n\tpublic static final String ARG_SECTION_NUMBER = \"section_number\";\n\n\tprivate static final String KEY_LOCATION_STALE = \"isStale\";\n\n\tprivate static final int PROVIDER_EXPIRATION_DELAY = 6000; // the time after which a location is considered stale \n\n\tprivate MainActivity mainActivity = null;\n\n\tOnlineTileSource onlineTileSource;\n\tprivate MapView mapMap;\n\tprivate TileDownloadLayer mapDownloadLayer = null;\n\tprivate TileRendererLayer mapRendererLayer = null;\n\tprivate TileCache mapDownloadTileCache = null;\n\tprivate TileCache mapRendererTileCache = null;\n\tprivate ImageButton mapReattach;\n\tprivate TextView mapAttribution;\n\tprivate boolean isMapViewAttached = true;\n\tprivate HashMap<String, Circle> mapCircles;\n\tprivate HashMap<String, Marker> mapMarkers;\n\n\n\t/**\n\t * Cached map of locations reported by the providers.\n\t * \n\t * The keys correspond to the provider names as defined by LocationManager.\n\t * The entries are {@link Location} instances. For valid and recent\n\t * locations these are copies of the locations supplied by\n\t * {@link LocationManager}. Invalid locations, intended as placeholders,\n\t * have an empty provider string and should not be processed. Stale\n\t * locations have isStale entry in their extras set to true. They can be\n\t * processed but may require special handling.\n\t */\n\tprivate HashMap<String, Location> providerLocations;\n\n\tprivate HashMap<String, String> providerStyles;\n\tprivate HashMap<String, String> providerAppliedStyles;\n\tprivate List<String> mAvailableProviderStyles;\n\tprivate Handler providerInvalidationHandler = null;\n\tprivate HashMap<String, Runnable> providerInvalidators;\n\n\n\tpublic MapSectionFragment() {\n\t}\n\t\n\n\t/**\n\t * Applies a style to the map overlays associated with a given location provider.\n\t * \n\t * This method changes the style (effectively, the color) of the circle and\n\t * marker overlays. Its main purpose is to switch the color of the overlays\n\t * between gray and the provider color.\n\t * \n\t * @param context The context of the caller\n\t * @param provider The name of the location provider, as returned by\n\t * {@link LocationProvider.getName()}.\n\t * @param styleName The name of the style to apply. If it is null, the\n\t * default style for the provider as returned by \n\t * assignLocationProviderStyle() is applied. \n\t */\n\tprotected void applyLocationProviderStyle(Context context, String provider, String styleName) {\n\t\tString sn = (styleName != null)?styleName:assignLocationProviderStyle(provider);\n\n\t\tBoolean isStyleChanged = !sn.equals(providerAppliedStyles.get(provider));\n\t\tBoolean needsRedraw = false;\n\n\t\tResources res = context.getResources();\n\t\tTypedArray style = res.obtainTypedArray(res.getIdentifier(sn, \"array\", context.getPackageName()));\n\n\t\t// Circle layer\n\t\tCircle circle = mapCircles.get(provider);\n\t\tif (circle != null) {\n\t\t\tcircle.getPaintFill().setColor(style.getColor(Const.STYLE_FILL, R.color.circle_gray_fill));\n\t\t\tcircle.getPaintStroke().setColor(style.getColor(Const.STYLE_STROKE, R.color.circle_gray_stroke));\n\t\t\tneedsRedraw = isStyleChanged && circle.isVisible();\n\t\t}\n\n\t\t//Marker layer\n\t\tMarker marker = mapMarkers.get(provider);\n\t\tif (marker != null) {\n\t\t\tDrawable drawable = style.getDrawable(Const.STYLE_MARKER);\n\t\t\tBitmap bitmap = AndroidGraphicFactory.convertToBitmap(drawable);\n\t\t\tmarker.setBitmap(bitmap);\n\t\t\tneedsRedraw = needsRedraw || (isStyleChanged && marker.isVisible());\n\t\t}\n\n\t\tif (needsRedraw) {\n\t\t\tLayerManager manager = mapMap.getLayerManager();\n\t\t\tif (manager != null)\n\t\t\t\tmanager.redrawLayers();\n\t\t}\n\t\tproviderAppliedStyles.put(provider, sn);\n\t\tstyle.recycle();\n\t}\n\n\n\t/**\n\t * Returns the map overlay style to use for a given location provider.\n\t * \n\t * This method first checks if a style has already been assigned to the\n\t * location provider. In that case the already assigned style is returned.\n\t * Otherwise a new style is assigned and the assignment is stored\n\t * internally and written to SharedPreferences.\n\t * @param provider\n\t * @return The style to use for non-stale locations\n\t */\n\tprotected String assignLocationProviderStyle(String provider) {\n\t\tString styleName = providerStyles.get(provider);\n\t\tif (styleName == null) {\n\t\t\t/*\n\t\t\t * Not sure if this ever happens but I can't rule it out. Scenarios I can think of:\n\t\t\t * - A custom location provider which identifies itself as \"passive\"\n\t\t\t * - A combination of the following:\n\t\t\t *   - Passive location provider is selected\n\t\t\t *   - A new provider is added while we're running (so it's not in our list)\n\t\t\t *   - Another app starts using the new provider\n\t\t\t *   - The passive location provider forwards us an update from the new provider\n\t\t\t */\n\t\t\tif (mAvailableProviderStyles.isEmpty())\n\t\t\t\tmAvailableProviderStyles.addAll(Arrays.asList(Const.LOCATION_PROVIDER_STYLES));\n\t\t\tstyleName = mainActivity.mSharedPreferences.getString(Const.KEY_PREF_LOC_PROV_STYLE + provider, mAvailableProviderStyles.get(0));\n\t\t\tproviderStyles.put(provider, styleName);\n\t\t\tSharedPreferences.Editor spEditor = mainActivity.mSharedPreferences.edit();\n\t\t\tspEditor.putString(Const.KEY_PREF_LOC_PROV_STYLE + provider, styleName);\n\t\t\tspEditor.commit();\n\t\t}\n\t\treturn styleName;\n\t}\n\t\n\t\n\t/**\n\t * Creates layers and associated tile caches for the map view.\n\t * \n\t * @param createOverlays Whether to create overlays (circle and markers) or just tile layers\n\t */\n\tprivate void createLayers(boolean createOverlays) {\n\t\tLayerManager layerManager = mapMap.getLayerManager();\n\t\tLayers layers = layerManager.getLayers();\n\n\t\tif (mainActivity.prefMapOffline && (ContextCompat.checkSelfPermission(mainActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)) {\n\t\t\t/*\n\t\t\t * If offline maps are enabled AND we have storage permission, use offline map tiles.\n\t\t\t * Skip this step if we don't have permission, else we would pollute the cache with blank tiles.\n\t\t\t */\n\t\t\tif (mapRendererTileCache == null)\n\t\t\t\tmapRendererTileCache = AndroidUtil.createExternalStorageTileCache(this.getContext(),\n\t\t\t\t\t\tConst.TILE_CACHE_INTERNAL_RENDER_THEME,\n\t\t\t\t\t\tMath.round(AndroidUtil.getMinimumCacheSize(this.getContext(),\n\t\t\t\t\t\t\t\tmapMap.getModel().displayModel.getTileSize(),\n\t\t\t\t\t\t\t\tmapMap.getModel().frameBufferModel.getOverdrawFactor(),\n\t\t\t\t\t\t\t\t1f)),\n\t\t\t\t\t\t\t\tmapMap.getModel().displayModel.getTileSize(),\n\t\t\t\t\t\t\t\ttrue);\n\t\t\t\n\t\t\t/*\n\t\t\t * If the offline map path changes, we need to purge the cache. This ensures we don't serve stale\n\t\t\t * tiles generated from the old map set.\n\t\t\t * \n\t\t\t * We accomplish this by comparing the current map path to the map path for which we last\n\t\t\t * instantiated a cache. If they differ, we flush the cache and store the new map path.\n\t\t\t */\n\t\t\tString cachedPath = mainActivity.mSharedPreferences.getString(Const.KEY_PREF_MAP_CACHED_PATH, \"\");\n\t\t\tif (!cachedPath.equals(mainActivity.prefMapPath)) {\n\t\t\t\tmapRendererTileCache.purge();\n\t\t\t\tSharedPreferences.Editor spEditor = mainActivity.mSharedPreferences.edit();\n\t\t\t\tspEditor.putString(Const.KEY_PREF_MAP_CACHED_PATH, mainActivity.prefMapPath);\n\t\t\t\tspEditor.commit();\n\t\t\t}\n\t\t\t\n\t\t\tMultiMapDataStore mapDataStore = new MultiMapDataStore(DataPolicy.DEDUPLICATE);\n\t\t\tFile mapDir = new File(mainActivity.prefMapPath);\n\t\t\tLog.i(TAG, String.format(\"Looking for maps in: %s\", mapDir.getName()));\n\t\t\tif (mapDir.exists() && mapDir.canRead() && mapDir.isDirectory())\n\t\t\t\tfor (File file : mapDir.listFiles())\n\t\t\t\t\tif (file.isFile())\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tMapFile mapFile = new MapFile(file);\n\t\t\t\t\t\t\tmapDataStore.addMapDataStore(mapFile, false, false);\n\t\t\t\t\t\t\tLog.i(TAG, String.format(\"Added map file: %s\", file.getName()));\n\t\t\t\t\t\t} catch (MapFileException e) {\n\t\t\t\t\t\t\t// not a map file, skip\n\t\t\t\t\t\t\tLog.w(TAG, String.format(\"Could not add map file: %s\", file.getName()));\n\t\t\t\t\t\t}\n\n\t\t\tmapRendererLayer = new TileRendererLayer(mapRendererTileCache, mapDataStore,\n\t\t\t\t\tmapMap.getModel().mapViewPosition, false, true, false, AndroidGraphicFactory.INSTANCE);\n\n\t\t\tmapRendererLayer.setXmlRenderTheme(InternalRenderTheme.OSMARENDER);\n\n\t\t\t//mapRendererLayer.setTextScale(1.5f); // FIXME\n\t\t\tlayers.add(0, mapRendererLayer);\n\t\t\tmapAttribution.setText(R.string.osm_attribution);\n\t\t} else if (!mainActivity.prefMapOffline) {\n\t\t\t// use online map tiles\n\t\t\tif (mapDownloadTileCache == null)\n\t\t\t\tmapDownloadTileCache = AndroidUtil.createExternalStorageTileCache(this.getContext(),\n\t\t\t\t\t\tConst.TILE_CACHE_OSM,\n\t\t\t\t\t\tMath.round(AndroidUtil.getMinimumCacheSize(this.getContext(),\n\t\t\t\t\t\t\t\tmapMap.getModel().displayModel.getTileSize(),\n\t\t\t\t\t\t\t\tmapMap.getModel().frameBufferModel.getOverdrawFactor(),\n\t\t\t\t\t\t\t\t1f)),\n\t\t\t\t\t\t\t\tmapMap.getModel().displayModel.getTileSize(),\n\t\t\t\t\t\t\t\ttrue);\n\n\t\t\tmapDownloadLayer = new TileDownloadLayer(mapDownloadTileCache,\n\t\t\t\t\tmapMap.getModel().mapViewPosition, onlineTileSource,\n\t\t\t\t\tAndroidGraphicFactory.INSTANCE);\n\t\t\tlayers.add(0, mapDownloadLayer);\n\t\t\t/*\n\t\t\t * Since tiles are now sourced from OSM (following Mapquest's decision to discontinue their free\n\t\t\t * tile service), attribution is the same for online and offline. This may change if we switch\n\t\t\t * to a different tile source (or allow multiple ones) - therefore, attribution should still\n\t\t\t * depend on the map source.\n\t\t\t */\n\t\t\tmapAttribution.setText(R.string.osm_attribution);\n\t\t} else\n\t\t\tmapAttribution.setText(\"\");\n\n\t\t//parse list of location providers\n\t\tif (createOverlays)\n\t\t\tonLocationProvidersChanged(\n\t\t\t\t\tmainActivity.mSharedPreferences.getStringSet(\n\t\t\t\t\t\t\tConst.KEY_PREF_LOC_PROV,\n\t\t\t\t\t\t\tnew HashSet<String>(Arrays.asList(\n\t\t\t\t\t\t\t\t\tnew String[] {LocationManager.GPS_PROVIDER, LocationManager.NETWORK_PROVIDER}\n\t\t\t\t\t\t\t\t\t))));\n\t\t\n\t\t// mark cache as purged\n\t\tSharedPreferences.Editor spEditor = mainActivity.mSharedPreferences.edit();\n\t\tspEditor.putBoolean(Const.KEY_PREF_MAP_PURGE, false);\n\t\tspEditor.commit();\n\t}\n\t\n\t\n\t/**\n\t * Destroys layers and associated tile caches for the map view.\n\t * \n\t * @param destroyOverlays Whether to destroy overlays (markers and circles) or just the tile layers\n\t */\n\tprivate void destroyLayers(boolean destroyOverlays) {\n\t\tLayers layers = null;\n\t\tif (mapMap != null)\n\t\t\tlayers = mapMap.getLayerManager().getLayers();\n\t\t\n\t\tif (mapDownloadLayer != null) {\n\t\t\tif (layers != null)\n\t\t\t\tlayers.remove(mapDownloadLayer);\n\t\t\tmapDownloadLayer.onDestroy();\n\t\t\tmapDownloadLayer = null;\n\t\t}\n\t\t\n\t\tif (mapRendererLayer != null) {\n\t\t\tif (layers != null)\n\t\t\t\tlayers.remove(mapRendererLayer);\n\t\t\tmapRendererLayer.onDestroy();\n\t\t\tmapRendererLayer = null;\n\t\t}\n\t\t\n\t\tif (mapDownloadTileCache != null) {\n\t\t\tmapDownloadTileCache.destroy();\n\t\t\tmapDownloadTileCache = null;\n\t\t}\n\n\t\tif (mapRendererTileCache != null) {\n\t\t\tmapRendererTileCache.destroy();\n\t\t\tmapRendererTileCache = null;\n\t\t}\n\n\t\tif (destroyOverlays && (layers != null))\n\t\t\tfor (Layer layer : layers) {\n\t\t\t\tlayer.onDestroy();\n\t\t\t\tlayers.remove(layer);\n\t\t\t}\n\t}\n\n\n\t/**\n\t * Determines if a location is stale.\n\t * \n\t * A location is considered stale if its Extras have an isStale key set to\n\t * True. A location without this key is not considered stale.\n\t * \n\t * @param location\n\t * @return True if stale, False otherwise\n\t */\n\tpublic static boolean isLocationStale(Location location) {\n\t\tBundle extras = location.getExtras();\n\t\tif (extras == null)\n\t\t\treturn false;\n\t\treturn extras.getBoolean(KEY_LOCATION_STALE);\n\t}\n\n\n\tpublic static void markLocationAsStale(Location location) {\n\t\tif (location.getExtras() == null)\n\t\t\tlocation.setExtras(new Bundle());\n\t\tlocation.getExtras().putBoolean(KEY_LOCATION_STALE, true);\n\t}\n\n\n\t@Override\n\tpublic View onCreateView(LayoutInflater inflater, ViewGroup container,\n\t\t\tBundle savedInstanceState) {\n\t\tmainActivity = (MainActivity) this.getContext();\n\t\tView rootView = inflater.inflate(R.layout.fragment_main_map, container, false);\n\t\tfloat density = this.getContext().getResources().getDisplayMetrics().density;\n\n\t\tString versionName;\n\t\ttry {\n\t\t\tversionName = mainActivity.getPackageManager().getPackageInfo(mainActivity.getPackageName(), 0).versionName;\n\t\t} catch (NameNotFoundException e) {\n\t\t\tversionName = \"unknown\";\n\t\t}\n\n\t\tmapReattach = (ImageButton) rootView.findViewById(R.id.mapReattach);\n\t\tmapAttribution = (TextView) rootView.findViewById(R.id.mapAttribution);\n\n\t\tmapReattach.setVisibility(View.GONE);\n\t\tisMapViewAttached = true;\n\n\t\tOnClickListener clis = new OnClickListener () {\n\t\t\t@Override\n\t\t\tpublic void onClick(View v) {\n\t\t\t\tif (v == mapReattach) {\n\t\t\t\t\tisMapViewAttached = true;\n\t\t\t\t\tmapReattach.setVisibility(View.GONE);\n\t\t\t\t\tupdateMap();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tmapReattach.setOnClickListener(clis);\n\n\t\t// Initialize controls\n\t\tmapMap = new MapView(rootView.getContext());\n\t\t((FrameLayout) rootView).addView(mapMap, 0);\n\n\t\tmapMap.setClickable(true);\n\t\tmapMap.getMapScaleBar().setVisible(true);\n\t\tmapMap.getMapScaleBar().setMarginVertical((int)(density * 16));\n\t\tmapMap.setBuiltInZoomControls(true);\n\t\tmapMap.getMapZoomControls().setZoomLevelMin((byte) 10);\n\t\tmapMap.getMapZoomControls().setZoomLevelMax((byte) 20);\n\t\tmapMap.getMapZoomControls().setZoomControlsOrientation(Orientation.VERTICAL_IN_OUT);\n\t\tmapMap.getMapZoomControls().setZoomInResource(R.drawable.zoom_control_in);\n\t\tmapMap.getMapZoomControls().setZoomOutResource(R.drawable.zoom_control_out);\n\t\tmapMap.getMapZoomControls().setMarginHorizontal((int)(density * 8));\n\t\tmapMap.getMapZoomControls().setMarginVertical((int)(density * 16));\n\t\tproviderLocations = new HashMap<String, Location>();\n\n\t\tmAvailableProviderStyles = new ArrayList<String>(Arrays.asList(Const.LOCATION_PROVIDER_STYLES));\n\n\t\tproviderStyles = new HashMap<String, String>();\n\t\tproviderAppliedStyles = new HashMap<String, String>();\n\n\t\tproviderInvalidationHandler = new Handler();\n\t\tproviderInvalidators = new HashMap<String, Runnable>();\n\n\t\tonlineTileSource = new OnlineTileSource(Const.TILE_SERVER_OSM, 80);\n\t\tonlineTileSource.setUserAgent(String.format(\"%s/%s (%s)\", \"SatStat\", versionName, System.getProperty(\"http.agent\")));\n\t\tonlineTileSource.setName(Const.TILE_CACHE_OSM)\n\t\t.setAlpha(false)\n\t\t.setBaseUrl(Const.TILE_URL_OSM)\n\t\t.setExtension(Const.TILE_EXTENSION_OSM)\n\t\t.setParallelRequestsLimit(8)\n\t\t.setProtocol(\"http\")\n\t\t.setTileSize(256)\n\t\t.setZoomLevelMax((byte) 18)\n\t\t.setZoomLevelMin((byte) 0);\n\n\t\tGestureDetector gd = new GestureDetector(rootView.getContext(),\n\t\t\t\tnew GestureDetector.SimpleOnGestureListener() {\n\t\t\tpublic boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {\n\t\t\t\tmapReattach.setVisibility(View.VISIBLE);\n\t\t\t\tisMapViewAttached = false;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\t\t\t);\n\n\t\tmapMap.setGestureDetector(gd);\n\n\t\tmainActivity.mapSectionFragment = this;\n\n\t\tfloat lat = mainActivity.mSharedPreferences.getFloat(Const.KEY_PREF_MAP_LAT, 360.0f);\n\t\tfloat lon = mainActivity.mSharedPreferences.getFloat(Const.KEY_PREF_MAP_LON, 360.0f);\n\n\t\tif ((lat < 360.0f) && (lon < 360.0f)) {\n\t\t\tmapMap.getModel().mapViewPosition.setCenter(new LatLong(lat, lon));\n\t\t}\n\n\t\tint zoom = mainActivity.mSharedPreferences.getInt(Const.KEY_PREF_MAP_ZOOM, 16);\n\t\tmapMap.getModel().mapViewPosition.setZoomLevel((byte) zoom);\n\t\t\n\t\tcreateLayers(true);\n\n\t\treturn rootView;\n\t}\n\n\t\n\t@Override\n\tpublic void onDestroyView() {\n\t\tdestroyLayers(true);\n\n\t\tif (mainActivity.mapSectionFragment == this)\n\t\t\tmainActivity.mapSectionFragment = null;\n\n\t\tif (mapMap != null)\n\t\t\tmapMap.destroyAll();\n\t\tAndroidGraphicFactory.clearResourceMemoryCache();\n\t\tsuper.onDestroyView();\n\t}\n\t\n\n\t/**\n\t * Called by {@link MainActivity} when the status of the GPS changes. Updates GPS display.\n\t */\n\tpublic void onGpsStatusChanged(GpsStatus status, int satsInView, int satsUsed, Iterable<GpsSatellite> sats) {\n\t\tif (satsUsed == 0) {\n\t\t\tLocation location = providerLocations.get(LocationManager.GPS_PROVIDER);\n\t\t\tif (location != null)\n\t\t\t\tmarkLocationAsStale(location);\n\t\t\tapplyLocationProviderStyle(this.getContext(), LocationManager.GPS_PROVIDER, Const.LOCATION_PROVIDER_GRAY);\n\t\t}\n\t}\n\n\n\t/**\n\t * Called when a new location is found by a registered location provider.\n\t * Stores the location and updates GPS display and map view.\n\t */\n\tpublic void onLocationChanged(Location location) {\n\t\t// some providers may report NaN for latitude and longitude:\n\t\t// if that happens, do not process this location and mark any previous\n\t\t// location from that provider as stale\n\t\tif (Double.isNaN(location.getLatitude()) || Double.isNaN(location.getLongitude())) {\n\t\t\tmarkLocationAsStale(providerLocations.get(location.getProvider()));\n\t\t\tapplyLocationProviderStyle(this.getContext(), location.getProvider(), Const.LOCATION_PROVIDER_GRAY);\n\t\t\treturn;\n\t\t}\n\n\t\tif (providerLocations.containsKey(location.getProvider()))\n\t\t\tproviderLocations.put(location.getProvider(), new Location(location));\n\n\t\tLatLong latLong = new LatLong(location.getLatitude(), location.getLongitude());\n\n\t\tCircle circle = mapCircles.get(location.getProvider());\n\t\tMarker marker = mapMarkers.get(location.getProvider());\n\n\t\tif (circle != null) {\n\t\t\tcircle.setLatLong(latLong);\n\t\t\tif (location.hasAccuracy()) {\n\t\t\t\tcircle.setVisible(true);\n\t\t\t\tcircle.setRadius(location.getAccuracy());\n\t\t\t} else {\n\t\t\t\tLog.d(\"MainActivity\", \"Location from \" + location.getProvider() + \" has no accuracy\");\n\t\t\t\tcircle.setVisible(false);\n\t\t\t}\n\t\t}\n\n\t\tif (marker != null) {\n\t\t\tmarker.setLatLong(latLong);\n\t\t\tmarker.setVisible(true);\n\t\t}\n\n\t\tapplyLocationProviderStyle(this.getContext(), location.getProvider(), null);\n\n\t\tRunnable invalidator = providerInvalidators.get(location.getProvider());\n\t\tif (invalidator != null) {\n\t\t\tproviderInvalidationHandler.removeCallbacks(invalidator);\n\t\t\tproviderInvalidationHandler.postDelayed(invalidator, PROVIDER_EXPIRATION_DELAY);\n\t\t}\n\n\t\t// redraw, move locations into view and zoom out as needed\n\t\tif ((circle != null) || (marker != null) || (invalidator != null))\n\t\t\tupdateMap();\n\t}\n\t\n\n\t/**\n\t * Updates internal data structures when the user's selection of location providers has changed.\n\t * @param providers The new set of location providers\n\t */\n\tpublic void onLocationProvidersChanged(Set<String> providers) {\n\t\tContext context = this.getContext();\n\t\tList<String> allProviders = mainActivity.locationManager.getAllProviders();\n\t\tArrayList<String> removedProviders = new ArrayList<String>();\n\n\t\tfor (String pr : providerLocations.keySet())\n\t\t\tif (!providers.contains(pr))\n\t\t\t\tremovedProviders.add(pr);\n\n\t\t// remove cached locations and invalidators for providers which are no longer selected\n\t\tfor (String pr: removedProviders) {\n\t\t\tproviderLocations.remove(pr);\n\t\t\tproviderInvalidators.remove(pr);\n\t\t}\n\n\t\t// ensure there is a cached location for each chosen provider (can be null)\n\t\tfor (String pr : providers) {\n\t\t\tif ((allProviders.indexOf(pr) >= 0) && !providerLocations.containsKey(pr)) {\n\t\t\t\tLocation location = new Location(\"\");\n\t\t\t\tproviderLocations.put(pr, location);\n\t\t\t}\n\t\t}\n\n\t\t// add overlays\n\t\tupdateLocationProviderStyles();\n\n\t\tmapCircles = new HashMap<String, Circle>();\n\t\tmapMarkers = new HashMap<String, Marker>();\n\n\t\tLog.d(TAG, \"Provider location cache: \" + providerLocations.keySet().toString());\n\n\t\tLayers layers = mapMap.getLayerManager().getLayers();\n\n\t\t// remove all layers other than tile render layer from map\n\t\tfor (Layer layer : layers)\n\t\t\tif (!(layer instanceof TileRendererLayer) && !(layer instanceof TileDownloadLayer)) {\n\t\t\t\tlayer.onDestroy();\n\t\t\t\tlayers.remove(layer);\n\t\t\t}\n\n\t\tfor (String pr : providers) {\n\t\t\t// no invalidator for GPS, which is invalidated through GPS status\n\t\t\tif ((!pr.equals(LocationManager.GPS_PROVIDER)) && (providerInvalidators.get(pr)) == null) {\n\t\t\t\tfinal String provider = pr;\n\t\t\t\tfinal Context ctx = context;\n\t\t\t\tproviderInvalidators.put(pr, new Runnable() {\n\t\t\t\t\tprivate String mProvider = provider;\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tLocation location = providerLocations.get(mProvider);\n\t\t\t\t\t\tif (location != null)\n\t\t\t\t\t\t\tmarkLocationAsStale(location);\n\t\t\t\t\t\tapplyLocationProviderStyle(ctx, mProvider, Const.LOCATION_PROVIDER_GRAY);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tString styleName = assignLocationProviderStyle(pr);\n\t\t\tLatLong latLong;\n\t\t\tfloat acc;\n\t\t\tboolean visible;\n\t\t\tif ((providerLocations.get(pr) != null) && (providerLocations.get(pr).getProvider() != \"\")) {\n\t\t\t\tlatLong = new LatLong(providerLocations.get(pr).getLatitude(),\n\t\t\t\t\t\tproviderLocations.get(pr).getLongitude());\n\t\t\t\tif (providerLocations.get(pr).hasAccuracy())\n\t\t\t\t\tacc = providerLocations.get(pr).getAccuracy();\n\t\t\t\telse\n\t\t\t\t\tacc = 0;\n\t\t\t\tvisible = true;\n\t\t\t\tif (isLocationStale(providerLocations.get(pr)))\n\t\t\t\t\tstyleName = Const.LOCATION_PROVIDER_GRAY;\n\t\t\t\tLog.d(\"MainActivity\", pr + \" has \" + latLong.toString());\n\t\t\t} else {\n\t\t\t\tlatLong = new LatLong(0, 0);\n\t\t\t\tacc = 0;\n\t\t\t\tvisible = false;\n\t\t\t\tLog.d(\"MainActivity\", pr + \" has no location, hiding\");\n\t\t\t}\n\n\t\t\t// Circle layer\n\t\t\tResources res = context.getResources();\n\t\t\tTypedArray style = res.obtainTypedArray(res.getIdentifier(styleName, \"array\", context.getPackageName()));\n\t\t\tPaint fill = AndroidGraphicFactory.INSTANCE.createPaint();\n\t\t\tfloat density = context.getResources().getDisplayMetrics().density;\n\t\t\tfill.setColor(style.getColor(Const.STYLE_FILL, R.color.circle_gray_fill));\n\t\t\tfill.setStyle(Style.FILL);\n\t\t\tPaint stroke = AndroidGraphicFactory.INSTANCE.createPaint();\n\t\t\tstroke.setColor(style.getColor(Const.STYLE_STROKE, R.color.circle_gray_stroke));\n\t\t\tstroke.setStrokeWidth(Math.max(1.5f * density, 1));\n\t\t\tstroke.setStyle(Style.STROKE);\n\t\t\tCircle circle = new Circle(latLong, acc, fill, stroke);\n\t\t\tmapCircles.put(pr, circle);\n\t\t\tlayers.add(circle);\n\t\t\tcircle.setVisible(visible);\n\n\t\t\t// Marker layer\n\t\t\tDrawable drawable = style.getDrawable(Const.STYLE_MARKER);\n\t\t\tBitmap bitmap = AndroidGraphicFactory.convertToBitmap(drawable);\n\t\t\tMarker marker = new Marker(latLong, bitmap, 0, -bitmap.getHeight() * 10 / 24);\n\t\t\tmapMarkers.put(pr, marker);\n\t\t\tlayers.add(marker);\n\t\t\tmarker.setVisible(visible);\n\t\t\tstyle.recycle();\n\t\t}\n\n\t\t// move layers into view\n\t\tupdateMap();\n\t}\n\t\n\t\n\t/**\n\t * Called when the source for the base map layer changes.\n\t * \n\t * This method destroys all tile layers and their associated tile caches while leaving overlays.\n\t */\n\tvoid onMapSourceChanged() {\n\t\tdestroyLayers(false);\n\t\tcreateLayers(false);\n\t}\n\t\n\n\t@Override\n\tpublic void onPause() {\n\t\tsuper.onPause();\n\t\tif (mapDownloadLayer != null)\n\t\t\tmapDownloadLayer.onPause();\n\t}\n\t\n\n\t@Override\n\tpublic void onResume() {\n\t\tsuper.onResume();\n\t\tif (mapDownloadLayer != null)\n\t\t\tmapDownloadLayer.onResume();\n\t}\n\t\n\n\t@Override\n\tpublic void onStop() {\n\t\tLatLong center = mapMap.getModel().mapViewPosition.getCenter();\n\t\tbyte zoom = mapMap.getModel().mapViewPosition.getZoomLevel();\n\n\t\tSharedPreferences.Editor spEditor = mainActivity.mSharedPreferences.edit();\n\t\tspEditor.putFloat(Const.KEY_PREF_MAP_LAT, (float) center.latitude);\n\t\tspEditor.putFloat(Const.KEY_PREF_MAP_LON, (float) center.longitude);\n\t\tspEditor.putInt(Const.KEY_PREF_MAP_ZOOM, zoom);\n\t\tspEditor.commit();\n\n\t\tsuper.onStop();\n\t}\n\n\n\t/**\n\t * Updates the list of styles to use for the location providers.\n\t * \n\t * This method updates the internal list of styles to use for displaying\n\t * locations on the map, assigning a style to each location provider.\n\t * Styles that are defined in {@link SharedPreferences} are preserved. If\n\t * none are defined, the GPS location provider is assigned the red style\n\t * and the network location provider is assigned the blue style. The\n\t * passive location provider is not assigned a style, as it does not send\n\t * any locations of its own. Other location providers are assigned one of\n\t * the following styles: green, orange, purple. If there are more location\n\t * providers than styles, the same style (including red and blue) can be\n\t * assigned to multiple providers. The mapping is written to \n\t * SharedPreferences so that it will be preserved even as available\n\t * location providers change.\n\t */\n\tpublic void updateLocationProviderStyles() {\n\t\t//FIXME: move code into assignLocationProviderStyle and use that\n\t\tList<String> allProviders = mainActivity.locationManager.getAllProviders();\n\t\tallProviders.remove(LocationManager.PASSIVE_PROVIDER);\n\t\tif (allProviders.contains(LocationManager.GPS_PROVIDER)) {\n\t\t\tproviderStyles.put(LocationManager.GPS_PROVIDER,\n\t\t\t\t\tmainActivity.mSharedPreferences.getString(Const.KEY_PREF_LOC_PROV_STYLE + LocationManager.GPS_PROVIDER, Const.LOCATION_PROVIDER_RED));\n\t\t\tmAvailableProviderStyles.remove(Const.LOCATION_PROVIDER_RED);\n\t\t\tallProviders.remove(LocationManager.GPS_PROVIDER);\n\t\t}\n\t\tif (allProviders.contains(LocationManager.NETWORK_PROVIDER)) {\n\t\t\tproviderStyles.put(LocationManager.NETWORK_PROVIDER,\n\t\t\t\t\tmainActivity.mSharedPreferences.getString(Const.KEY_PREF_LOC_PROV_STYLE + LocationManager.NETWORK_PROVIDER, Const.LOCATION_PROVIDER_BLUE));\n\t\t\tmAvailableProviderStyles.remove(Const.LOCATION_PROVIDER_BLUE);\n\t\t\tallProviders.remove(LocationManager.NETWORK_PROVIDER);\n\t\t}\n\t\tfor (String prov : allProviders) {\n\t\t\tif (mAvailableProviderStyles.isEmpty())\n\t\t\t\tmAvailableProviderStyles.addAll(Arrays.asList(Const.LOCATION_PROVIDER_STYLES));\n\t\t\tproviderStyles.put(prov,\n\t\t\t\t\tmainActivity.mSharedPreferences.getString(Const.KEY_PREF_LOC_PROV_STYLE + prov, mAvailableProviderStyles.get(0)));\n\t\t\tmAvailableProviderStyles.remove(providerStyles.get(prov));\n\t\t};\n\t\tSharedPreferences.Editor spEditor = mainActivity.mSharedPreferences.edit();\n\t\tfor (String prov : providerStyles.keySet())\n\t\t\tspEditor.putString(Const.KEY_PREF_LOC_PROV_STYLE + prov, providerStyles.get(prov));\n\t\tspEditor.commit();\n\t}\n\n\n\t/**\n\t * Updates the map view so that all markers are visible.\n\t */\n\tpublic void updateMap() {\n\t\tboolean needsRedraw = false;\n\t\tDimension dimension = mapMap.getModel().mapViewDimension.getDimension();\n\t\t// just trigger a redraw if we're not going to pan or zoom\n\t\tif ((dimension == null) || (!isMapViewAttached)) {\n\t\t\tmapMap.getLayerManager().redrawLayers();\n\t\t\treturn;\n\t\t}\n\t\t// move locations into view and zoom out as needed\n\t\tint tileSize = mapMap.getModel().displayModel.getTileSize();\n\t\tBoundingBox bb = null;\n\t\tBoundingBox bb2 = null;\n\t\tfor (Location l : providerLocations.values())\n\t\t\tif ((l != null) && (l.getProvider() != \"\")) {\n\t\t\t\tdouble lat = l.getLatitude();\n\t\t\t\tdouble lon = l.getLongitude();\n\t\t\t\tdouble yRadius = l.hasAccuracy()?((l.getAccuracy() * 360.0f) / Const.EARTH_CIRCUMFERENCE):0;\n\t\t\t\tdouble xRadius = l.hasAccuracy()?(yRadius * Math.abs(Math.cos(lat))):0;\n\n\t\t\t\tdouble minLon = Math.max(lon - xRadius, -180);\n\t\t\t\tdouble maxLon = Math.min(lon + xRadius, 180);\n\t\t\t\tdouble minLat = Math.max(lat - yRadius, -90);\n\t\t\t\tdouble maxLat = Math.min(lat + yRadius, 90);\n\n\t\t\t\tif (!isLocationStale(l)) {\n\t\t\t\t\t// location is up to date, add to main BoundingBox\n\t\t\t\t\tif (bb != null) {\n\t\t\t\t\t\tminLat = Math.min(bb.minLatitude, minLat);\n\t\t\t\t\t\tmaxLat = Math.max(bb.maxLatitude, maxLat);\n\t\t\t\t\t\tminLon = Math.min(bb.minLongitude, minLon);\n\t\t\t\t\t\tmaxLon = Math.max(bb.maxLongitude, maxLon);\n\t\t\t\t\t}\n\t\t\t\t\tbb = new BoundingBox(minLat, minLon, maxLat, maxLon);\n\t\t\t\t} else {\n\t\t\t\t\t// location is stale, add to stale BoundingBox\n\t\t\t\t\tif (bb2 != null) {\n\t\t\t\t\t\tminLat = Math.min(bb2.minLatitude, minLat);\n\t\t\t\t\t\tmaxLat = Math.max(bb2.maxLatitude, maxLat);\n\t\t\t\t\t\tminLon = Math.min(bb2.minLongitude, minLon);\n\t\t\t\t\t\tmaxLon = Math.max(bb2.maxLongitude, maxLon);\n\t\t\t\t\t}\n\t\t\t\t\tbb2 = new BoundingBox(minLat, minLon, maxLat, maxLon);\n\t\t\t\t}\n\t\t\t}\n\t\tif (bb == null) bb = bb2; // all locations are stale, center to them\n\t\tif (bb == null) {\n\t\t\tneedsRedraw = true;\n\t\t} else {\n\t\t\tbyte newZoom = LatLongUtils.zoomForBounds(dimension, bb, tileSize);\n\t\t\tif (newZoom < 0)\n\t\t\t\tnewZoom = 0;\n\t\t\tif (newZoom < mapMap.getModel().mapViewPosition.getZoomLevel()) {\n\t\t\t\tmapMap.getModel().mapViewPosition.setZoomLevel(newZoom);\n\t\t\t} else {\n\t\t\t\tneedsRedraw = true;\n\t\t\t}\n\n\t\t\tMapViewProjection proj = new MapViewProjection(mapMap);\n\t\t\tPoint nw = proj.toPixels(new LatLong(bb.maxLatitude, bb.minLongitude));\n\t\t\tPoint se = proj.toPixels(new LatLong(bb.minLatitude, bb.maxLongitude));\n\n\t\t\t// move only if bb is not entirely visible\n\t\t\tif ((nw.x < 0) || (nw.y < 0) || (se.x > dimension.width) || (se.y > dimension.height)) {\n\t\t\t\tmapMap.getModel().mapViewPosition.setCenter(bb.getCenterPoint());\n\t\t\t} else {\n\t\t\t\tneedsRedraw = true;\n\t\t\t}\n\t\t}\n\t\tif (needsRedraw)\n\t\t\tmapMap.getLayerManager().redrawLayers();\n\t}\n}"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/ui/RadioSectionFragment.java",
    "content": "/*\n * Copyright © 2013–2016 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat.ui;\n\nimport static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;\nimport static android.telephony.TelephonyManager.PHONE_TYPE_GSM;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\n\nimport com.vonglasow.michael.satstat.Const;\nimport com.vonglasow.michael.satstat.R;\nimport com.vonglasow.michael.satstat.data.CellTower;\nimport com.vonglasow.michael.satstat.data.CellTowerCdma;\nimport com.vonglasow.michael.satstat.data.CellTowerGsm;\nimport com.vonglasow.michael.satstat.data.CellTowerListCdma;\nimport com.vonglasow.michael.satstat.data.CellTowerListGsm;\nimport com.vonglasow.michael.satstat.data.CellTowerListLte;\nimport com.vonglasow.michael.satstat.data.CellTowerLte;\nimport com.vonglasow.michael.satstat.utils.WifiCapabilities;\nimport com.vonglasow.michael.satstat.utils.WifiScanResultComparator;\n\nimport android.Manifest;\nimport android.annotation.SuppressLint;\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.content.pm.PackageManager;\nimport android.net.ConnectivityManager;\nimport android.net.NetworkInfo;\nimport android.net.wifi.ScanResult;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.support.v4.app.ActivityCompat;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.content.ContextCompat;\nimport android.telephony.CellInfo;\nimport android.telephony.CellLocation;\nimport android.telephony.NeighboringCellInfo;\nimport android.telephony.PhoneStateListener;\nimport android.telephony.SignalStrength;\nimport android.telephony.cdma.CdmaCellLocation;\nimport android.telephony.gsm.GsmCellLocation;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewGroup.LayoutParams;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.TableLayout;\nimport android.widget.TableRow;\nimport android.widget.TextView;\nimport android.widget.ImageView.ScaleType;\n\n/**\n * The fragment which displays radio network data.\n */\npublic class RadioSectionFragment extends Fragment {\n\tpublic static final String TAG = \"RadioSectionFragment\";\n\t/**\n\t * The fragment argument representing the section number for this\n\t * fragment.\n\t */\n\tpublic static final String ARG_SECTION_NUMBER = \"section_number\";\n\tprivate static final int NETWORK_REFRESH_DELAY = 1000; //the polling interval for the network type\n\tprivate static final int WIFI_REFRESH_DELAY = 1000; //the time between two requests for WLAN rescan.\n\n\tprivate MainActivity mainActivity = null;\n\n\tprivate CellTower mServingCell;\n\tprivate CellTowerListGsm mCellsGsm = new CellTowerListGsm();\n\tprivate CellTowerListCdma mCellsCdma = new CellTowerListCdma();\n\tprivate CellTowerListLte mCellsLte = new CellTowerListLte();\n\tint mLastNetworkGen = 0; //the last observed (and displayed) network type\n\tprivate int mLastCellAsu = NeighboringCellInfo.UNKNOWN_RSSI;\n\tprivate int mLastCellDbm = CellTower.DBM_UNKNOWN;\n\tprivate Handler networkTimehandler = null;\n\tprivate Runnable networkTimeRunnable = null;\n\n\tList <ScanResult> scanResults = null;\n\tWifiScanResultComparator wifiComparator;\n\tprivate String selectedBSSID = \"\";\n\tprivate Handler wifiTimehandler = null;\n\tprivate Runnable wifiTimeRunnable = null;\n\n\n\tprivate LinearLayout rilGsmLayout;\n\tprivate TableLayout rilCells;\n\tprivate LinearLayout rilCdmaLayout;\n\tprivate TableLayout rilCdmaCells;\n\tprivate LinearLayout rilLteLayout;\n\tprivate TableLayout rilLteCells;\n\tprivate LinearLayout wifiAps;\n\n\n\t@SuppressLint(\"UseSparseArrays\")\n\tprivate final static HashMap<Integer, Integer> channelsFrequency = new HashMap<Integer, Integer>() {\n\t\t/*\n\t\t * Required for serializable objects\n\t\t */\n\t\tprivate static final long serialVersionUID = 6793015643527778045L;\n\n\t\t{\n\t\t\t// 2.4 GHz (802.11 b/g/n)\n\t\t\tthis.put(2412, 1);\n\t\t\tthis.put(2417, 2);\n\t\t\tthis.put(2422, 3);\n\t\t\tthis.put(2427, 4);\n\t\t\tthis.put(2432, 5);\n\t\t\tthis.put(2437, 6);\n\t\t\tthis.put(2442, 7);\n\t\t\tthis.put(2447, 8);\n\t\t\tthis.put(2452, 9);\n\t\t\tthis.put(2457, 10);\n\t\t\tthis.put(2462, 11);\n\t\t\tthis.put(2467, 12);\n\t\t\tthis.put(2472, 13);\n\t\t\tthis.put(2484, 14);\n\n\t\t\t//5 GHz (802.11 a/h/j/n/ac)\n\t\t\tthis.put(4915, 183);\n\t\t\tthis.put(4920, 184);\n\t\t\tthis.put(4925, 185);\n\t\t\tthis.put(4935, 187);\n\t\t\tthis.put(4940, 188);\n\t\t\tthis.put(4945, 189);\n\t\t\tthis.put(4960, 192);\n\t\t\tthis.put(4980, 196);\n\n\t\t\tthis.put(5035, 7);\n\t\t\tthis.put(5040, 8);\n\t\t\tthis.put(5045, 9);\n\t\t\tthis.put(5055, 11);\n\t\t\tthis.put(5060, 12);\n\t\t\tthis.put(5080, 16);\n\n\t\t\tthis.put(5170, 34);\n\t\t\tthis.put(5180, 36);\n\t\t\tthis.put(5190, 38);\n\t\t\tthis.put(5200, 40);\n\t\t\tthis.put(5210, 42);\n\t\t\tthis.put(5220, 44);\n\t\t\tthis.put(5230, 46);\n\t\t\tthis.put(5240, 48);\n\t\t\tthis.put(5260, 52);\n\t\t\tthis.put(5280, 56);\n\t\t\tthis.put(5300, 60);\n\t\t\tthis.put(5320, 64);\n\n\t\t\tthis.put(5500, 100);\n\t\t\tthis.put(5520, 104);\n\t\t\tthis.put(5540, 108);\n\t\t\tthis.put(5560, 112);\n\t\t\tthis.put(5580, 116);\n\t\t\tthis.put(5600, 120);\n\t\t\tthis.put(5620, 124);\n\t\t\tthis.put(5640, 128);\n\t\t\tthis.put(5660, 132);\n\t\t\tthis.put(5680, 136);\n\t\t\tthis.put(5700, 140);\n\t\t\tthis.put(5745, 149);\n\t\t\tthis.put(5765, 153);\n\t\t\tthis.put(5785, 157);\n\t\t\tthis.put(5805, 161);\n\t\t\tthis.put(5825, 165);\n\t\t}\n\t};\n\n\n\tpublic RadioSectionFragment() {\n\t}\n\n\n\tprivate final void addWifiResult(ScanResult result) {\n\t\t// needed to pass a persistent reference to the OnClickListener\n\t\tfinal ScanResult r = result;\n\t\tandroid.view.View.OnClickListener clis = new android.view.View.OnClickListener () {\n\n\t\t\t@Override\n\t\t\tpublic void onClick(View v) {\n\t\t\t\tonWifiEntryClick(r.BSSID);\n\t\t\t}\n\t\t};\n\n\t\tLinearLayout wifiLayout = new LinearLayout(wifiAps.getContext());\n\t\twifiLayout.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));\n\t\twifiLayout.setOrientation(LinearLayout.HORIZONTAL);\n\t\twifiLayout.setWeightSum(22);\n\t\twifiLayout.setMeasureWithLargestChildEnabled(false);\n\n\t\tImageView wifiType = new ImageView(wifiAps.getContext());\n\t\twifiType.setLayoutParams(new TableRow.LayoutParams(0, LayoutParams.MATCH_PARENT, 3));\n\t\tif (WifiCapabilities.isAdhoc(result)) {\n\t\t\twifiType.setImageResource(R.drawable.ic_content_wifi_adhoc);\n\t\t} else if ((WifiCapabilities.isEnterprise(result)) || (WifiCapabilities.getScanResultSecurity(result) == WifiCapabilities.EAP)) {\n\t\t\twifiType.setImageResource(R.drawable.ic_content_wifi_eap);\n\t\t} else if (WifiCapabilities.getScanResultSecurity(result) == WifiCapabilities.PSK) {\n\t\t\twifiType.setImageResource(R.drawable.ic_content_wifi_psk);\n\t\t} else if (WifiCapabilities.getScanResultSecurity(result) == WifiCapabilities.WEP) {\n\t\t\twifiType.setImageResource(R.drawable.ic_content_wifi_wep);\n\t\t} else if (WifiCapabilities.getScanResultSecurity(result) == WifiCapabilities.OPEN) {\n\t\t\twifiType.setImageResource(R.drawable.ic_content_wifi_open);\n\t\t} else {\n\t\t\twifiType.setImageResource(R.drawable.ic_content_wifi_unknown);\n\t\t}\n\n\t\twifiType.setScaleType(ScaleType.CENTER);\n\t\twifiLayout.addView(wifiType);\n\n\t\tTableLayout wifiDetails = new TableLayout(wifiAps.getContext());\n\t\twifiDetails.setLayoutParams(new TableRow.LayoutParams(0, LayoutParams.WRAP_CONTENT, 19));\n\t\tTableRow innerRow1 = new TableRow(wifiAps.getContext());\n\t\tTextView newMac = new TextView(wifiAps.getContext());\n\t\tnewMac.setLayoutParams(new TableRow.LayoutParams(0, LayoutParams.WRAP_CONTENT, 14));\n\t\tnewMac.setTextAppearance(wifiAps.getContext(), android.R.style.TextAppearance_Medium);\n\t\tnewMac.setText(result.BSSID);\n\t\tinnerRow1.addView(newMac);\n\t\tTextView newCh = new TextView(wifiAps.getContext());\n\t\tnewCh.setLayoutParams(new TableRow.LayoutParams(0, LayoutParams.WRAP_CONTENT, 2));\n\t\tnewCh.setTextAppearance(wifiAps.getContext(), android.R.style.TextAppearance_Medium);\n\t\tnewCh.setText(getChannelFromFrequency(result.frequency));\n\t\tinnerRow1.addView(newCh);\n\t\tTextView newLevel = new TextView(wifiAps.getContext());\n\t\tnewLevel.setLayoutParams(new TableRow.LayoutParams(0, LayoutParams.WRAP_CONTENT, 3));\n\t\tnewLevel.setTextAppearance(wifiAps.getContext(), android.R.style.TextAppearance_Medium);\n\t\tnewLevel.setText(String.valueOf(result.level));\n\t\tinnerRow1.addView(newLevel);\n\t\tinnerRow1.setOnClickListener(clis);\n\t\twifiDetails.addView(innerRow1,new TableLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));\n\n\t\tTableRow innerRow2 = new TableRow(wifiAps.getContext());\n\t\tTextView newSSID = new TextView(wifiAps.getContext());\n\t\tnewSSID.setLayoutParams(new TableRow.LayoutParams(0, LayoutParams.WRAP_CONTENT, 19));\n\t\tnewSSID.setTextAppearance(wifiAps.getContext(), android.R.style.TextAppearance_Small);\n\t\tnewSSID.setText(result.SSID);\n\t\tinnerRow2.addView(newSSID);\n\t\tinnerRow2.setOnClickListener(clis);\n\t\twifiDetails.addView(innerRow2, new TableLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));\n\n\t\twifiLayout.addView(wifiDetails);\n\t\twifiLayout.setOnClickListener(clis);\n\t\twifiAps.addView(wifiLayout);\n\t}\n\n\n\t/**\n\t * Formats an item of cell information data for display.\n\t * <p>\n\t * This helper function formats any item of cell information data, such as\n\t * the cell ID, PSC or similar. For valid data a string with the properly\n\t * formatted value will be returned. If the input value is\n\t * {@link com.vonglasow.michael.satstat.data.CellTower#UNKNOWN}, then the\n\t * {@code value_none} resource string will be returned. \n\t * @param context the context of the caller\n\t * @param format a format string, which must contain placeholders for exactly one variable, or {@code null}.\n\t * @param raw the value to format\n\t * @return\n\t */\n\tpublic static String formatCellData(Context context, String format, int raw) {\n\t\tif (raw == CellTower.UNKNOWN)\n\t\t\treturn context.getResources().getString(R.string.value_none);\n\t\telse {\n\t\t\tString fmt = (format != null) ? format : \"%d\";\n\t\t\treturn String.format(fmt, raw);\n\t\t}\n\t}\n\n\n\t/**\n\t * Formats cell signal strength for display.\n\t * <p>\n\t * This helper function formats the signal strength for a cell. For valid\n\t * data a string with the properly formatted value will be returned. If the\n\t * input value is\n\t * {@link com.vonglasow.michael.satstat.data.CellTower#DBM_UNKNOWN}, then\n\t * the {@code value_none} resource string will be returned.\n\t * @param context the context of the caller\n\t * @param format a format string, which must contain placeholders for exactly one variable, or {@code null}.\n\t * @param raw the signal strength in dBm\n\t * @return\n\t */\n\tpublic static String formatCellDbm(Context context, String format, int raw) {\n\t\tif (raw == CellTower.DBM_UNKNOWN)\n\t\t\treturn context.getResources().getString(R.string.value_none);\n\t\telse {\n\t\t\tString fmt = (format != null) ? format : \"%d\";\n\t\t\treturn String.format(fmt, raw);\n\t\t}\n\t}\n\n\n\t/**\n\t * Gets the WiFi channel number for a frequency\n\t * @param frequency The frequency in MHz\n\t * @return The channel number corresponding to {@code frequency}\n\t */\n\tpublic static String getChannelFromFrequency(int frequency) {\n\t\tif (channelsFrequency.containsKey(frequency)) {\n\t\t\treturn String.valueOf(channelsFrequency.get(frequency));\n\t\t}\n\t\telse {\n\t\t\treturn \"?\";\n\t\t}\n\t}\n\n\n\t/**\n\t * Gets the display color for a phone network generation.\n\t * @param generation The network generation, i.e. {@code 2}, {@code 3} or {@code 4} for any flavor of 2G, 3G or 4G, or {@code 0} for unknown\n\t * @return The color in which to display the indicator. If {@code generation} is {@code 0} or not a valid generation, the color returned will be transparent.\n\t */\n\tpublic static int getColorFromGeneration(int generation) {\n\t\tswitch (generation) {\n\t\tcase 2:\n\t\t\treturn(R.color.gen2);\n\t\tcase 3:\n\t\t\treturn(R.color.gen3);\n\t\tcase 4:\n\t\t\treturn(R.color.gen4);\n\t\tdefault:\n\t\t\treturn(android.R.color.transparent);\n\t\t}\n\t}\n\n\n\t@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\n\t@Override\n\tpublic View onCreateView(LayoutInflater inflater, ViewGroup container,\n\t\t\tBundle savedInstanceState) {\n\t\tmainActivity = (MainActivity) this.getContext();\n\t\tView rootView = inflater.inflate(R.layout.fragment_main_radio, container, false);\n\n\t\t// Initialize controls\n\t\trilGsmLayout = (LinearLayout) rootView.findViewById(R.id.rilGsmLayout);\n\t\trilCells = (TableLayout) rootView.findViewById(R.id.rilCells);\n\n\t\trilCdmaLayout = (LinearLayout) rootView.findViewById(R.id.rilCdmaLayout);\n\t\trilCdmaCells = (TableLayout) rootView.findViewById(R.id.rilCdmaCells);\n\n\t\trilLteLayout = (LinearLayout) rootView.findViewById(R.id.rilLteLayout);\n\t\trilLteCells = (TableLayout) rootView.findViewById(R.id.rilLteCells);\n\n\t\twifiAps = (LinearLayout) rootView.findViewById(R.id.wifiAps);\n\n\t\trilGsmLayout.setVisibility(View.GONE);\n\t\trilCdmaLayout.setVisibility(View.GONE);\n\t\trilLteLayout.setVisibility(View.GONE);\n\n\t\tnetworkTimehandler = new Handler();\n\t\tnetworkTimeRunnable = new Runnable() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tint newNetworkType = mainActivity.telephonyManager.getNetworkType();\n\t\t\t\tif (CellTower.getGenerationFromNetworkType(newNetworkType) != mLastNetworkGen)\n\t\t\t\t\tonNetworkTypeChanged(newNetworkType);\n\t\t\t\telse\n\t\t\t\t\tnetworkTimehandler.postDelayed(this, NETWORK_REFRESH_DELAY);\n\t\t\t}\n\t\t};\n\t\t\n\t\twifiComparator = new WifiScanResultComparator();\n\t\twifiComparator.setCriterion(mainActivity.prefWifiSort);\n\n\t\twifiTimehandler = new Handler();\n\t\twifiTimeRunnable = new Runnable() {\n\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tmainActivity.wifiManager.startScan();\n\t\t\t\twifiTimehandler.postDelayed(this, WIFI_REFRESH_DELAY);\n\t\t\t}\n\t\t};\n\n\t\t//get current phone info (first update won't fire until the cell actually changes)\n\t\tif (ContextCompat.checkSelfPermission(mainActivity, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)\n\t\t\tupdateCellData(null, null, null);\n\t\telse\n\t\t\tmainActivity.permsRequested[Const.PERM_REQUEST_CELL_INFO] = true;\n\t\t//and make sure we have the correct network type\n\t\tonNetworkTypeChanged(mainActivity.telephonyManager.getNetworkType());\n\n\t\tmainActivity.wifiManager.startScan();\n\n\t\tmainActivity.radioSectionFragment = this;\n\n\t\treturn rootView;\n\t}\n\n\n\t@Override\n\tpublic void onDestroyView() {\n\t\tsuper.onDestroyView();\n\t\tif (mainActivity.radioSectionFragment == this)\n\t\t\tmainActivity.radioSectionFragment = null;\n\t}\n\n\n\t/**\n\t * Updates the network type indicator for the current cell. Called by\n\t * {@link networkTimeRunnable.run()} or\n\t * {@link android.telephony.PhoneStateListener#onDataConnectionStateChanged(int, int)}.\n\t * \n\t * @param networkType One of the NETWORK_TYPE_xxxx constants defined in {@link android.telephony.TelephonyManager}\n\t */\n\tprotected void onNetworkTypeChanged(int networkType) {\n\t\tLog.d(\"MainActivity\", \"Network type changed to \" + Integer.toString(networkType));\n\t\tint newNetworkGen = CellTower.getGenerationFromNetworkType(networkType);\n\t\tint oldNetworkGen = mLastNetworkGen;\n\t\tif (newNetworkGen != mLastNetworkGen) {\n\t\t\tnetworkTimehandler.removeCallbacks(networkTimeRunnable);\n\t\t\tmLastNetworkGen = newNetworkGen;\n\t\t\t/*\n\t\t\t * Network type changes occur slightly before or after cell changes. Therefore, we may have\n\t\t\t * stored cells in the wrong list when switching from or to LTE.\n\t\t\t */\n\t\t\tif ((newNetworkGen == 4) || (oldNetworkGen == 4))\n\t\t\t\tupdateCellData(null, null, null);\n\t\t\telse if (mServingCell != null) {\n\t\t\t\tmServingCell.setNetworkType(networkType);\n\t\t\t\tLog.d(MainActivity.class.getSimpleName(), String.format(\"Setting network type to %d for cell %s (%s)\", mServingCell.getGeneration(), mServingCell.getText(), mServingCell.getAltText()));\n\t\t\t}\n\t\t}\n\t\tshowCells();\n\t}\n\n\n\t@Override\n\tpublic void onResume() {\n\t\tsuper.onResume();\n\t\twifiTimehandler.postDelayed(wifiTimeRunnable, WIFI_REFRESH_DELAY);\n\t}\n\n\n\t@Override\n\tpublic void onStop() {\n\t\tnetworkTimehandler.removeCallbacks(networkTimeRunnable);\n\t\twifiTimehandler.removeCallbacks(wifiTimeRunnable);\n\t\t// we'll just skip that so locations will get invalidated in any case\n\t\t//providerInvalidationHandler.removeCallbacksAndMessages(null);\n\t\tsuper.onStop();\n\t}\n\n\n\tprivate final void onWifiEntryClick(String BSSID) {\n\t\tselectedBSSID = BSSID;\n\t\trefreshWifiResults();\n\t}\n\n\n\tfinal void refreshWifiResults() {\n\t\tif (scanResults != null) {\n\t\t\twifiAps.removeAllViews();\n\t\t\twifiComparator.setCriterion(mainActivity.prefWifiSort);\n\t\t\tCollections.sort(scanResults, wifiComparator);\n\t\t\t//add the selected network first\n\t\t\tfor (ScanResult result : scanResults) {\n\t\t\t\tif (result.BSSID.equals(selectedBSSID)) {\n\t\t\t\t\taddWifiResult(result);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (ScanResult result : scanResults) {\n\t\t\t\tif (!result.BSSID.equals(selectedBSSID)) {\n\t\t\t\t\taddWifiResult(result);\t\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n\t/**\n\t * Updates the list of cells in range.\n\t * <p>\n\t * This method is automatically called by\n\t * {@link PhoneStateListener#onCellInfoChanged(List)}\n\t * and {@link PhoneStateListener.onCellLocationChanged}. It must be called\n\t * manually whenever {@link #mCellsCdma}, {@link #mCellsGsm}, \n\t * {@link #mCellsLte} or one of their values are modified, typically after\n\t * calling {@link android.telephony.TelephonyManager#getAllCellInfo()},\n\t * {@link android.telephony.TelephonyManager#getCellLocation()} or\n\t * {@link android.telephony.TelephonyManager#getNeighboringCellInfo()}. \n\t */\n\tprotected void showCells() {\n\t\tint cdmaVisibility = View.GONE;\n\t\tint gsmVisibility = View.GONE;\n\t\tint lteVisibility = View.GONE;\n\n\t\trilCells.removeAllViews();\n\t\tif (mCellsGsm.containsValue(mServingCell)) {\n\t\t\tshowCellGsm((CellTowerGsm) mServingCell);\n\t\t\tgsmVisibility = View.VISIBLE;\n\t\t}\n\t\tfor (CellTowerGsm cell : mCellsGsm.getAll())\n\t\t\tif (cell.hasSource() && (cell != mServingCell)) {\n\t\t\t\tshowCellGsm(cell);\n\t\t\t\tgsmVisibility = View.VISIBLE;\n\t\t\t}\n\t\trilGsmLayout.setVisibility(gsmVisibility);\n\n\t\trilCdmaCells.removeAllViews();\n\t\tif (mCellsCdma.containsValue(mServingCell)) {\n\t\t\tshowCellCdma((CellTowerCdma) mServingCell);\n\t\t\tcdmaVisibility = View.VISIBLE;\n\t\t}\n\t\tfor (CellTowerCdma cell : mCellsCdma.getAll())\n\t\t\tif (cell.hasSource() && (cell != mServingCell)) {\n\t\t\t\tshowCellCdma(cell);\n\t\t\t\tcdmaVisibility = View.VISIBLE;\n\t\t\t}\n\t\trilCdmaLayout.setVisibility(cdmaVisibility);\n\n\t\trilLteCells.removeAllViews();\n\t\tif (mCellsLte.containsValue(mServingCell)) {\n\t\t\tshowCellLte((CellTowerLte) mServingCell);\n\t\t\tlteVisibility = View.VISIBLE;\n\t\t}\n\t\tfor (CellTowerLte cell : mCellsLte.getAll())\n\t\t\tif (cell.hasSource() && (cell != mServingCell)) {\n\t\t\t\tshowCellLte(cell);\n\t\t\t\tlteVisibility = View.VISIBLE;\n\t\t\t}\n\t\trilLteLayout.setVisibility(lteVisibility);\n\t}\n\n\n\tprotected void showCellCdma(CellTowerCdma cellTower) {\n\t\tTableRow row = (TableRow) mainActivity.getLayoutInflater().inflate(R.layout.ril_cdma_list_item, null);\n\t\tTextView type = (TextView) row.findViewById(R.id.type);\n\t\tTextView sid = (TextView) row.findViewById(R.id.sid);\n\t\tTextView nid = (TextView) row.findViewById(R.id.nid);\n\t\tTextView bsid = (TextView) row.findViewById(R.id.bsid);\n\t\tTextView dbm = (TextView) row.findViewById(R.id.dbm);\n\n\t\ttype.setTextColor(rilCdmaCells.getContext().getResources().getColor(getColorFromGeneration(cellTower.getGeneration())));\n\t\ttype.setText(rilCdmaCells.getContext().getResources().getString(R.string.smallDot));\n\n\t\tsid.setText(formatCellData(rilCdmaCells.getContext(), null, cellTower.getSid()));\n\n\t\tnid.setText(formatCellData(rilCdmaCells.getContext(), null, cellTower.getNid()));\n\n\t\tbsid.setText(formatCellData(rilCdmaCells.getContext(), null, cellTower.getBsid()));\n\n\t\tdbm.setText(formatCellDbm(rilCdmaCells.getContext(), null, cellTower.getDbm()));\n\n\t\trilCdmaCells.addView(row,new TableLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));\n\t}\n\n\n\tprotected void showCellGsm(CellTowerGsm cellTower) {\n\t\tTableRow row = (TableRow) mainActivity.getLayoutInflater().inflate(R.layout.ril_list_item, null);\n\t\tTextView type = (TextView) row.findViewById(R.id.type);\n\t\tTextView mcc = (TextView) row.findViewById(R.id.mcc);\n\t\tTextView mnc = (TextView) row.findViewById(R.id.mnc);\n\t\tTextView area = (TextView) row.findViewById(R.id.area);\n\t\tTextView cell = (TextView) row.findViewById(R.id.cell);\n\t\tTextView cell2 = (TextView) row.findViewById(R.id.cell2);\n\t\tTextView unit = (TextView) row.findViewById(R.id.unit);\n\t\tTextView dbm = (TextView) row.findViewById(R.id.dbm);\n\n\t\ttype.setTextColor(rilCells.getContext().getResources().getColor(getColorFromGeneration(cellTower.getGeneration())));\n\t\ttype.setText(rilCells.getContext().getResources().getString(R.string.smallDot));\n\n\t\tmcc.setText(formatCellData(rilCells.getContext(), \"%03d\", cellTower.getMcc()));\n\n\t\tmnc.setText(formatCellData(rilCells.getContext(), \"%02d\", cellTower.getMnc()));\n\n\t\tarea.setText(formatCellData(rilCells.getContext(), null, cellTower.getLac()));\n\n\t\tint rtcid = cellTower.getCid() / 0x10000;\n\t\tint cid = cellTower.getCid() % 0x10000;\n\t\tif ((mainActivity.prefCid) && (cellTower.getCid() != CellTower.UNKNOWN) && (cellTower.getCid() > 0x0ffff)) {\n\t\t\tcell.setText(String.format(\"%d-%d\", rtcid, cid));\n\t\t\tcell2.setText(formatCellData(rilCells.getContext(), null, cellTower.getCid()));\n\t\t} else {\n\t\t\tcell.setText(formatCellData(rilCells.getContext(), null, cellTower.getCid()));\n\t\t\tcell2.setText(String.format(\"%d-%d\", rtcid, cid));\n\t\t}\n\t\tcell2.setVisibility((mainActivity.prefCid2 && (cellTower.getCid() > 0x0ffff)) ? View.VISIBLE : View.GONE);\n\n\t\tunit.setText(formatCellData(rilCells.getContext(), null, cellTower.getPsc()));\n\n\t\tdbm.setText(formatCellDbm(rilCells.getContext(), null, cellTower.getDbm()));\n\n\t\trilCells.addView(row,new TableLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));\n\t}\n\n\n\tprotected void showCellLte(CellTowerLte cellTower) {\n\t\tTableRow row = (TableRow) mainActivity.getLayoutInflater().inflate(R.layout.ril_list_item, null);\n\t\tTextView type = (TextView) row.findViewById(R.id.type);\n\t\tTextView mcc = (TextView) row.findViewById(R.id.mcc);\n\t\tTextView mnc = (TextView) row.findViewById(R.id.mnc);\n\t\tTextView area = (TextView) row.findViewById(R.id.area);\n\t\tTextView cell = (TextView) row.findViewById(R.id.cell);\n\t\tTextView cell2 = (TextView) row.findViewById(R.id.cell2);\n\t\tTextView unit = (TextView) row.findViewById(R.id.unit);\n\t\tTextView dbm = (TextView) row.findViewById(R.id.dbm);\n\n\t\ttype.setTextColor(rilLteCells.getContext().getResources().getColor(getColorFromGeneration(cellTower.getGeneration())));\n\t\ttype.setText(rilLteCells.getContext().getResources().getString(R.string.smallDot));\n\n\t\tmcc.setText(formatCellData(rilLteCells.getContext(), \"%03d\", cellTower.getMcc()));\n\n\t\tmnc.setText(formatCellData(rilLteCells.getContext(), \"%02d\", cellTower.getMnc()));\n\n\t\tarea.setText(formatCellData(rilLteCells.getContext(), null, cellTower.getTac()));\n\n\t\tint eNodeBId = cellTower.getCi() / 0x100;\n\t\tint sectorId = cellTower.getCi() % 0x100;\n\t\tif ((mainActivity.prefCid) && (cellTower.getCi() != CellTower.UNKNOWN)) {\n\t\t\tcell.setText(String.format(\"%d-%d\", eNodeBId, sectorId));\n\t\t\tcell2.setText(formatCellData(rilLteCells.getContext(), null, cellTower.getCi()));\n\t\t} else {\n\t\t\tcell.setText(formatCellData(rilLteCells.getContext(), null, cellTower.getCi()));\n\t\t\tcell2.setText(String.format(\"%d-%d\", eNodeBId, sectorId));\n\t\t}\n\t\tcell2.setVisibility(mainActivity.prefCid2 ? View.VISIBLE : View.GONE);\n\n\t\tunit.setText(formatCellData(rilLteCells.getContext(), null, cellTower.getPci()));\n\n\t\tdbm.setText(formatCellDbm(rilLteCells.getContext(), null, cellTower.getDbm()));\n\n\t\trilLteCells.addView(row,new TableLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));\n\t}\n\n\n\t/**\n\t * Updates all cell data.\n\t * \n\t * This method is called whenever any change in the cell environment (cells in view or signal\n\t * strengths) is signaled, e.g. by a call to a {@link android.telephony.PhoneStateListener}. The\n\t * arguments of this method should be filled with the data passed to the\n\t * {@link android.telephony.PhoneStateListener} where possible, and null passed for all others.\n\t * \n\t * To force an update of all cell data, simply call this method with each argument set to null.\n\t * \n\t * If any of the arguments is null, this method will try to obtain that data by querying\n\t * {@link android.telephony.TelephonyManager}. The only exception is {@code signalStrength}, which\n\t * will not be explicitly queried if missing.\n\t * \n\t * It will first process {@code aCellInfo}, then {@code aLocation}, querying current values from\n\t * {@link android.telephony.TelephonyManager} if one of these arguments is null. Next it will process\n\t * {@code signalStrength}, if supplied, and eventually obtain neighboring cells by calling\n\t * {@link android.telephony.TelephonyManager#getNeighboringCellInfo()} and process these. Eventually\n\t * it will refresh the list of cells.\n\t * \n\t * @param aLocation The {@link android.telephony.CellLocation} reported by a\n\t * {@link android.telephony.PhoneStateListener}. If null, the current value will be queried.\n\t * @param aSignalStrength The {@link android.telephony.SignalStrength} reported by a\n\t * {@link android.telephony.PhoneStateListener}. If null, the signal strength of the serving cell\n\t * will either be taken from {@code aCellInfo}, if available, or not be updated at all.\n\t * @param aCellInfo A list of {@link android.telephony.CellInfo} instances reported by a\n\t * {@link android.telephony.PhoneStateListener}. If null, the current value will be queried.\n\t */\n\t@SuppressLint(\"NewApi\")\n\tpublic void updateCellData(CellLocation aLocation, SignalStrength signalStrength, List<CellInfo> aCellInfo) {\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n\t\t\ttry {\n\t\t\t\t/*\n\t\t\t\t * CellInfo requires API 17+ and should in theory return all cells in view. In practice,\n\t\t\t\t * some devices do not implement it or return only a partial list. On some devices,\n\t\t\t\t * PhoneStateListener#onCellInfoChanged() will fire but always receive a null argument.\n\t\t\t\t */\n\t\t\t\tList<CellInfo> cellInfo = (aCellInfo != null) ? aCellInfo : mainActivity.telephonyManager.getAllCellInfo();\n\t\t\t\tmCellsGsm.updateAll(cellInfo);\n\t\t\t\tmCellsCdma.updateAll(cellInfo);\n\t\t\t\tmCellsLte.updateAll(cellInfo);\n\t\t\t} catch (SecurityException e) {\n\t\t\t\t// Permission not granted, can't retrieve cell data\n\t\t\t\tLog.w(TAG, \"Permission not granted, TelephonyManager#getAllCellInfo() failed\");\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\t/*\n\t\t\t * CellLocation should return the serving cell, unless it is LTE (in which case it should\n\t\t\t * return null). In practice, however, some devices do return LTE cells. The approach of\n\t\t\t * this method does not work well for devices with multiple radios.\n\t\t\t */\n\t\t\tCellLocation location = (aLocation != null) ? aLocation : mainActivity.telephonyManager.getCellLocation();\n\t\t\tString networkOperator = mainActivity.telephonyManager.getNetworkOperator();\n\t\t\tmCellsGsm.removeSource(CellTower.SOURCE_CELL_LOCATION);\n\t\t\tmCellsCdma.removeSource(CellTower.SOURCE_CELL_LOCATION);\n\t\t\tmCellsLte.removeSource(CellTower.SOURCE_CELL_LOCATION);\n\t\t\tif (location instanceof GsmCellLocation) {\n\t\t\t\tif (mLastNetworkGen < 4) {\n\t\t\t\t\tmServingCell = mCellsGsm.update(networkOperator, (GsmCellLocation) location);\n\t\t\t\t\tif ((mServingCell.getDbm() == CellTower.DBM_UNKNOWN) && (mServingCell instanceof CellTowerGsm))\n\t\t\t\t\t\t((CellTowerGsm) mServingCell).setAsu(mLastCellAsu);\n\t\t\t\t} else {\n\t\t\t\t\tmServingCell = mCellsLte.update(networkOperator, (GsmCellLocation) location);\n\t\t\t\t\tif (mServingCell.getDbm() == CellTower.DBM_UNKNOWN)\n\t\t\t\t\t\t((CellTowerLte) mServingCell).setAsu(mLastCellAsu);\n\t\t\t\t}\n\t\t\t} else if (location instanceof CdmaCellLocation) {\n\t\t\t\tmServingCell = mCellsCdma.update((CdmaCellLocation) location);\n\t\t\t\tif (mServingCell.getDbm() == CellTower.DBM_UNKNOWN)\n\t\t\t\t\t((CellTowerCdma) mServingCell).setDbm(mLastCellDbm);\n\t\t\t}\n\t\t\tnetworkTimehandler.removeCallbacks(networkTimeRunnable);\n\t\t} catch (SecurityException e) {\n\t\t\t// Permission not granted, can't retrieve cell data\n\t\t\tLog.w(TAG, \"Permission not granted, cannot retrieve cell location\");\n\t\t}\n\n\t\tif ((mServingCell == null) || (mServingCell.getGeneration() <= 0)) {\n\t\t\tif ((mLastNetworkGen != 0) && (mServingCell != null))\n\t\t\t\tmServingCell.setGeneration(mLastNetworkGen);\n\t\t\tNetworkInfo netinfo = mainActivity.connectivityManager.getActiveNetworkInfo();\n\t\t\tif ((netinfo == null)\n\t\t\t\t\t|| (netinfo.getType() < ConnectivityManager.TYPE_MOBILE_MMS)\n\t\t\t\t\t|| (netinfo.getType() > ConnectivityManager.TYPE_MOBILE_HIPRI)) {\n\t\t\t\tnetworkTimehandler.postDelayed(networkTimeRunnable, NETWORK_REFRESH_DELAY);\n\t\t\t}\n\t\t} else if (mServingCell != null) {\n\t\t\tmLastNetworkGen = mServingCell.getGeneration();\n\t\t}\n\n\t\tif ((signalStrength != null) && (mServingCell != null)) {\n\t\t\tint pt = mainActivity.telephonyManager.getPhoneType();\n\t\t\tif (pt == PHONE_TYPE_GSM) {\n\t\t\t\tmLastCellAsu = signalStrength.getGsmSignalStrength();\n\t\t\t\tupdateNeighboringCellInfo();\n\t\t\t\tif (mServingCell instanceof CellTowerGsm)\n\t\t\t\t\t((CellTowerGsm) mServingCell).setAsu(mLastCellAsu);\n\t\t\t\telse\n\t\t\t\t\tLog.w(MainActivity.class.getSimpleName(),\n\t\t\t\t\t\t\t\"Got SignalStrength for PHONE_TYPE_GSM but serving cell is not GSM\");\n\t\t\t} else if (pt == PHONE_TYPE_CDMA) {\n\t\t\t\tmLastCellDbm = signalStrength.getCdmaDbm();\n\t\t\t\tif ((mServingCell != null) && (mServingCell instanceof CellTowerCdma))\n\t\t\t\t\tmServingCell.setDbm(mLastCellDbm);\n\t\t\t\telse\n\t\t\t\t\tLog.w(MainActivity.class.getSimpleName(),\n\t\t\t\t\t\t\t\"Got SignalStrength for PHONE_TYPE_CDMA but serving cell is not CDMA\");\n\t\t\t} else\n\t\t\t\tLog.w(MainActivity.class.getSimpleName(),\n\t\t\t\t\t\tString.format(\"Got SignalStrength for unknown phone type (%d)\", pt));\n\t\t} else if (mServingCell == null) {\n\t\t\tLog.w(MainActivity.class.getSimpleName(),\n\t\t\t\t\t\"Got SignalStrength but serving cell is null\");\n\t\t}\n\n\t\tupdateNeighboringCellInfo();\n\n\t\tshowCells();\n\t}\n\n\n\t/**\n\t * Requeries neighboring cells\n\t */\n\tprotected void updateNeighboringCellInfo() {\n\t\ttry {\n\t\t\t/*\n\t\t\t * NeighboringCellInfo is not supported on some devices and will return no data. It lists\n\t\t\t * only GSM and successors' cells, but not CDMA cells.\n\t\t\t */\n\t\t\tList<NeighboringCellInfo> neighboringCells = mainActivity.telephonyManager.getNeighboringCellInfo();\n\t\t\tString networkOperator = mainActivity.telephonyManager.getNetworkOperator();\n\t\t\tmCellsGsm.updateAll(networkOperator, neighboringCells);\n\t\t\tmCellsLte.updateAll(networkOperator, neighboringCells);\n\t\t} catch (SecurityException e) {\n\t\t\t// Permission not granted, can't retrieve cell data\n\t\t\tLog.w(TAG, \"Permission not granted, cannot get neighboring cell info\");\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/ui/SensorSectionFragment.java",
    "content": "/*\n * Copyright © 2013–2016 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat.ui;\n\nimport static android.hardware.SensorManager.SENSOR_STATUS_ACCURACY_HIGH;\nimport static android.hardware.SensorManager.SENSOR_STATUS_ACCURACY_LOW;\nimport static android.hardware.SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM;\nimport static android.hardware.SensorManager.SENSOR_STATUS_UNRELIABLE;\n\nimport com.vonglasow.michael.satstat.R;\nimport android.hardware.Sensor;\nimport android.hardware.SensorEvent;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\n\n/**\n * The fragment which displays sensor data.\n */\npublic class SensorSectionFragment extends Fragment {\n\tpublic static final String TAG = \"SensorSectionFragment\";\n\t/**\n\t * The fragment argument representing the section number for this\n\t * fragment.\n\t */\n\tpublic static final String ARG_SECTION_NUMBER = \"section_number\";\n\n\tprivate MainActivity mainActivity = null;\n\n\t/*\n\t *  Maximum resolutions for sensors, expressed as number of decimals. These\n\t *  values were chosen based on screen real estate and significance. They\n\t *  may be lowered if actual precision is lower, but will not be increased\n\t *  even if sensors are capable of delivering higher precision.\n\t */\n\tprivate byte mAccSensorRes = 3;\n\tprivate byte mGyroSensorRes = 4;\n\tprivate byte mMagSensorRes = 2;\n\tprivate byte mLightSensorRes = 1;\n\tprivate byte mProximitySensorRes = 1;\n\tprivate byte mPressureSensorRes = 0;\n\tprivate byte mHumiditySensorRes = 0;\n\tprivate byte mTempSensorRes = 1;\n\n\tprivate TextView accStatus;\n\tprivate TextView accHeader;\n\tprivate TextView accTotal;\n\tprivate TextView accX;\n\tprivate TextView accY;\n\tprivate TextView accZ;\n\tprivate TextView rotStatus;\n\tprivate TextView rotHeader;\n\tprivate TextView rotTotal;\n\tprivate TextView rotX;\n\tprivate TextView rotY;\n\tprivate TextView rotZ;\n\tprivate TextView magStatus;\n\tprivate TextView magHeader;\n\tprivate TextView magTotal;\n\tprivate TextView magX;\n\tprivate TextView magY;\n\tprivate TextView magZ;\n\tprivate TextView orStatus;\n\tprivate TextView orHeader;\n\tprivate TextView orAzimuth;\n\tprivate TextView orAziText;\n\tprivate TextView orPitch;\n\tprivate TextView orRoll;\n\tprivate TextView miscHeader;\n\tprivate TextView tempStatus;\n\tprivate TextView tempHeader;\n\tprivate TextView metTemp;\n\tprivate TextView pressureStatus;\n\tprivate TextView pressureHeader;\n\tprivate TextView metPressure;\n\tprivate TextView humidStatus;\n\tprivate TextView humidHeader;\n\tprivate TextView metHumid;\n\tprivate TextView lightStatus;\n\tprivate TextView lightHeader;\n\tprivate TextView light;\n\tprivate TextView proximityStatus;\n\tprivate TextView proximityHeader;\n\tprivate TextView proximity;\n\n\n\tpublic SensorSectionFragment() {\n\t}\n\n\n\t/**\n\t * Converts an accuracy value into a color identifier.\n\t */\n\tpublic static int accuracyToColor(int accuracy) {\n\t\tswitch (accuracy) {\n\t\tcase SENSOR_STATUS_ACCURACY_HIGH:\n\t\t\treturn(R.color.accHigh);\n\t\tcase SENSOR_STATUS_ACCURACY_MEDIUM:\n\t\t\treturn(R.color.accMedium);\n\t\tcase SENSOR_STATUS_ACCURACY_LOW:\n\t\t\treturn(R.color.accLow);\n\t\tcase SENSOR_STATUS_UNRELIABLE:\n\t\t\treturn(R.color.accUnreliable);\n\t\tdefault:\n\t\t\treturn(android.R.color.background_dark);\n\t\t}\n\t}\n\n\n\t/**\n\t * Gets the number of decimal digits to show when displaying sensor values, based on sensor accuracy.\n\t * @param sensor The sensor\n\t * @param maxDecimals The maximum number of decimals to display, even if the sensor's accuracy is higher\n\t * @return\n\t */\n\tpublic static byte getSensorDecimals(Sensor sensor, byte maxDecimals) {\n\t\tif (sensor == null) return 0;\n\t\tfloat res = sensor.getResolution();\n\t\tif (res == 0) return maxDecimals;\n\t\treturn (byte) Math.min(maxDecimals,\n\t\t\t\t(sensor != null) ? (byte) Math.max(Math.ceil(\n\t\t\t\t\t\t(float) -Math.log10(sensor.getResolution())), 0) : 0);\n\t}\n\n\n\t@Override\n\tpublic View onCreateView(LayoutInflater inflater, ViewGroup container,\n\t\t\tBundle savedInstanceState) {\n\t\tmainActivity = (MainActivity) this.getContext();\n\t\tView rootView = inflater.inflate(R.layout.fragment_main_sensors, container, false);\n\n\t\tSensor mAccSensor = mainActivity.sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);\n\t\tSensor mGyroSensor = mainActivity.sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);\n\t\tSensor mMagSensor = mainActivity.sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);\n\t\tSensor mLightSensor = mainActivity.sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);\n\t\tSensor mProximitySensor = mainActivity.sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);\n\t\tSensor mPressureSensor = mainActivity.sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);\n\t\tSensor mHumiditySensor = mainActivity.sensorManager.getDefaultSensor(Sensor.TYPE_RELATIVE_HUMIDITY);\n\t\tSensor mTempSensor = mainActivity.sensorManager.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE);\n\n\t\tmAccSensorRes = getSensorDecimals(mAccSensor, mAccSensorRes);\n\t\tmGyroSensorRes = getSensorDecimals(mGyroSensor, mGyroSensorRes);\n\t\tmMagSensorRes = getSensorDecimals(mMagSensor, mMagSensorRes);\n\t\tmLightSensorRes = getSensorDecimals(mLightSensor, mLightSensorRes);\n\t\tmProximitySensorRes = getSensorDecimals(mProximitySensor, mProximitySensorRes);\n\t\tmPressureSensorRes = getSensorDecimals(mPressureSensor, mPressureSensorRes);\n\t\tmHumiditySensorRes = getSensorDecimals(mHumiditySensor, mHumiditySensorRes);\n\t\tmTempSensorRes = getSensorDecimals(mTempSensor, mTempSensorRes);\n\n\t\t// Initialize controls\n\t\taccStatus = (TextView) rootView.findViewById(R.id.accStatus);\n\t\taccHeader = (TextView) rootView.findViewById(R.id.accHeader);\n\t\taccX = (TextView) rootView.findViewById(R.id.accX);\n\t\taccY = (TextView) rootView.findViewById(R.id.accY);\n\t\taccZ = (TextView) rootView.findViewById(R.id.accZ);\n\t\taccTotal = (TextView) rootView.findViewById(R.id.accTotal);\n\t\trotStatus = (TextView) rootView.findViewById(R.id.rotStatus);\n\t\trotHeader = (TextView) rootView.findViewById(R.id.rotHeader);\n\t\trotX = (TextView) rootView.findViewById(R.id.rotX);\n\t\trotY = (TextView) rootView.findViewById(R.id.rotY);\n\t\trotZ = (TextView) rootView.findViewById(R.id.rotZ);\n\t\trotTotal = (TextView) rootView.findViewById(R.id.rotTotal);\n\t\tmagStatus = (TextView) rootView.findViewById(R.id.magStatus);\n\t\tmagHeader = (TextView) rootView.findViewById(R.id.magHeader);\n\t\tmagX = (TextView) rootView.findViewById(R.id.magX);\n\t\tmagY = (TextView) rootView.findViewById(R.id.magY);\n\t\tmagZ = (TextView) rootView.findViewById(R.id.magZ);\n\t\tmagTotal = (TextView) rootView.findViewById(R.id.magTotal);\n\t\torStatus = (TextView) rootView.findViewById(R.id.orStatus);\n\t\torHeader = (TextView) rootView.findViewById(R.id.orHeader);\n\t\torAzimuth = (TextView) rootView.findViewById(R.id.orAzimuth);\n\t\torAziText = (TextView) rootView.findViewById(R.id.orAziText);\n\t\torPitch = (TextView) rootView.findViewById(R.id.orPitch);\n\t\torRoll = (TextView) rootView.findViewById(R.id.orRoll);\n\t\tmiscHeader = (TextView) rootView.findViewById(R.id.miscHeader);\n\t\ttempStatus = (TextView) rootView.findViewById(R.id.tempStatus);\n\t\ttempHeader = (TextView) rootView.findViewById(R.id.tempHeader);\n\t\tmetTemp = (TextView) rootView.findViewById(R.id.metTemp);\n\t\tpressureStatus = (TextView) rootView.findViewById(R.id.pressureStatus);\n\t\tpressureHeader = (TextView) rootView.findViewById(R.id.pressureHeader);\n\t\tmetPressure = (TextView) rootView.findViewById(R.id.metPressure);\n\t\thumidStatus = (TextView) rootView.findViewById(R.id.humidStatus);\n\t\thumidHeader = (TextView) rootView.findViewById(R.id.humidHeader);\n\t\tmetHumid = (TextView) rootView.findViewById(R.id.metHumid);\n\t\tlightStatus = (TextView) rootView.findViewById(R.id.lightStatus);\n\t\tlightHeader = (TextView) rootView.findViewById(R.id.lightHeader);\n\t\tlight = (TextView) rootView.findViewById(R.id.light);\n\t\tproximityStatus = (TextView) rootView.findViewById(R.id.proximityStatus);\n\t\tproximityHeader = (TextView) rootView.findViewById(R.id.proximityHeader);\n\t\tproximity = (TextView) rootView.findViewById(R.id.proximity);\n\n\t\tmainActivity.sensorSectionFragment = this;\n\n\t\treturn rootView;\n\t}\n\n\n\t@Override\n\tpublic void onDestroyView() {\n\t\tsuper.onDestroyView();\n\t\tif (mainActivity.sensorSectionFragment == this)\n\t\t\tmainActivity.sensorSectionFragment = null;\n\t}\n\n\t/**\n\t * Called by {@link MainActivity} when a sensor's reading changes. Updates sensor display.\n\t */\n\tpublic void onSensorChanged(SensorEvent event) {\n\t\tswitch (event.sensor.getType()) {\n\t\tcase Sensor.TYPE_ACCELEROMETER:\n\t\t\taccX.setText(String.format(\"%.\" + mAccSensorRes + \"f\", event.values[0]));\n\t\t\taccY.setText(String.format(\"%.\" + mAccSensorRes + \"f\", event.values[1]));\n\t\t\taccZ.setText(String.format(\"%.\" + mAccSensorRes + \"f\", event.values[2]));\n\t\t\taccTotal.setText(String.format(\"%.\" + mAccSensorRes + \"f\", Math.sqrt(Math.pow(event.values[0], 2) + Math.pow(event.values[1], 2) + Math.pow(event.values[2], 2))));\n\t\t\taccStatus.setTextColor(getResources().getColor(accuracyToColor(event.accuracy)));\n\t\t\tbreak;\n\t\tcase Sensor.TYPE_ORIENTATION:\n\t\t\torAzimuth.setText(String.format(\"%.0f%s\", event.values[0], getString(R.string.unit_degree)));\n\t\t\torAziText.setText(MainActivity.formatOrientation(this.getContext(), event.values[0]));\n\t\t\torPitch.setText(String.format(\"%.0f%s\", event.values[1], getString(R.string.unit_degree)));\n\t\t\torRoll.setText(String.format(\"%.0f%s\", event.values[2], getString(R.string.unit_degree)));\n\t\t\torStatus.setTextColor(getResources().getColor(accuracyToColor(event.accuracy)));\n\t\t\tbreak;\n\t\tcase Sensor.TYPE_GYROSCOPE:\n\t\t\trotX.setText(String.format(\"%.\" + mGyroSensorRes + \"f\", event.values[0]));\n\t\t\trotY.setText(String.format(\"%.\" + mGyroSensorRes + \"f\", event.values[1]));\n\t\t\trotZ.setText(String.format(\"%.\" + mGyroSensorRes + \"f\", event.values[2]));\n\t\t\trotTotal.setText(String.format(\"%.\" + mGyroSensorRes + \"f\", Math.sqrt(Math.pow(event.values[0], 2) + Math.pow(event.values[1], 2) + Math.pow(event.values[2], 2))));\n\t\t\trotStatus.setTextColor(getResources().getColor(accuracyToColor(event.accuracy)));\n\t\t\tbreak;\n\t\tcase Sensor.TYPE_MAGNETIC_FIELD:\n\t\t\tmagX.setText(String.format(\"%.\" + mMagSensorRes + \"f\", event.values[0]));\n\t\t\tmagY.setText(String.format(\"%.\" + mMagSensorRes + \"f\", event.values[1]));\n\t\t\tmagZ.setText(String.format(\"%.\" + mMagSensorRes + \"f\", event.values[2]));\n\t\t\tmagTotal.setText(String.format(\"%.\" + mMagSensorRes + \"f\", Math.sqrt(Math.pow(event.values[0], 2) + Math.pow(event.values[1], 2) + Math.pow(event.values[2], 2))));\n\t\t\tmagStatus.setTextColor(getResources().getColor(accuracyToColor(event.accuracy)));\n\t\t\tbreak;\n\t\tcase Sensor.TYPE_LIGHT:\n\t\t\tlight.setText(String.format(\"%.\" + mLightSensorRes + \"f\", event.values[0]));\n\t\t\tlightStatus.setTextColor(getResources().getColor(accuracyToColor(event.accuracy)));\n\t\t\tbreak;\n\t\tcase Sensor.TYPE_PROXIMITY:\n\t\t\tproximity.setText(String.format(\"%.\" + mProximitySensorRes + \"f\", event.values[0]));\n\t\t\tproximityStatus.setTextColor(getResources().getColor(accuracyToColor(event.accuracy)));\n\t\t\tbreak;\n\t\tcase Sensor.TYPE_PRESSURE:\n\t\t\tmetPressure.setText(String.format(\"%.\" + mPressureSensorRes + \"f\", event.values[0]));\n\t\t\tpressureStatus.setTextColor(getResources().getColor(accuracyToColor(event.accuracy)));\n\t\t\tbreak;\n\t\tcase Sensor.TYPE_RELATIVE_HUMIDITY:\n\t\t\tmetHumid.setText(String.format(\"%.\" + mHumiditySensorRes + \"f\", event.values[0]));\n\t\t\thumidStatus.setTextColor(getResources().getColor(accuracyToColor(event.accuracy)));\n\t\t\tbreak;\n\t\tcase Sensor.TYPE_AMBIENT_TEMPERATURE:\n\t\t\tmetTemp.setText(String.format(\"%.\" + mTempSensorRes + \"f\", event.values[0]));\n\t\t\ttempStatus.setTextColor(getResources().getColor(accuracyToColor(event.accuracy)));\n\t\t\tbreak;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/ui/SettingsActivity.java",
    "content": "/*\n * Copyright © 2013–2016 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat.ui;\n\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.mapsforge.map.android.util.AndroidUtil;\nimport org.mapsforge.map.layer.cache.TileCache;\n\nimport com.vonglasow.michael.satstat.Const;\nimport com.vonglasow.michael.satstat.PasvLocListenerService;\nimport com.vonglasow.michael.satstat.R;\n\nimport android.Manifest;\nimport android.location.LocationManager;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.preference.ListPreference;\nimport android.preference.Preference;\nimport android.preference.Preference.OnPreferenceClickListener;\nimport android.preference.PreferenceFragment;\nimport android.preference.PreferenceManager;\nimport android.support.v4.app.ActivityCompat;\nimport android.support.v4.content.ContextCompat;\nimport android.support.v7.app.ActionBar;\nimport android.support.v7.app.AlertDialog;\nimport android.support.v7.app.AppCompatActivity;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.widget.EditText;\nimport android.widget.ImageButton;\nimport android.widget.Toast;\nimport android.annotation.SuppressLint;\nimport android.app.NotificationManager;\nimport android.content.ActivityNotFoundException;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.content.SharedPreferences.OnSharedPreferenceChangeListener;\nimport android.content.pm.PackageManager;\n\npublic class SettingsActivity extends AppCompatActivity implements OnPreferenceClickListener, OnSharedPreferenceChangeListener{\n\tpublic static final String TAG = SettingsActivity.class.getSimpleName();\n\n\tpublic static final int REQUEST_CODE_PICK_MAP_PATH = 1;\n\n\t/**\n\t * A string array that specifies the name of the intent to use, and the scheme to use with it\n\t * when setting the data for the intent.\n\t * \n\t * @author k9mail, mvglasow\n\t */\n\tprivate static final String[][] PICK_DIRECTORY_INTENTS = {\n\t\t{ Intent.ACTION_PICK, \"folder://\" },                      // CM File Manager, Blackmoon File Browser, possibly others\n\t\t{ \"org.openintents.action.PICK_DIRECTORY\", \"file://\" },   // OI File Manager, possibly others\n\t\t{ \"com.estrongs.action.PICK_DIRECTORY\", \"file://\" },      // ES File Explorer\n\t\t{ \"com.androidworkz.action.PICK_DIRECTORY\", \"file://\" }\n\t};\n\t\n\tprivate SharedPreferences mSharedPreferences;\n\tPreference prefMapPath;\n\tString prefMapPathValue = Const.MAP_PATH_DEFAULT;\n\tPreference prefMapDownload;\n\tPreference prefMapPurge;\n\n\t@SuppressLint(\"NewApi\")\n\t@Override\n\tprotected void onActivityResult(int requestCode, int resultCode, Intent data) {\n\t\tif (requestCode == REQUEST_CODE_PICK_MAP_PATH) {\n\t\t\tif (resultCode == RESULT_OK) {\n\t\t\t\tsetMapPath(data.getData().getPath());\n\t\t\t}\n\t\t}\n\t}\n\t\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.activity_settings);\n\n\t\t// Show the Up button in the action bar.\n\t\tActionBar actionBar = getSupportActionBar();\n\t\tactionBar.setDisplayHomeAsUpEnabled(true);\n\n\t\t// Display the fragment as the main content.\n\t\tgetFragmentManager().beginTransaction()\n\t\t.replace(android.R.id.content, new SettingsFragment())\n\t\t.commit();\n\n\t\tmSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);\n\n        // some logic to use the pre-1.7 setting KEY_PREF_UPDATE_WIFI as a\n\t\t// fallback if KEY_PREF_UPDATE_NETWORKS is not set\n\t\tif (!mSharedPreferences.contains(Const.KEY_PREF_UPDATE_NETWORKS)) {\n\t\t\tSet<String> fallbackUpdateNetworks = new HashSet<String>();\n\t\t\tif (mSharedPreferences.getBoolean(Const.KEY_PREF_UPDATE_WIFI, false)) {\n\t\t\t\tfallbackUpdateNetworks.add(Const.KEY_PREF_UPDATE_NETWORKS_WIFI);\n\t\t\t}\n\t\t\tSharedPreferences.Editor spEditor = mSharedPreferences.edit();\n\t\t\tspEditor.putStringSet(Const.KEY_PREF_UPDATE_NETWORKS, fallbackUpdateNetworks);\n\t\t\tspEditor.commit();\n\t\t}\n\t\t\n\t\t// by default, show GPS and network location in map\n\t\tif (!mSharedPreferences.contains(Const.KEY_PREF_LOC_PROV)) {\n\t\t\tSet<String> defaultLocProvs = new HashSet<String>(Arrays.asList(new String[] {LocationManager.GPS_PROVIDER, LocationManager.NETWORK_PROVIDER}));\n\t\t\tSharedPreferences.Editor spEditor = mSharedPreferences.edit();\n\t\t\tspEditor.putStringSet(Const.KEY_PREF_LOC_PROV, defaultLocProvs);\n\t\t\tspEditor.commit();\n\t\t}\n\t}\n\t\n\t@Override\n\tpublic boolean onOptionsItemSelected(MenuItem item) {\n\t\tswitch (item.getItemId()) {\n\t\tcase android.R.id.home:\n\t\t\tonBackPressed();\n\t\t\treturn true;\n\t\t}\n\n\t\treturn super.onOptionsItemSelected(item);\n\t}\n\n\n\t/**\n\t * @author k9mail, mvglasow\n\t */\n\t@Override\n\tpublic boolean onPreferenceClick(Preference preference) {\n\t\tif (preference == prefMapPath) {\n\t\t\tboolean success = false;\n\t\t\tint i = 0;\n\t\t\tdo {\n\t\t\t\tString intentAction = PICK_DIRECTORY_INTENTS[i][0];\n\t\t\t\tString uriPrefix = PICK_DIRECTORY_INTENTS[i][1];\n\t\t\t\tIntent intent = new Intent(intentAction);\n\t\t\t\tif (uriPrefix != null)\n\t\t\t\t\tintent.setData(Uri.parse(uriPrefix + prefMapPathValue));\n\n\t\t\t\ttry {\n\t\t\t\t\tstartActivityForResult(intent, REQUEST_CODE_PICK_MAP_PATH);\n\t\t\t\t\tLog.i(\"SettingsActivity\", String.format(\"Sending intent: %s\", intentAction));\n\t\t\t\t\tsuccess = true;\n\t\t\t\t} catch (ActivityNotFoundException e) {\n\t\t\t\t\t// Try the next intent in the list\n\t\t\t\t\ti++;\n\t\t\t\t}\n\t\t\t} while (!success && (i < PICK_DIRECTORY_INTENTS.length));\n\n\t\t\tif (!success) {\n\t\t\t\t//No app for folder browsing is installed, show a fallback dialog\n\t\t\t\tAlertDialog.Builder builder = new AlertDialog.Builder(this);\n\n\t\t\t\tbuilder.setTitle(getString(R.string.pref_map_path));\n\t\t\t\t\n\t\t\t\tLayoutInflater inflater = LayoutInflater.from(this);\n\t\t\t\tfinal View alertView = inflater.inflate(R.layout.alert_map_path, null);\n\t\t\t\tfinal EditText editPath = (EditText) alertView.findViewById(R.id.editPath);\n\t\t\t\teditPath.setText(prefMapPathValue);\n\t\t\t\tfinal ImageButton btnOiFilemanager = (ImageButton) alertView.findViewById(R.id.btn_oi_filemanager);\n\t\t\t\tbtnOiFilemanager.setTag(Uri.parse(\"market://details?id=org.openintents.filemanager\"));\n\t\t\t\tfinal ImageButton btnCmFilemanager = (ImageButton) alertView.findViewById(R.id.btn_cm_filemanager);\n\t\t\t\tbtnCmFilemanager.setTag(Uri.parse(\"market://details?id=com.cyanogenmod.filemanager.ics\"));\n\t\t\t\tfinal View.OnClickListener clickListener = new View.OnClickListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onClick(View v) {\n\t\t\t\t\t\tif (v.getTag() instanceof Uri)\n\t\t\t\t\t\tstartActivity(new Intent(Intent.ACTION_VIEW, (Uri) v.getTag()));\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tbuilder.setView(alertView);\n\t\t\t\tbtnOiFilemanager.setOnClickListener(clickListener);\n\t\t\t\tbtnCmFilemanager.setOnClickListener(clickListener);\n\n\t\t\t\tbuilder.setPositiveButton(getString(R.string.action_ok), new DialogInterface.OnClickListener() {\n\t\t\t\t\tpublic void onClick(DialogInterface dialog, int whichButton) {\n\t\t\t\t\t\tsetMapPath(editPath.getText().toString());\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tbuilder.setNegativeButton(getString(R.string.action_cancel), new DialogInterface.OnClickListener() {\n\t\t\t\t\tpublic void onClick(DialogInterface dialog, int whichButton) {\n\t\t\t\t\t\t// NOP\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tbuilder.show();\n\t\t\t\tsuccess = true;\n\t\t\t}\n\n\t\t\treturn success;\n\t\t} else if (preference == prefMapDownload) {\n\t\t\tif (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)\n\t\t\t\tstartActivity(new Intent(this, MapDownloadActivity.class));\n\t\t\telse\n\t\t\t\tActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, Const.PERM_REQUEST_MAP_DOWNLOAD);\n\t\t\treturn true;\n\t\t} else if (preference == prefMapPurge) {\n\t\t\tTileCache mapRendererTileCache = AndroidUtil.createExternalStorageTileCache(this,\n\t\t\t\t\tConst.TILE_CACHE_INTERNAL_RENDER_THEME, 0, 256, true);\n\t\t\tTileCache mapDownloadTileCache = AndroidUtil.createExternalStorageTileCache(this,\n\t\t\t\t\tConst.TILE_CACHE_OSM, 0, 256, true);\n\t\t\tmapRendererTileCache.purge();\n\t\t\tmapDownloadTileCache.purge();\n\t\t\tmapRendererTileCache.destroy();\n\t\t\tmapDownloadTileCache.destroy();\n\t\t\t\n\t\t\t/*\n\t\t\t * This is a hack to have the map view (if it is active) redraw the map:\n\t\t\t * Setting the preference to true causes the listener in MainView to fire.\n\t\t\t * The listener will, in turn, determine if a map view is active and, if so,\n\t\t\t * cause it to reload all tile layers, then reset the preference to false.\n\t\t\t */\n\t\t\tSharedPreferences.Editor spEditor = mSharedPreferences.edit();\n\t\t\tspEditor.putBoolean(Const.KEY_PREF_MAP_PURGE, true);\n\t\t\tspEditor.commit();\n\t\t\t\n\t\t\tString message = getString(R.string.status_map_purged);\n\t\t\tToast.makeText(this, message, Toast.LENGTH_SHORT).show();\n\n\t\t\treturn true;\n\t\t} else\n\t\t\treturn false;\n\t}\n\n\n\t@Override\n\tpublic void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {\n\t\tif ((requestCode == Const.PERM_REQUEST_MAP_DOWNLOAD) && (grantResults.length > 0)) {\n\t\t\tif (grantResults[0] == PackageManager.PERMISSION_GRANTED)\n\t\t\t\tstartActivity(new Intent(this, MapDownloadActivity.class));\n\t\t\telse {\n\t\t\t\tString message = getString(R.string.status_perm_map_download);\n\t\t\t\tToast.makeText(this, message, Toast.LENGTH_SHORT).show();\n\t\t\t}\n\t\t} else if ((requestCode == Const.PERM_REQUEST_LOCATION_PREF) && (grantResults.length > 0)) {\n\t\t\tif (grantResults[0] == PackageManager.PERMISSION_GRANTED) {\n\t\t\t\tNotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);\n\t\t\t\tnotificationManager.cancel(Const.PERM_REQUEST_LOCATION_NOTIFICATION);\n\t\t\t} else\n\t\t\t\tLog.i(TAG, \"ACCESS_FINE_LOCATION permission not granted, a notification will appear when it is needed\");\n\t\t}\n\t}\n\n\n\t@Override\n\tprotected void onResume() {\n\t\tsuper.onResume();\n\t\tmSharedPreferences.registerOnSharedPreferenceChangeListener(this);\n\t\t\n\t\tSettingsFragment sf = (SettingsFragment) getFragmentManager().findFragmentById(android.R.id.content);\n\t\tPreference prefUpdateLast = sf.findPreference(Const.KEY_PREF_UPDATE_LAST);\n        final long value = mSharedPreferences.getLong(Const.KEY_PREF_UPDATE_LAST, 0);\n        prefUpdateLast.setSummary(String.format(getString(R.string.pref_lastupdate_summary), value));\n\t}\n\n\t@Override\n\tpublic void onSharedPreferenceChanged(SharedPreferences sharedPreferences,\n\t\t\tString key) {\n\t\tboolean needsLocationPerm = false;\n\t\tif (key.equals(Const.KEY_PREF_NOTIFY_FIX) || key.equals(Const.KEY_PREF_NOTIFY_SEARCH)) {\n\t\t\tboolean notifyFix = sharedPreferences.getBoolean(Const.KEY_PREF_NOTIFY_FIX, false);\n\t\t\tboolean notifySearch = sharedPreferences.getBoolean(Const.KEY_PREF_NOTIFY_SEARCH, false);\n\t\t\tif (!(notifyFix || notifySearch)) {\n\t\t\t\tIntent stopServiceIntent = new Intent(this, PasvLocListenerService.class);\n\t\t\t\tthis.stopService(stopServiceIntent);\n\t\t\t} else if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {\n\t\t\t\tneedsLocationPerm = true;\n\t\t\t}\n\t\t} else if (key.equals(Const.KEY_PREF_UPDATE_FREQ)) {\n\t\t\t// this piece of code is necessary because Android has no way\n\t\t\t// of updating the preference summary automatically. I am\n\t\t\t// told the absence of such functionality is a feature...\n\t\t\tSettingsFragment sf = (SettingsFragment) getFragmentManager().findFragmentById(android.R.id.content);\n\t\t\tListPreference prefUpdateFreq = (ListPreference) sf.findPreference(Const.KEY_PREF_UPDATE_FREQ);\n            final String value = sharedPreferences.getString(key, key);\n            final int index = prefUpdateFreq.findIndexOfValue(value);            \n            if (index >= 0) {\n                final String summary = (String)prefUpdateFreq.getEntries()[index];         \n                prefUpdateFreq.setSummary(summary);\n            }\n\t\t} else if (key.equals(Const.KEY_PREF_MAP_PATH)) {\n\t\t\tSettingsFragment sf = (SettingsFragment) getFragmentManager().findFragmentById(android.R.id.content);\n\t\t\tPreference prefMapPath = sf.findPreference(Const.KEY_PREF_MAP_PATH);\n\t\t\tprefMapPathValue = mSharedPreferences.getString(Const.KEY_PREF_MAP_PATH, prefMapPathValue);\n\t\t\tprefMapPath.setSummary(prefMapPathValue);\n\t\t} else if (key.equals(Const.KEY_PREF_UPDATE_NETWORKS)) {\n\t\t\tif (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {\n\t\t\t\tSet<String> updateNetworks = sharedPreferences.getStringSet(Const.KEY_PREF_UPDATE_NETWORKS, new HashSet<String>());\n\t\t\t\tif (!updateNetworks.isEmpty())\n\t\t\t\t\tneedsLocationPerm = true;\n\t\t\t}\n\t\t}\n\t\tif (needsLocationPerm)\n\t\t\tActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, Const.PERM_REQUEST_LOCATION_PREF);\n\t}\n\n\t@Override\n\tprotected void onStart() {\n\t\tsuper.onStart();\n\n\t\tSettingsFragment sf = (SettingsFragment) getFragmentManager().findFragmentById(android.R.id.content);\n\t\tprefMapPath = sf.findPreference(Const.KEY_PREF_MAP_PATH);\n\t\tprefMapPathValue = mSharedPreferences.getString(Const.KEY_PREF_MAP_PATH, prefMapPathValue);\n\t\tprefMapPath.setSummary(prefMapPathValue);\n\t\tprefMapPath.setOnPreferenceClickListener(this);\n\t\tprefMapDownload = sf.findPreference(Const.KEY_PREF_MAP_DOWNLOAD);\n\t\tprefMapDownload.setOnPreferenceClickListener(this);\n\t\tprefMapPurge = sf.findPreference(Const.KEY_PREF_MAP_PURGE);\n\t\tprefMapPurge.setOnPreferenceClickListener(this);\n\t}\n\n\t@Override\n\tprotected void onStop() {\n\t\tmSharedPreferences.unregisterOnSharedPreferenceChangeListener(this);\n\t\tsuper.onStop();\n\t}\n\n\tprotected void setMapPath(String path) {\n\t\tSharedPreferences.Editor spEditor = mSharedPreferences.edit();\n\t\tspEditor.putString(Const.KEY_PREF_MAP_PATH, path);\n\t\tspEditor.commit();\n\t}\n\n\tpublic static class SettingsFragment extends PreferenceFragment {\n\t\t@Override\n\t\tpublic void onCreate(Bundle savedInstanceState) {\n\t\t\tsuper.onCreate(savedInstanceState);\n\n\t\t\t// Load the preferences from an XML resource\n\t\t\taddPreferencesFromResource(R.xml.preferences);\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/utils/DownloadTreeViewAdapter.java",
    "content": "/*\n * Copyright (c) 2011, Polidea\n * Copyright © 2013–2016 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat.utils;\n\nimport java.io.File;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.TimeZone;\n\nimport com.vonglasow.michael.satstat.Const;\nimport com.vonglasow.michael.satstat.R;\nimport com.vonglasow.michael.satstat.ui.MapDownloadActivity;\n\nimport pl.polidea.treeview.AbstractTreeViewAdapter;\nimport pl.polidea.treeview.TreeNodeInfo;\nimport pl.polidea.treeview.TreeStateManager;\nimport android.app.Activity;\nimport android.app.AlertDialog;\nimport android.app.DownloadManager;\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.content.SharedPreferences;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.preference.PreferenceManager;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.ImageButton;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.ProgressBar;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\n/**\n * This is a very simple adapter that provides very basic tree view with a\n * simple item description.\n * \n */\npublic class DownloadTreeViewAdapter extends AbstractTreeViewAdapter<RemoteFile> implements RemoteDirListListener {\n\tprivate static final String TAG = DownloadTreeViewAdapter.class.getSimpleName();\n\t\n\t/**\n\t * Progress update delay in milliseconds\n\t */\n\tprivate static final int PROGRESS_DELAY = 1000;\n\t\n\tTreeStateManager<RemoteFile> manager;\n\tMap<RemoteDirListTask, RemoteFile> listTasks;\n\tMap<Long, DownloadInfo> downloadsByReference;\n\tMap<Uri, DownloadInfo> downloadsByUri;\n\tMap<File, DownloadInfo> downloadsByFile;\n\tDownloadManager downloadManager;\n\tSharedPreferences sharedPreferences;\n\tBundle savedInstanceState = null;\n\t\n\tSimpleDateFormat df = new SimpleDateFormat(\"yyyy-MM-dd\", Locale.ROOT);\n\t\n\tHandler handler = new Handler();\n\n\tprivate boolean isProgressCheckerRunning = false;\n\tprivate boolean isReleased = true;\n\n    /**\n     * \n     * @param activity\n     * @param treeStateManager\n     * @param numberOfLevels\n     */\n\tpublic DownloadTreeViewAdapter(final Activity activity,\n            final TreeStateManager<RemoteFile> treeStateManager,\n            final int numberOfLevels) {\n        super(activity, treeStateManager, numberOfLevels);\n        this.manager = treeStateManager;\n        listTasks = new HashMap<RemoteDirListTask, RemoteFile>();\n        downloadsByReference = new HashMap<Long, DownloadInfo>();\n        downloadsByUri = new HashMap<Uri, DownloadInfo>();\n        downloadsByFile = new HashMap<File, DownloadInfo>();\n        df.setTimeZone(TimeZone.getDefault());\n        downloadManager = (DownloadManager) activity.getSystemService(Context.DOWNLOAD_SERVICE);\n        sharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity);\n\n        checkProgress();\n        if (!downloadsByReference.isEmpty())\n        \tstartProgressChecker();\n    }\n\t\n\t/**\n\t * Checks download progress.\n\t * \n\t * This will populate the download lists with any items that are missing, which would be the case if\n\t * downloads are already in progress as the Activity is started, or if the download manager has renamed\n\t * the downloaded file to avoid overwriting an existing one.\n\t */\n\tprivate void checkProgress() {\n        DownloadManager.Query query = new DownloadManager.Query();\n        query.setFilterByStatus(~(DownloadManager.STATUS_FAILED | DownloadManager.STATUS_SUCCESSFUL));\n        Cursor cursor = downloadManager.query(query);\n        if (!cursor.moveToFirst()) {\n        \tcursor.close();\n        \treturn;\n        }\n        do {\n        \t/*\n        \t * The download manager will not supply a local file name until the download has started.\n        \t * We therefore need to cover all possible cases:\n        \t * - No DownloadInfo yet (file objects may or may not be null)\n        \t * - DownloadInfo exists but has no files\n        \t * - DownloadInfo exists but does not have the file actually used by the download manager\n        \t */\n        \tDownloadInfo info;\n        \tLong reference = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID));\n        \tUri uri = Uri.parse(cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_URI)));\n        \tString downloadFileName = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));\n        \tFile downloadFile = (downloadFileName != null) ? new File(downloadFileName) : null;\n        \tFile targetFile = (downloadFile != null) ? new File(downloadFile.getParent(), uri.getLastPathSegment()) : null;\n        \tint progress = (int) (cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)) / 1024);\n        \tif (downloadsByReference.containsKey(reference)) {\n        \t\tinfo = downloadsByReference.get(reference);\n        \t\tif (downloadFile != null) {\n        \t\t\tif ((info.downloadFile == null) || (!info.downloadFile.equals(info.targetFile)))\n        \t\t\t\tinfo.downloadFile = downloadFile;\n        \t\t\tif (info.targetFile == null)\n        \t\t\t\tinfo.targetFile = targetFile;\n        \t\t}\n        \t\tinfo.progress = progress;\n        \t} else {\n            \tinfo = new DownloadInfo(uri, targetFile, downloadFile, reference, progress);\n            \tdownloadsByReference.put(info.reference, info);\n            \tdownloadsByUri.put(info.uri, info);\n            \tif (info.targetFile != null)\n            \t\tdownloadsByFile.put(info.targetFile, info);\n        \t}\n        \tif ((downloadFile != null) && (!downloadsByFile.containsKey(downloadFile))) {\n        \t\tinfo.downloadFile = downloadFile;\n        \t\tdownloadsByFile.put(info.downloadFile, info);\n        \t}\n        } while (cursor.moveToNext());\n        cursor.close();\n\t}\n\n    /**\n     * Registers the receiver for download events.\n     */\n    public void registerIntentReceiver() {\n    \tgetActivity().getApplicationContext().registerReceiver(downloadReceiver, new IntentFilter(Const.DOWNLOAD_RECEIVER_REGISTERED));\n    \tgetActivity().getApplicationContext().registerReceiver(downloadReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));\n    \tgetActivity().getApplicationContext().registerReceiver(downloadReceiver, new IntentFilter(DownloadManager.ACTION_NOTIFICATION_CLICKED));\n    \tisReleased = false;\n    \t/*\n    \t * Send a DOWNLOAD_RECEIVER_REGISTERED broadcast, causing all released receivers to unregister.\n    \t * Since sendBroadcast() is asynchronous, this may not be 100% safe against race conditions: if a\n    \t * download finishes (or the download notification gets clicked) in the short window between sending\n    \t * and receiving the broadcast, the intent for the download would be processed twice. This is not\n    \t * a problem as the related code is safe to run multiple times as long as this happens in an atomic\n    \t * manner, which is the case as long as they are run from the main thread's queue. \n    \t */\n    \tIntent registeredIntent = new Intent(Const.DOWNLOAD_RECEIVER_REGISTERED);\n    \tgetActivity().sendBroadcast(registeredIntent);\n    }\n    \n    /**\n     * Notifies the {@code DownloadTreeViewAdapter} that the caller no longer needs the receiver.\n     * \n     * Calling this method will unregister the receiver only if no downloads are currently in progress. If\n     * downloads are in progress, a flag will be set, causing the receiver to be unregistered after the last\n     * download has finished.\n     * \n     * This will also stop the progress checker.\n     */\n    public void releaseIntentReceiver() {\n    \tisReleased = true;\n    \tstopProgressChecker();\n    \tif (downloadsByUri.isEmpty())\n    \t\tgetActivity().getApplicationContext().unregisterReceiver(downloadReceiver);\n    }\n\n    @Override\n    public View getNewChildView(final TreeNodeInfo<RemoteFile> treeNodeInfo) {\n    \tfinal LinearLayout viewLayout;\n    \tviewLayout = (LinearLayout) getActivity().getLayoutInflater().inflate(R.layout.download_list_item, null);\n    \treturn updateView(viewLayout, treeNodeInfo);\n    }\n\n    @Override\n    public LinearLayout updateView(final View view,\n            final TreeNodeInfo<RemoteFile> treeNodeInfo) {\n        final LinearLayout viewLayout = (LinearLayout) view;\n        final RemoteFile rfile = treeNodeInfo.getId();\n        String rfileName = rfile.name;\n        \n        TextView downloadListItem = (TextView) viewLayout.findViewById(R.id.downloadListItem);\n        TextView downloadSize = (TextView) viewLayout.findViewById(R.id.downloadSize);\n        TextView downloadDate = (TextView) viewLayout.findViewById(R.id.downloadDate);\n        ProgressBar downloadDirProgress = (ProgressBar) viewLayout.findViewById(R.id.downloadDirProgress);\n        ProgressBar downloadFileProgress = (ProgressBar) view.findViewById(R.id.downloadFileProgress);\n        ImageView downloadIcon = (ImageView) view.findViewById(R.id.downloadIcon);\n        ImageButton downloadCancel = (ImageButton) view.findViewById(R.id.downloadCancel);\n        downloadListItem.setText(rfileName);\n        if (rfile.isDirectory) {\n        \tview.setPadding(8, 8, 8, 8);\n        \tdownloadSize.setVisibility(View.GONE);\n        \tdownloadDate.setVisibility(View.GONE);\n        \tdownloadFileProgress.setVisibility(View.GONE);\n        \tdownloadIcon.setVisibility(View.GONE);\n        \tdownloadCancel.setVisibility(View.GONE);\n        \tif (listTasks.containsValue(rfile))\n        \t\tdownloadDirProgress.setVisibility(View.VISIBLE);\n        \telse\n        \t\tdownloadDirProgress.setVisibility(View.INVISIBLE);\n        } else {\n        \tview.setPadding(8, 8, 8, 0);\n        \tdownloadSize.setText(rfile.getFriendlySize());\n        \tdownloadDate.setText(df.format(new Date(rfile.timestamp)));\n        \tdownloadSize.setVisibility(View.VISIBLE);\n        \tdownloadDate.setVisibility(View.VISIBLE);\n        \tdownloadDirProgress.setVisibility(View.GONE);\n        \tif (downloadsByUri.containsKey(rfile.getUri())) {\n        \t\tfinal DownloadInfo info = downloadsByUri.get(rfile.getUri());\n        \t\tdownloadFileProgress.setVisibility(View.VISIBLE);\n        \t\tdownloadFileProgress.setMax((int) (rfile.size / 1024));\n        \t\tdownloadFileProgress.setProgress(downloadsByUri.get(rfile.getUri()).progress);\n        \t\tdownloadIcon.setVisibility(View.GONE);\n        \t\tdownloadCancel.setVisibility(View.VISIBLE);\n        \t\tdownloadCancel.setOnClickListener(new OnClickListener() {\n        \t\t\t@Override\n        \t\t\tpublic void onClick(View v) {\n        \t\t\t\tif (downloadManager.remove(info.reference) > 0) {\n        \t\t\t\t\tremoveDownload(info.reference, false);\n        \t\t\t\t}\n        \t\t\t}\n        \t\t});\n        \t} else {\n        \t\tFile mapFile = new File(\n        \t\t\t\tsharedPreferences.getString(Const.KEY_PREF_MAP_PATH, Const.MAP_PATH_DEFAULT),\n        \t\t\t\trfile.name);\n        \t\tdownloadFileProgress.setVisibility(View.INVISIBLE);\n        \t\tdownloadCancel.setVisibility(View.GONE);\n        \t\tif (!mapFile.exists())\n        \t\t\tdownloadIcon.setImageDrawable(getActivity().getResources().getDrawable(R.drawable.ic_file_download));\n        \t\telse if (mapFile.lastModified() < rfile.timestamp)\n        \t\t\t// TODO recheck this condition (granularity of timestamps, botched timezones)\n        \t\t\tdownloadIcon.setImageDrawable(getActivity().getResources().getDrawable(R.drawable.ic_refresh));\n        \t\telse\n        \t\t\tdownloadIcon.setImageDrawable(getActivity().getResources().getDrawable(R.drawable.ic_check));\n        \t\tdownloadIcon.setVisibility(View.VISIBLE);\n        \t}\n        }\n\n        return viewLayout;\n    }\n\n    @Override\n    public void handleItemClick(final View view, final Object id) {\n        final RemoteFile rfile = (RemoteFile) id;\n        if (rfile.isDirectory) {\n        \tif (rfile.children != null) {\n        \t\t// Show directory contents (warn if directory is empty)\n        \t\tif (rfile.children.length > 0)\n        \t\t\tsuper.handleItemClick(view, id);\n        \t\telse {\n        \t\t\tString message = getActivity().getString(R.string.status_folder_empty);\n        \t\t\tToast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();\n        \t\t}\n        \t} else {\n        \t\tString urlStr = rfile.getUriString();\n        \t\t// Retrieve directory contents from server\n        \t\tRemoteDirListTask task = new RemoteDirListTask(this, rfile);\n        \t\tlistTasks.put(task, rfile);\n        \t\ttask.execute(urlStr);\n                ProgressBar downloadDirProgress = (ProgressBar) view.findViewById(R.id.downloadDirProgress);\n                downloadDirProgress.setVisibility(View.VISIBLE);\n        \t}\n        } else {\n        \t// check if a download is already in progress\n        \tif (!downloadsByUri.containsValue(rfile.getUri())) {\n        \t\t// Download file\n        \t\tfinal File mapFile = new File(\n        \t\t\t\tsharedPreferences.getString(Const.KEY_PREF_MAP_PATH, Const.MAP_PATH_DEFAULT),\n        \t\t\t\trfile.name);\n\n        \t\tif (downloadsByFile.containsKey(mapFile)) {\n        \t\t\t// prevent multiple downloads with same map file name\n        \t\t\tToast.makeText(getActivity(), getActivity().getString(R.string.status_already_downloading), Toast.LENGTH_LONG).show();\n        \t\t\treturn;\n        \t\t}\n        \t\t\n        \t\t// check if we have a current version\n        \t\t// TODO recheck this condition (granularity of timestamps, botched timezones)\n        \t\tif (mapFile.exists() && (mapFile.lastModified() >= rfile.timestamp)) {\n        \t\t\tAlertDialog.Builder builder = new AlertDialog.Builder(getActivity());\n\n        \t\t\tbuilder.setMessage(getActivity().getString(R.string.confirm_download));\n\n        \t\t\tbuilder.setPositiveButton(getActivity().getString(R.string.action_yes), new DialogInterface.OnClickListener() {\n        \t\t\t\tpublic void onClick(DialogInterface dialog, int whichButton) {\n        \t\t\t\t\tstartDownload(rfile, mapFile, view);\n        \t\t\t\t}\n        \t\t\t});\n\n        \t\t\tbuilder.setNegativeButton(getActivity().getString(R.string.action_no), new DialogInterface.OnClickListener() {\n        \t\t\t\tpublic void onClick(DialogInterface dialog, int whichButton) {\n        \t\t\t\t\t// NOP\n        \t\t\t\t}\n        \t\t\t});\n        \t\t\t\n        \t\t\tbuilder.show();\n        \t\t} else\n        \t\t\tstartDownload(rfile, mapFile, view);\n        \t}\n        }\n    }\n\n    @Override\n    public long getItemId(final int position) {\n        return getTreeId(position).hashCode();\n    }\n\n\t@Override\n\tpublic void onRemoteDirListReady(RemoteDirListTask task, RemoteFile[] rfiles) {\n\t\tRemoteFile parent = listTasks.get(task);\n\n\t\tlistTasks.remove(task);\n\t\t\n\t\tif (rfiles == null) {\n\t\t\t// TODO\n\t\t} else if (rfiles.length == 0) {\n\t\t\tmanager.refresh();\n\t\t\thandleItemClick(null, parent);\n\t\t} else\n\t\t\tfor (RemoteFile rf : rfiles)\n\t\t\t\tmanager.addAfterChild(parent, rf, null);\n\t}\n\t\n\t/**\n\t * Called when a download has completed, failed or been canceled.\n\t * \n\t * This removes the download from all internal data structures and cleans up backup copies, if any: If\n\t * the download was successful, the backup file is deleted. If the download failed or was canceled, the\n\t * incompletely downloaded file is deleted and the backup file is moved to its original location. Finally\n\t * a refresh of the UI is triggered to reflect the new state of the file.\n\t * \n\t * This method is safe to invoke from multiple instances holding identical download lists, as long as the\n\t * calls are made in sequence (which is the case if they are invoked from the main thread's message loop.)\n\t * This is important as race conditions between registering a new receiver and unregistering a previous\n\t * one are possible, though unlikely.\n\t * \n\t * @param reference The reference used by DownloadManager.\n\t * @param success True if the download completed successfully, false if it failed or was canceled.\n\t */\n\tprivate void removeDownload(long reference, boolean success) {\n\t\tDownloadInfo info = downloadsByReference.get(reference);\n\t\tdownloadsByReference.remove(reference);\n\t\tif (info != null) {\n\t\t\tdownloadsByUri.remove(info.uri);\n\t\t\tdownloadsByFile.remove(info.targetFile);\n\t\t\tdownloadsByFile.remove(info.downloadFile);\n\t\t\t// if we're refreshing an existing map file, do the swap operation now\n\t\t\tif (success && !info.targetFile.equals(info.downloadFile) && info.downloadFile.exists())\n\t\t\t\tif (!info.targetFile.exists() || info.targetFile.delete())\n\t\t\t\t\tinfo.downloadFile.renameTo(info.targetFile);\n\t\t}\n\t\tif (downloadsByUri.isEmpty()) {\n\t\t\t/*\n\t\t\t * All downloads have finished. We no longer need the saved instance state and progress checker, \n\t\t\t * and if the activity has been destroyed, we can unregister the broadcast receiver as well.\n\t\t\t */\n\t\t\tthis.storeInstanceState(null);\n\t\t\tstopProgressChecker();\n\t\t\tif (isReleased)\n\t\t\t\tgetActivity().getApplicationContext().unregisterReceiver(downloadReceiver);\n\t\t\tToast.makeText(getActivity(), getActivity().getString(R.string.status_downloads_completed), Toast.LENGTH_SHORT).show();\n\t\t}\n\t\tmanager.refresh();\n\t}\n    \n    /**\n     * Starts a map download.\n     * \n     * This will also start the progress checker.\n     * \n     * @param rfile The remote file to download\n     * @param mapFile The local file to which the map will be saved\n     * @param view The {@link View} displaying the map file\n     */\n    private void startDownload(RemoteFile rfile, File mapFile, View view) {\n    \tUri uri = rfile.getUri();\n    \tUri destUri = Uri.fromFile(mapFile);\n    \ttry {\n    \t\tDownloadManager.Request request = new DownloadManager.Request(uri);\n    \t\t//request.setTitle(rfile.name);\n    \t\t//request.setDescription(\"SatStat map download\");\n    \t\trequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);\n    \t\t//request.setDestinationInExternalFilesDir(getActivity(), dirType, subPath)\n    \t\trequest.setDestinationUri(destUri);\n    \t\tLog.d(TAG, String.format(\"Ready to download %s to %s (local name %s)\", uri.toString(), destUri.toString(), mapFile.getName()));\n    \t\tLong reference = downloadManager.enqueue(request);\n    \t\tDownloadInfo info = new DownloadInfo(rfile, uri, mapFile, reference);\n    \t\tdownloadsByReference.put(reference, info);\n    \t\tdownloadsByUri.put(rfile.getUri(), info);\n    \t\tdownloadsByFile.put(mapFile, info);\n    \t\tProgressBar downloadFileProgress = (ProgressBar) view.findViewById(R.id.downloadFileProgress);\n    \t\tdownloadFileProgress.setVisibility(View.VISIBLE);\n    \t\tdownloadFileProgress.setMax((int) (rfile.size / 1024));\n    \t\tdownloadFileProgress.setProgress(0);\n    \t\tstartProgressChecker();\n    \t} catch (SecurityException e) {\n    \t\tLog.w(TAG, String.format(\"Permission not granted to download %s to %s\", uri.toString(), destUri.toString()));\n    \t}\n    }\n    \n    /**\n     * Starts watching download progress.\n     * \n     * This method is safe to call multiple times. Starting an already running progress checker is a no-op.\n     */\n    private void startProgressChecker() {\n    \tif (!isProgressCheckerRunning) {\n    \t\tprogressChecker.run();\n    \t\tisProgressCheckerRunning = true;\n    \t}\n    }\n    \n    /**\n     * Stops watching download progress.\n     */\n    private void stopProgressChecker() {\n    \thandler.removeCallbacks(progressChecker);\n    \tisProgressCheckerRunning = false;\n    }\n\n\t/**\n\t * Stores the state of the associated {@link MapDownloadActivity}.\n\t * \n\t * This is needed to recreate the activity when the download notification is tapped. When that happens,\n\t * the Intent which restarts the activity will have a Bundle extra named {@code KEY_SAVED_INSTANCE_STATE},\n\t * which contains the object stored with this method.\n\t * \n\t * @param savedInstanceState The saved instance state\n\t */\n\tpublic void storeInstanceState(Bundle savedInstanceState) {\n\t\tthis.savedInstanceState = savedInstanceState;\n\t}\n\t\n\t/**\n\t * Checks download progress and updates status, then re-schedules itself.\n\t */\n\tprivate Runnable progressChecker = new Runnable() {\n\t\t@Override\n\t\tpublic void run() {\n\t\t\ttry {\n\t\t\t\tcheckProgress();\n\t\t\t\tmanager.refresh();\n\t\t\t} finally {\n\t\t\t\thandler.postDelayed(progressChecker, PROGRESS_DELAY);\n\t\t\t}\n\t\t}\n\t};\n\n\tprivate BroadcastReceiver downloadReceiver = new BroadcastReceiver() {\n\t\t/*\n\t\t * This method is safe to invoke from multiple instances holding identical download lists, as long as\n\t\t * the calls are made in sequence (which is the case if they are invoked from the main thread's\n\t\t * message loop.) This is important as race conditions between registering a new receiver and\n\t\t * unregistering a previous one are possible, though unlikely.\n\t\t */\n\t\t@Override\n\t\tpublic void onReceive(Context context, Intent intent) {\n\t\t\tif (intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {\n\t\t\t\t// this will be called when a download finishes\n\t\t\t\tLong reference = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);\n\t\t\t\tDownloadManager.Query query = new DownloadManager.Query();\n\t\t\t\tquery.setFilterById(reference);\n\t\t\t\tCursor cursor = downloadManager.query(query);\n\t\t\t\tif (!cursor.moveToFirst()) {\n\t\t\t\t\tcursor.close();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tint status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));\n\t\t\t\t//int reason = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_REASON));\n\t\t\t\tcursor.close();\n\t\t\t\tswitch (status) {\n\t\t\t\tcase DownloadManager.STATUS_SUCCESSFUL:\n\t\t\t\t\t// The file was downloaded successfully\n\t\t\t\t\tremoveDownload(reference, true);\n\t\t\t\t\tbreak;\n\t\t\t\tcase DownloadManager.STATUS_FAILED:\n\t\t\t\t\t// The download failed\n\t\t\t\t\tremoveDownload(reference, false);\n\t\t\t\t\tbreak;\n\t\t\t\tcase DownloadManager.STATUS_PAUSED:\n\t\t\t\t\t// The download was paused, update status once more\n\t\t\t\t\tcheckProgress();\n\t\t\t\t\tmanager.refresh();\n\t\t\t\t\tbreak;\n\t\t\t\t// The other status values are unusable because they don't fire reliably.\n\t\t\t\t}\n\t\t\t} else if (intent.getAction().equals(DownloadManager.ACTION_NOTIFICATION_CLICKED)) {\n\t\t\t\tIntent mapDownloadIntent = new Intent(getActivity().getApplicationContext(), MapDownloadActivity.class);\n\t\t\t\tmapDownloadIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);\n\t\t\t\tif (savedInstanceState != null)\n\t\t\t\t\tmapDownloadIntent.putExtra(Const.KEY_SAVED_INSTANCE_STATE, savedInstanceState);\n\t\t\t\tgetActivity().getApplicationContext().startActivity(mapDownloadIntent);\n\t\t\t} else if (intent.getAction().equals(Const.DOWNLOAD_RECEIVER_REGISTERED)) {\n\t\t\t\t/*\n\t\t\t\t * A new download receiver has been registered. If we're released, unregister.\n\t\t\t\t */\n\t\t\t\tif (isReleased)\n\t\t\t\t\tgetActivity().getApplicationContext().unregisterReceiver(downloadReceiver);\n\t\t\t}\n\t\t}\n\t};\n\t\n\t/**\n\t * Information about a download in progress.\n\t */\n\tprivate class DownloadInfo {\n\t\t/**\n\t\t * The RemoteFile representing the file being downloaded.\n\t\t */\n\t\tprivate RemoteFile remoteFile;\n\t\t\n\t\t/**\n\t\t * The URI from which the file is actually being downloaded.\n\t\t */\n\t\tprivate Uri uri;\n\t\t\n\t\t/**\n\t\t * The local map file at which the map will be saved once the download finishes.\n\t\t */\n\t\tprivate File targetFile;\n\t\t\n\t\t/**\n\t\t * The file to which the map is being downloaded.\n\t\t * \n\t\t * When downloading a map for the first time, this is equal to {@code targetFile}.\n\t\t * When an existing map is being replaced, this is different from {@code targetFile}.\n\t\t */\n\t\tprivate File downloadFile;\n\t\t\n\t\t/**\n\t\t * The reference under which the download manager tracks the download.\n\t\t */\n\t\tprivate long reference;\n\t\t\n\t\t/**\n\t\t * Download progress in kiB.\n\t\t */\n\t\tprivate int progress;\n\n\t\tprivate DownloadInfo(RemoteFile remoteFile, Uri uri, File targetFile, long reference) {\n\t\t\tsuper();\n\t\t\tthis.remoteFile = remoteFile;\n\t\t\tthis.uri = uri;\n\t\t\tthis.targetFile = targetFile;\n\t\t\tthis.downloadFile = targetFile;\n\t\t\tthis.reference = reference;\n\t\t\tthis.progress = 0;\n\t\t}\n\n\t\tprivate DownloadInfo(Uri uri, File targetFile, File downloadFile, long reference, int progress) {\n\t\t\tsuper();\n\t\t\tthis.uri = uri;\n\t\t\tthis.targetFile = targetFile;\n\t\t\tthis.downloadFile = downloadFile;\n\t\t\tthis.reference = reference;\n\t\t\tthis.progress = progress;\n\t\t}\n\t}\n}"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/utils/HttpDownloader.java",
    "content": "/*\n * Copyright © 2013–2016 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat.utils;\n\nimport java.io.BufferedInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.HttpURLConnection;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URLConnection;\nimport java.util.ArrayList;\nimport java.util.Collections;\n\nimport com.vonglasow.michael.satstat.Const;\n\nimport android.text.Html;\nimport android.text.Spanned;\nimport android.text.style.URLSpan;\nimport android.util.Log;\n\n/**\n * Provides methods to browse and download from HTTP sites with an FTP-like UI (folder lists).\n */\npublic class HttpDownloader {\n\tprivate static final String CONTENT_LENGTH = \"content-length\";\n\n\tprivate static final String TAG = \"HttpDownloader\";\n\t\n\tprivate static final RemoteFileComparator comparator = new RemoteFileComparator();\n\n\t/**\n\t * @brief Returns the value of the {@code content-length} header field as a long.\n\t * \n\t * This is a workaround for a limitation of the Android API, which has no native method to\n\t * report content lengths in excess of 2 GB on API levels lower than N.\n\t * \n\t * @param connection The URL connection\n\t * \n\t * @return Content length in bytes, or -1 if an error occurs\n\t */\n\tprivate static long getContentLength(URLConnection connection) {\n\t\tlong res = -1;\n\t\ttry {\n\t\t\tres = Long.valueOf(connection.getHeaderField(CONTENT_LENGTH));\n\t\t} catch (Exception e) {\n\t\t\t// do nothing and return -1\n\t\t}\n\t\tif (res < 0)\n\t\t\tres = -1;\n\t\treturn res;\n\t}\n\n\t/**\n\t * @brief Retrieves information about a remote file or directory\n\t * \n\t * @param url\n\t * @return A {@link RemoteFile} filled in with the data of the remote file or directory, or {@code null}\n\t * if an error occurred.\n\t */\n\tprivate static RemoteFile getFileInfo(URL context, String href) {\n\t\tString baseUrl = context.toString();\n\t\tboolean isDirectory = false;\n\t\tString name;\n\t\tlong size;\n\t\tlong timestamp;\n\t\tURL url;\n\t\tboolean isTypeKnown = false; // whether we know already if the target is a directory\n\t\tHttpURLConnection http = null;\n\t\t/*\n\t\tLog.d(TAG, String.format(\"Download from:\\n\\tProtocol: %s\\n\\tHost: %s\\n\\tPort: %d\\n\\tUser: %s\\n\\tPath: %s\",\n\t\t\turl.getProtocol(),\n\t\t\turl.getHost(),\n\t\t\turl.getPort(),\n\t\t\turl.getUserInfo(),\n\t\t\turl.getPath()));\n\t\t*/\n\t\ttry {\n\t\t\turl = new URL(context, href);\n\t\t} catch (MalformedURLException e) {\n\t\t\tLog.w(TAG, String.format(\"%s is not a valid href, skipping\", href));\n\t\t\treturn null;\n\t\t}\n\t\t//Log.d(TAG, String.format(\"Getting information for %s:\\n\\tURL: %s\", href, url.toString()));\n\n\t\tif (href.endsWith(\"/\")) {\n\t\t\tname = href.substring(0, href.length() - 1);\n\t\t\tif (!isTypeKnown)\n\t\t\t\tisDirectory = true; // best guess, hence isTypeKnown remains false\n\t\t} else\n\t\t\tname = href;\n\t\ttry {\n\t\t\thttp = (HttpURLConnection) url.openConnection();\n\t\t\thttp.setRequestMethod(\"HEAD\");\n\t\t\thttp.connect();\n\t\t\tif (http.getContentType() != null) {\n\t\t\t\tif (Const.CONTENT_TYPE_HTML.equals(http.getContentType()) || http.getContentType().startsWith(Const.CONTENT_TYPE_HTML + \";\")) {\n\t\t\t\t\tif (!isTypeKnown)\n\t\t\t\t\t\tisDirectory = true; // best guess, hence isTypeKnown remains false\n\t\t\t\t} else {\n\t\t\t\t\tisTypeKnown = true;\n\t\t\t\t\tisDirectory = false;\n\t\t\t\t}\n\t\t\t}\n\t\t\tsize = getContentLength(http);\n\t\t\ttimestamp = http.getLastModified();\n\t\t\t//Log.d(TAG, String.format(\"\\tContent Type: %s\\n\\tSize: %d\\n\\tTimestamp: %d\", http.getContentType(), http.getContentLength(), http.getLastModified()));\n\t\t} catch (IOException e) {\n\t\t\tLog.e(TAG, \"IOException trying to connect: \" + e.getMessage());\n\t\t\te.printStackTrace();\n\t\t\treturn null;\n\t\t} finally {\n\t\t\tif (http != null)\n\t\t\t\thttp.disconnect();\n\t\t}\n\t\treturn new RemoteFile(baseUrl, isDirectory, name, size, timestamp);\n\t}\n\t\n\t\n\t/**\n\t * @brief Determines if two URLs have the same port.\n\t * \n\t * This method determines the port used by each of the two URLs (either an explicitly specified\n\t * port or, where absent, the default port) and compares them.\n\t * \n\t * @param url1\n\t * @param url2\n\t * @return true if the URLs effectively use the same port, false otherwise\n\t */\n\tprivate static boolean isPortEqual(URL url1, URL url2) {\n\t\tint port1 = (url1.getPort() > 0) ? url1.getPort() : url1.getDefaultPort();\n\t\tint port2 = (url2.getPort() > 0) ? url2.getPort() : url2.getDefaultPort();\n\t\t\n\t\treturn (port1 == port2);\n\t}\n\t\n\t\n\t/**\n\t * @brief Lists a remote directory.\n\t * \n\t * @param urlStr The URL of the remote directory.\n\t * @return An array of {@link RemoteFile} objects representing the contents of the remote directory.\n\t * An empty array is returned if the remote directory is empty. If an error is encountered, {@code null}\n\t * is returned.\n\t */\n\tpublic static RemoteFile[] list(String urlStr) {\n\t\tArrayList<RemoteFile> rfiles = new ArrayList<RemoteFile>();\n\t\tURL url;\n\t\tHttpURLConnection http = null;\n\t\tSpanned parsedHtml;\n\t\ttry {\n\t\t\tString base = urlStr;\n\t\t\tif (base.charAt(base.length() - 1) != '/')\n\t\t\t\tbase = base + \"/\";\n\t\t\turl = new URL(base);\n\t\t} catch (MalformedURLException e) {\n\t\t\tLog.e(TAG, \"MalformedURLException: \" + e.getMessage());\n\t\t\te.printStackTrace();\n\t\t\treturn null;\n\t\t}\n\t\t/*\n\t\tLog.d(TAG, String.format(\"Download from:\\n\\tProtocol: %s\\n\\tHost: %s\\n\\tPort: %d\\n\\tUser: %s\\n\\tPath: %s\",\n\t\t\turl.getProtocol(),\n\t\t\turl.getHost(),\n\t\t\turl.getPort(),\n\t\t\turl.getUserInfo(),\n\t\t\turl.getPath()));\n\t\t */\n\t\ttry {\n\t\t\thttp = (HttpURLConnection) url.openConnection();\n\t\t\tInputStream in = new BufferedInputStream(http.getInputStream());\n\t\t\t// FIXME this redirect check will barf on perfectly legit redirects (think Akamai)\n\t\t\tif (!url.getHost().equals(http.getURL().getHost()))\n\t\t\t\tthrow new IOException(\"Unexpected redirection! Do you need to sign into your network first?\");\n\t\t\tif ((http.getContentType() == null) || (!Const.CONTENT_TYPE_HTML.equals(http.getContentType()) && !http.getContentType().startsWith(Const.CONTENT_TYPE_HTML + \";\")))\n\t\t\t\tthrow new IOException(String.format(\"Response is not in HTML format, got %s\", http.getContentType()));\n\t\t\t\n\t\t\t// read output into a stream which we can convert to a string so we can process it further\n\t\t\tByteArrayOutputStream out = new ByteArrayOutputStream();\n\t\t\tint i;\n\t\t\ttry {\n\t\t\t\ti = in.read();\n\t\t\t\twhile (i != -1) {\n\t\t\t\t\tout.write(i);\n\t\t\t\t\ti = in.read();\n\t\t\t\t}\n\t\t\t\tin.close();\n\t\t\t} catch (IOException e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\t\n\t\t\tparsedHtml = Html.fromHtml(out.toString());\n\n\t\t\t// links get converted to URLSpan objects, examine them\n\t\t\tURLSpan [] us = parsedHtml.getSpans(0, parsedHtml.length(), android.text.style.URLSpan.class);\n\t\t\tfor (URLSpan u : us) {\n\t\t\t\tString href = u.getURL();\n\t\t\t\t//Log.d(TAG, href);\n\t\t\t\t\n\t\t\t\tURL hrefUrl;\n\t\t\t\ttry {\n\t\t\t\t\threfUrl = new URL(url, href);\n\t\t\t\t\tif (!url.getProtocol().matches(hrefUrl.getProtocol())\n\t\t\t\t\t\t\t|| !url.getHost().matches(hrefUrl.getHost())\n\t\t\t\t\t\t\t|| !isPortEqual(url, hrefUrl))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t} catch (MalformedURLException e) {\n\t\t\t\t\tLog.w(TAG, String.format(\"%s is not a valid href, skipping\", href));\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// both URLs refer to the same protocol, host and port, therefore we can discard those parts\n\t\t\t\thref = hrefUrl.getPath();\n\t\t\t\t// href is now just a path (absolute or relative)\n\t\t\t\t// get the base path (path from url, ensuring it ends with a slash)\n\t\t\t\tString basePath = url.getPath();\n\t\t\t\tif (href.startsWith(basePath))\n\t\t\t\t\thref = href.substring(basePath.length());\n\t\t\t\telse if (href.startsWith(\"/\"))\n\t\t\t\t\tcontinue;\n\t\t\t\t// href is now a relative path but may still contain queries or anchors\n\t\t\t\t// query and ref are null if not specified (TODO what if they are specified but empty?)\n\t\t\t\t//Log.d(TAG, String.format(\"\\tquery: %s anchor: %s\", hrefUrl.getQuery(), hrefUrl.getRef()));\n\t\t\t\tif ((hrefUrl.getQuery() != null) && (!hrefUrl.getQuery().isEmpty()))\n\t\t\t\t\tcontinue;\n\t\t\t\tif ((hrefUrl.getRef() != null) && (!hrefUrl.getRef().isEmpty()))\n\t\t\t\t\tcontinue;\n\t\t\t\t// href is now a relative path, free of queries or anchors but any number of levels deep\n\t\t\t\tif (href.substring(0, href.length() - 1).indexOf(\"/\") >= 0)\n\t\t\t\t\tcontinue;\n\t\t\t\t// href points to an immediate child object, examine it\n\t\t\t\tRemoteFile rf = getFileInfo(url, href);\n\t\t\t\tif (rf == null)\n\t\t\t\t\tcontinue;\n\t\t\t\trfiles.add(rf);\n\t\t\t}\n\t\t\tCollections.sort(rfiles, comparator);\n\t\t\treturn rfiles.toArray(new RemoteFile[]{});\n\t\t} catch (IOException e) {\n\t\t\tLog.e(TAG, \"IOException trying to connect: \" + e.getMessage());\n\t\t\te.printStackTrace();\n\t\t\treturn null;\n\t\t} finally {\n\t\t\tif (http != null)\n\t\t\t\thttp.disconnect();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/utils/PermissionHelper.java",
    "content": "/*\n * Copyright © 2013–2016 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat.utils;\n\nimport com.vonglasow.michael.satstat.Const;\nimport android.app.NotificationManager;\nimport android.app.PendingIntent;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.support.v4.app.ActivityCompat;\nimport android.support.v4.app.NotificationCompat;\nimport android.support.v4.app.TaskStackBuilder;\nimport android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback;\nimport android.support.v4.os.ResultReceiver;\nimport android.support.v7.app.AppCompatActivity;\n\n/**\n * Provides helper methods to request permissions from components other than Activities.\n */\npublic class PermissionHelper {\n\tprivate static final String TAG = PermissionHelper.class.getSimpleName();\n\n\n\t/**\n\t * Requests permissions to be granted to this application.\n\t * \n\t * This method is a wrapper around\n\t * {@link android.support.v4.app.ActivityCompat#requestPermissions(android.app.Activity, String[], int)}\n\t * which works in a similar way, except it can be called from non-activity contexts. When called, it\n\t * displays a notification with a customizable title and text. When the user taps the notification, an\n\t * activity is launched in which the user is prompted to allow or deny the request.\n\t * \n\t * After the user has made a choice,\n\t * {@link android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback#onRequestPermissionsResult(int, String[], int[])}\n\t * is called, reporting whether the permissions were granted or not.\n\t * \n\t * @param context The context from which the request was made. The context supplied must implement\n\t * {@link android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback} and will receive the\n\t * result of the operation.\n\t * @param permissions The requested permissions\n\t * @param requestCode Application specific request code to match with a result reported to\n\t * {@link android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback#onRequestPermissionsResult(int, String[], int[])}\n\t * @param notificationTitle The title for the notification\n\t * @param notificationText The text for the notification\n\t * @param notificationIcon Resource identifier for the notification icon\n\t */\n\tpublic static <T extends Context & OnRequestPermissionsResultCallback> void requestPermissions(final T context, String[] permissions, int requestCode, String notificationTitle, String notificationText, int notificationIcon) {\n\t\tResultReceiver resultReceiver = new ResultReceiver(new Handler(Looper.getMainLooper())) {\n\t\t\t@Override\n\t\t\tprotected void onReceiveResult (int resultCode, Bundle resultData) {\n\t\t\t\tString[] outPermissions = resultData.getStringArray(Const.KEY_PERMISSIONS);\n\t\t\t\tint[] grantResults = resultData.getIntArray(Const.KEY_GRANT_RESULTS);\n\t\t\t\tcontext.onRequestPermissionsResult(resultCode, outPermissions, grantResults);\n\t\t\t}\n\t\t};\n\n\t\tIntent permIntent = new Intent(context, PermissionRequestActivity.class);\n\t\tpermIntent.putExtra(Const.KEY_RESULT_RECEIVER, resultReceiver);\n\t\tpermIntent.putExtra(Const.KEY_PERMISSIONS, permissions);\n\t\tpermIntent.putExtra(Const.KEY_REQUEST_CODE, requestCode);\n\n\t\tTaskStackBuilder stackBuilder = TaskStackBuilder.create(context);\n\t\tstackBuilder.addNextIntent(permIntent);\n\n\t\tPendingIntent permPendingIntent =\n\t\t\t\tstackBuilder.getPendingIntent(\n\t\t\t\t\t\t0,\n\t\t\t\t\t\tPendingIntent.FLAG_UPDATE_CURRENT\n\t\t\t\t\t\t);\n\n\t\tNotificationCompat.Builder builder = new NotificationCompat.Builder(context)\n\t\t.setSmallIcon(notificationIcon)\n\t\t.setContentTitle(notificationTitle)\n\t\t.setContentText(notificationText)\n\t\t.setOngoing(true)\n\t\t//.setCategory(Notification.CATEGORY_STATUS)\n\t\t.setAutoCancel(true)\n\t\t.setWhen(0)\n\t\t.setContentIntent(permPendingIntent)\n\t\t.setStyle(null);\n\n\t\tNotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);\n\t\tnotificationManager.notify(requestCode, builder.build());\n\t}\n\n\n\t/**\n\t * A blank {@link Activity} on top of which permission request dialogs can be displayed\n\t */\n\tpublic static class PermissionRequestActivity extends AppCompatActivity {\n\t\tResultReceiver resultReceiver;\n\t\tString[] permissions;\n\t\tint requestCode;\n\n\t\t/**\n\t\t * Called when the user has made a choice in the permission dialog.\n\t\t * \n\t\t * This method wraps the responses in a {@link Bundle} and passes it to the {@link ResultReceiver}\n\t\t * specified in the {@link Intent} that started the activity, then closes the activity.\n\t\t */\n\t\t@Override\n\t\tpublic void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {\n\t\t\tBundle resultData = new Bundle();\n\t\t\tresultData.putStringArray(Const.KEY_PERMISSIONS, permissions);\n\t\t\tresultData.putIntArray(Const.KEY_GRANT_RESULTS, grantResults);\n\t\t\tresultReceiver.send(requestCode, resultData);\n\t\t\tfinish();\n\t\t}\n\n\n\t\t/**\n\t\t * Called when the activity is started.\n\t\t * \n\t\t * This method obtains several extras from the {@link Intent} that started the activity: the request\n\t\t * code, the requested permissions and the {@link ResultReceiver} which will receive the results.\n\t\t * After that, it issues the permission request.\n\t\t */\n\t\t@Override\n\t\tprotected void onStart() {\n\t\t\tsuper.onStart();\n\n\t\t\tresultReceiver = this.getIntent().getParcelableExtra(Const.KEY_RESULT_RECEIVER);\n\t\t\tpermissions = this.getIntent().getStringArrayExtra(Const.KEY_PERMISSIONS);\n\t\t\trequestCode = this.getIntent().getIntExtra(Const.KEY_REQUEST_CODE, 0);\n\n\t\t\tActivityCompat.requestPermissions(this, permissions, requestCode);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/utils/RemoteDirListListener.java",
    "content": "/*\n * Copyright © 2013–2016 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat.utils;\n\n\n/**\n * Receives notifications when a remote directory listing has completed.\n */\npublic interface RemoteDirListListener {\n\t/**\n\t * Called when a remote directory listing has been retrieved.\n\t * \n\t * @param rfiles An array of all objects in the remote directory.\n\t */\n\tpublic void onRemoteDirListReady(RemoteDirListTask task, RemoteFile[] rfiles);\n}"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/utils/RemoteDirListTask.java",
    "content": "/*\n * Copyright © 2013–2016 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat.utils;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Locale;\nimport java.util.TimeZone;\n\nimport android.net.Uri;\nimport android.os.AsyncTask;\nimport android.util.Log;\n\n/**\n * A task which retrieves the contents of a remote directory in the background and notifies a listener\n * upon completion.\n */\npublic class RemoteDirListTask extends AsyncTask<String, Void, RemoteFile[]> {\n\tprivate static final String TAG = RemoteDirListTask.class.getSimpleName();\n\tprivate RemoteDirListListener listener = null;\n\tprivate RemoteFile parent = null;\n\t\n\t/**\n\t * Creates a new {@code RemoteDirListTask} task, and registers it with a listener.\n\t * \n\t * @param listener The {@link RemoteDirListListener} which will be notified when the task has completed.\n\t * @param parent The directory to be listed. When this task finishes, it populates the {@code children}\n\t * member of {@code parent} with the objects it retrieved. May be {@code null}.\n\t */\n\tpublic RemoteDirListTask(RemoteDirListListener listener, RemoteFile parent) {\n\t\tsuper();\n\t\tthis.listener = listener;\n\t\tthis.parent = parent;\n\t}\n\t\n\t\n\t@Override\n\tprotected RemoteFile[] doInBackground(String... params) {\n\t\tUri uri = Uri.parse(params[0]);\n\t\tRemoteFile[] rfiles = null;\n\t\tSimpleDateFormat df = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\", Locale.ROOT);\n\t\tdf.setTimeZone(TimeZone.getDefault());\n\t\tif (uri.getScheme() == null)\n\t\t\treturn null;\n\t\tif (uri.getScheme().equals(\"http\") || uri.getScheme().equals(\"https\"))\n\t\t\trfiles = HttpDownloader.list(params[0]);\n\t\t\n\t\tif (rfiles == null)\n\t\t\tLog.w(TAG, \"Error – could not retrieve content!\");\n\t\telse if (rfiles.length == 0)\n\t\t\tLog.w(TAG, \"Remote directory is empty.\");\n\t\t/*\n\t\telse {\n\t\t\tLog.d(TAG, \"Remote directory contents:\");\n\t\t\tfor (RemoteFile rf : rfiles)\n\t\t\t\tLog.d(TAG, String.format(\"\\n\\t%s \\t%s \\t%s \\t%s\",\n\t\t\t\t\trf.isDirectory ? \"D\" : \"F\",\n\t\t\t\t\tdf.format(new Date(rf.timestamp)),\n\t\t\t\t\trf.getFriendlySize(),\n\t\t\t\t\trf.name));\n\t\t}\n\t\t */\n\t\treturn rfiles;\n\t}\n\n\tprotected void onPostExecute(RemoteFile[] result) {\n\t\tif (parent != null)\n\t\t\tparent.children = result;\n\t\tif (listener != null)\n\t\t\tlistener.onRemoteDirListReady(this, result);\n\t}\n}"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/utils/RemoteFile.java",
    "content": "/*\n * Copyright © 2013–2016 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat.utils;\n\nimport java.io.Serializable;\nimport java.util.Locale;\n\nimport android.net.Uri;\n\n/**\n * Describes a file system object that can be fetched from a remote (HTTP or FTP) server.\n */\n// Implementing Serializable is required for saving DownloadTreeStateManager to a Bundle\npublic class RemoteFile implements Serializable {\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * The URL of the containing folder.\n\t */\n\tpublic String baseUrl;\n\n\t/**\n\t * The children of this object, i.e. files in this folder and direct subfolders. Valid for folders only.\n\t * A value of {@code null} indicates that a folder listing has not yet been retrieved, whereas an empty\n\t * array indicates a folder that is known to be empty.\n\t */\n\tpublic RemoteFile[] children = null;\n\n\t/**\n\t * Whether the file system object is a directory or a regular file.\n\t */\n\tpublic boolean isDirectory;\n\n\t/**\n\t * The local name of the file system object.\n\t */\n\tpublic String name;\n\n\t/**\n\t * The size of the file system object, in bytes (-1 if unknown).\n\t */\n\tpublic long size;\n\n\t/**\n\t * The timestamp of the file system object. This is typically the last modification time. 0 if unknown.\n\t */\n\tpublic long timestamp;\n\n\tpublic RemoteFile(String baseUrl, boolean isDirectory, String name, long size, long timestamp) {\n\t\tsuper();\n\t\tthis.baseUrl = baseUrl;\n\t\tthis.isDirectory = isDirectory;\n\t\tthis.name = name;\n\t\tthis.size = size;\n\t\tthis.timestamp = timestamp;\n\t}\n\n\tpublic String getFriendlySize() {\n\t\tif (size < 1024)\n\t\t\treturn String.format(Locale.getDefault(), \"%d\", size);\n\t\tfloat tmp = size / 1024;\n\t\tif (tmp < 1024)\n\t\t\treturn String.format(Locale.getDefault(), \"%.1fk\", tmp);\n\t\ttmp /= 1024;\n\t\tif (tmp < 1024)\n\t\t\treturn String.format(Locale.getDefault(), \"%.1fM\", tmp);\n\t\ttmp /= 1024;\n\t\tif (tmp < 1024)\n\t\t\treturn String.format(Locale.getDefault(), \"%.1fG\", tmp);\n\t\ttmp /= 1024;\n\t\treturn String.format(Locale.getDefault(), \"%.1fT\", tmp);\n\t}\n\n\t/**\n\t * Returns the full URI to the remote file.\n\t */\n\tpublic Uri getUri() {\n\t\tUri baseUri = Uri.parse(this.baseUrl);\n\t\tUri uri = baseUri.buildUpon().appendPath(this.name).build();\n\t\treturn uri;\n\t}\n\n\t/**\n\t * Returns the full URI to the remote file as a string.\n\t */\n\tpublic String getUriString() {\n\t\treturn this.getUri().toString();\n\t}\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/utils/RemoteFileComparator.java",
    "content": "package com.vonglasow.michael.satstat.utils;\n\nimport java.text.Collator;\nimport java.util.Comparator;\n\n/**\n * A Comparator for {@link RemoteFile}s.\n * \n * Sorting is done by name, which may alternate between files and directories (unlike the customary file\n * manager experience, where directories tend to be listed first). Sort order is determined by the default\n * locale.\n */\npublic class RemoteFileComparator implements Comparator<RemoteFile> {\n\n\t@Override\n\tpublic int compare(RemoteFile lhs, RemoteFile rhs) {\n\t\tCollator collator = Collator.getInstance();\n\t\treturn collator.compare(lhs.name, rhs.name);\n\t}\n\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/utils/WifiCapabilities.java",
    "content": "/*\n * Copyright © 2014–2016 Michael von Glasow.\n * Portions copyright © 2007, 2012 The Android Open Source Project\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat.utils;\n\nimport java.io.IOException;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\n\nimport android.net.wifi.ScanResult;\nimport android.util.Log;\n\npublic abstract class WifiCapabilities {\n\n    // Constants used for different security types\n    public static final String PSK = \"PSK\";\n    public static final String WEP = \"WEP\";\n    public static final String EAP = \"EAP\";\n    public static final String OPEN = \"Open\";\n\n    public static final String[] EAP_METHOD = { \"PEAP\", \"TLS\", \"TTLS\" };\n\n    /** String present in capabilities if the scan result is ad-hoc */\n    private static final String ADHOC_CAPABILITY = \"[IBSS]\";\n    /** String present in capabilities if the scan result is enterprise secured */\n    private static final String ENTERPRISE_CAPABILITY = \"-EAP-\";\n\n    public static final String BSSID_ANY = \"any\";\n    public static final int NETWORK_ID_NOT_SET = -1;\n    /** This should be used with care! */\n    static final int NETWORK_ID_ANY = -2;\n   \n    public static final int MATCH_NONE = 0;\n    public static final int MATCH_WEAK = 1;\n    public static final int MATCH_STRONG = 2;\n    public static final int MATCH_EXACT = 3;\n\n    /* Enterprise Fields */\n    public static final int IDENTITY = 0;\n    public static final int ANONYMOUS_IDENTITY = 1;\n    public static final int CLIENT_CERT = 2;\n    public static final int CA_CERT = 3;\n    public static final int PRIVATE_KEY = 4;\n    public static final int MAX_ENTRPRISE_FIELD = 5;\n    \n    public static final String CAPTIVE_PORTAL_SERVER = \"clients3.google.com\";\n    private static final int SOCKET_TIMEOUT_MS = 10000;\n    \n    public static final int NETWORK_AVAILABLE = 0;\n    public static final int NETWORK_CAPTIVE_PORTAL = 1;\n    public static final int NETWORK_ERROR = 2;\n    \n    /**\n     * @return The security of a given {@link ScanResult}.\n     */\n    public static String getScanResultSecurity(ScanResult scanResult) {\n        final String cap = scanResult.capabilities;\n        final String[] securityModes = { WEP, PSK, EAP };\n        for (int i = securityModes.length - 1; i >= 0; i--) {\n            if (cap.contains(securityModes[i])) {\n                return securityModes[i];\n            }\n        }\n       \n        return OPEN;\n    }\n   \n    /**\n     * @return Whether the given ScanResult represents an adhoc network.\n     */\n    public static boolean isAdhoc(ScanResult scanResult) {\n        return scanResult.capabilities.contains(ADHOC_CAPABILITY);\n    }\n   \n    /**\n     * @return Whether the given ScanResult has enterprise security.\n     */\n    public static boolean isEnterprise(ScanResult scanResult) {\n        return scanResult.capabilities.contains(ENTERPRISE_CAPABILITY);\n    }\n    \n    /**\n     * Checks if an unrestricted Internet connection is available.\n     * \n     * This method detects captive portals (also known as walled gardens),\n     * which redirect Web traffic to a sign-in page as long as the user has not\n     * provided any credentials. It does so by connecting to a particular\n     * Web address, which will respond with HTTP status code 204 (no content)\n     * and an empty result body. If a different response (such as a document\n     * or redirection) is obtained, it will assume the presence of a captive\n     * portal.\n     * \n     * Once the user has signed into a captive portal, it will not be reported\n     * as such, provided the portal grants transparent Internet access to\n     * signed-in users.\n     * \n     * Since this method involves a network operation, it cannot be called from\n     * the main UI thread. Consider instead creating an {@link AsyncTask}\n     * around it.\n     * @return NETWORK_AVAILABLE if we have unrestricted network access,\n     * NETWORK_CAPTIVE_PORTAL if we are behind a captive portal or\n     * NETWORK_ERROR if a network error occurred during the check, which\n     * happens if no network is available.\n     */\n    public static int getNetworkConnectivity() {\n        HttpURLConnection urlConnection = null;\n\n        String mUrl = \"http://\" + CAPTIVE_PORTAL_SERVER + \"/generate_204\";\n        Log.d(WifiCapabilities.class.getSimpleName(), \"Checking \" + mUrl + \" to see if we're behind a captive portal\");\n        try {\n            URL url = new URL(mUrl);\n            urlConnection = (HttpURLConnection) url.openConnection();\n            urlConnection.setInstanceFollowRedirects(false);\n            urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);\n            urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);\n            urlConnection.setUseCaches(false);\n            urlConnection.getInputStream();\n            // we got a valid response, but not from the real google\n            return (urlConnection.getResponseCode() != 204)?NETWORK_CAPTIVE_PORTAL:NETWORK_AVAILABLE;\n        } catch (IOException e) {\n            Log.d(WifiCapabilities.class.getSimpleName(), \"Probably not a portal: exception \" + e);\n            return NETWORK_ERROR;\n        } finally {\n            if (urlConnection != null) {\n                urlConnection.disconnect();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/utils/WifiScanResultComparator.java",
    "content": "package com.vonglasow.michael.satstat.utils;\n\nimport java.util.Comparator;\n\nimport android.net.wifi.ScanResult;\n\n/**\n * A comparator for {@link android.net.wifi.ScanResult}.\n * \n * The criterion by which this comparator will perform comparison can be altered at runtime.\n * However, if two values are equal by that criterion, the {@code bssid} member will always be used\n * as a secondary criterion. Values will be reported as being equal only if they are equal by both\n * criteria. The initial criterion is {@link #WIFI_SORT_BSSID}.\n */\npublic class WifiScanResultComparator implements Comparator<ScanResult> {\n\t/**\n\t * Sort WiFis by BSSID, ascending order\n\t */\n\tpublic static final int WIFI_SORT_BSSID = 0;\n\n\t/**\n\t * Sort WiFis by name (SSID), alphabetically and case-insensitive\n\t */\n\tpublic static final int WIFI_SORT_SSID = 1;\n\n\t/**\n\t * Sort WiFis by channel (frequency), ascending\n\t */\n\tpublic static final int WIFI_SORT_FREQUENCY = 2;\n\n\t/**\n\t * Sort WiFis by signal strength (level), strongest first\n\t */\n\tpublic static final int WIFI_SORT_LEVEL = 3;\n\n\t// The criterion to use for comparison, initially set to BSSID (fallback)\n\tprivate int criterion = WIFI_SORT_BSSID;\n\n\n\t/**\n\t * Compares two ScanResults.\n\t * \n\t * Comparison is performed using the previously selected criterion. The BSSID is used as a secondary\n\t * criterion if both {@code lhs} and {code lhs} are equal by the chosen criterion.\n\t * \n\t * @return A negative value if {@code lhs < rhs}, a positive value if {@code lhs > rhs}, or zero if\n\t * both are equal.\n\t */\n\t@Override\n\tpublic int compare(ScanResult lhs, ScanResult rhs) {\n\t\tswitch(criterion) {\n\t\tcase WIFI_SORT_SSID:\n\t\t\tint temp = lhs.SSID.compareToIgnoreCase(rhs.SSID);\n\t\t\tif (temp != 0)\n\t\t\t\treturn temp;\n\t\t\tbreak;\n\t\tcase WIFI_SORT_FREQUENCY:\n\t\t\tif (lhs.frequency < rhs.frequency)\n\t\t\t\treturn -1;\n\t\t\telse if (lhs.frequency > rhs.frequency)\n\t\t\t\treturn 1;\n\t\t\tbreak;\n\t\tcase WIFI_SORT_LEVEL:\n\t\t\tif (lhs.level > rhs.level)\n\t\t\t\treturn -1;\n\t\t\telse if (lhs.level < rhs.level)\n\t\t\t\treturn 1;\n\t\t\tbreak;\n\t\t}\n\t\treturn lhs.BSSID.compareToIgnoreCase(rhs.BSSID);\n\t}\n\n\n\t/**\n\t * Sets the criterion for comparison.\n\t * \n\t * If two values are equal by that criterion, the {@code bssid} member will always be used as a\n\t * secondary criterion. Values will be reported as being equal only if they are equal by both\n\t * criteria.\n\t * \n\t * @param newCriterion\n\t */\n\tpublic void setCriterion(int newCriterion) {\n\t\tcriterion = newCriterion;\n\t}\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/widgets/GpsSnrView.java",
    "content": "/*\n * Copyright © 2013–2016 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat.widgets;\n\nimport com.vonglasow.michael.satstat.R;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.Path;\nimport android.location.GpsSatellite;\nimport android.util.AttributeSet;\nimport android.util.DisplayMetrics;\nimport android.util.Log;\nimport android.view.View;\n\n/**\n * Displays the signal-to-noise ratio of the GPS satellites in a bar chart.\n */\npublic class GpsSnrView extends View {\n\tprivate final String TAG = \"GpsSnrView\";\n\t\n\t/**\n\t * The highest currently supported NMEA ID.\n\t */\n\tprivate final int MAX_NMEA_ID = 336;\n\n\tprivate Iterable<GpsSatellite> mSats;\n\n\tprivate Paint activePaint;\n\tprivate Paint inactivePaint;\n\tprivate Paint gridPaint;\n\tprivate Paint gridPaintStrong;\n\tprivate Paint gridPaintNone;\n\tprivate Paint labelPaint;\n\n\t// Stroke width for grid lines\n\tprivate int gridStrokeWidth;\n\t\n\t// Display density\n\tprivate float density;\n\t\n\t// Effective height of label text in pixels\n\tprivate int textHeight;\n\t\n\t// Preferred height of the view in pixels so that labels and legible bars can be accommodated\n\tprivate int preferredHeight;\n\n\t/*\n\t * Which satellites to draw:\n\t * 1–32: GPS\n\t * 33–54: Various SBAS systems (EGNOS, WAAS, SDCM, GAGAN, MSAS) – some IDs still unused\n\t * 55–64: not used (might be assigned to further SBAS systems)\n\t * 65–88: GLONASS\n\t * 89–96: GLONASS (future extensions?)\n\t * 97–192: not used\n\t * 193–195: QZSS\n\t * 196–200: QZSS (future extensions?)\n\t * 201–235: Beidou\n\t * 236–300: not used\n\t * 301–336: Galileo\n\t */\n\tprivate boolean draw_1_32 = false;\n\tprivate boolean draw_33_54 = false;\n\tprivate boolean draw_55_64 = false;\n\tprivate boolean draw_65_88 = false;\n\tprivate boolean draw_89_96 = false;\n\tprivate boolean draw_97_192 = false;\n\tprivate boolean draw_193_195 = false;\n\tprivate boolean draw_196_200 = false;\n\tprivate boolean draw_201_235 = false;\n\tprivate boolean draw_236_300 = false;\n\tprivate boolean draw_301_336 = false;\n\n\n\t/**\n\t * @param context\n\t */\n\tpublic GpsSnrView(Context context) {\n\t\tsuper(context);\n\t\tdoInit(context);\n\t}\n\n\t/**\n\t * @param context\n\t * @param attrs\n\t */\n\tpublic GpsSnrView(Context context, AttributeSet attrs) {\n\t\tsuper(context, attrs);\n\t\tdoInit(context);\n\t}\n\n\t/**\n\t * @param context\n\t * @param attrs\n\t * @param defStyle\n\t */\n\tpublic GpsSnrView(Context context, AttributeSet attrs, int defStyle) {\n\t\tsuper(context, attrs, defStyle);\n\t\tdoInit(context);\n\t}\n\n\tprivate void doInit(Context context) {\n\t\tDisplayMetrics metrics = context.getResources().getDisplayMetrics();\n\t\tdensity = metrics.density;\n\t\t\n\t\tgridStrokeWidth = Math.max(1, (int) (density));\n\t\t\n\t\tactivePaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n\t\tactivePaint.setColor(Color.parseColor(\"#FF80CBC4\")); // Teal 200\n\t\tactivePaint.setStyle(Paint.Style.FILL);\n\n\t\tinactivePaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n\t\tinactivePaint.setColor(Color.parseColor(\"#FFF44336\")); // Red 500\n\t\tinactivePaint.setStyle(Paint.Style.FILL);\n\n\t\tgridPaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n\t\tgridPaint.setColor(Color.parseColor(\"#FF424242\")); // Gray 800\n\t\tgridPaint.setStyle(Paint.Style.STROKE);\n\t\tgridPaint.setStrokeWidth(gridStrokeWidth);\n\n\t\tgridPaintStrong = new Paint(gridPaint);\n\t\tgridPaintStrong.setColor(Color.parseColor(\"#FFFFFFFF\"));\n\n\t\tgridPaintNone = new Paint(gridPaint);\n\t\tgridPaintNone.setColor(Color.parseColor(\"#00000000\"));\n\t\t\n\t\t// FIXME style text properly\n\t\tlabelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n\t\tlabelPaint.setStyle(Paint.Style.FILL);\n\t\tlabelPaint.setTextAlign(Paint.Align.CENTER);\n\t\tlabelPaint.setColor(context.getResources().getColor(R.color.secondary_text_default_material_dark));\n\t\tlabelPaint.setTextSize(context.getResources().getDimensionPixelSize(R.dimen.abc_text_size_small_material));\n\t\t\n\t\t/*\n\t\tint ap = R.style.TextAppearance_AppCompat_Medium;\n\t\tTypedArray appearance = null;\n\t\tappearance = context.getTheme().obtainStyledAttributes(ap, R.styleable.AppCompatTextView);\n\t\tif (appearance != null) {\n\t\t\tint n = appearance.getIndexCount();\n\t\t\tfor (int i = 0; i < n; i++) {\n\t\t\t\tint attr = appearance.getIndex(i);\n\t\t\t\t\n\t\t\t\tswitch (attr) {\n\t\t\t\tcase R.styleable.TextAppearance_android_textColor:\n\t\t\t\t\tlabelPaint.setColor(appearance.getColor(attr, labelPaint.getColor()));\n\t\t\t\t\tbreak;\n\t\t\t\t\t\n\t\t\t\tcase R.styleable.TextAppearance_android_textSize:\n\t\t\t\t\t//labelPaint.setTextSize(appearance.getDimensionPixelSize(attr, (int) labelPaint.getTextSize()));\n\t\t\t\t\tbreak;\n\t\t\t\t\t\n\t\t\t\tcase R.styleable.TextAppearance_android_typeface:\n\t\t\t\t\t//labelPaint.setTypeface(); // typefaceIndex = appearance.getInt(attr, -1);\n\t\t\t\t\tbreak;\n\t\t\t\t\t\n\t\t\t\tcase R.styleable.TextAppearance_android_shadowColor:\n\t\t\t\tcase R.styleable.TextAppearance_android_shadowDx:\n\t\t\t\tcase R.styleable.TextAppearance_android_shadowDy:\n\t\t\t\tcase R.styleable.TextAppearance_android_shadowRadius:\n\t\t\t\t\t// not yet implemented\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tappearance.recycle();\n\t\t}\n\t\t*/\n\n\t\t/*\n\t\t * Get the total height of the text. Note that this is not the same as getTextSize/setTextSize.\n\t\t * Also note that the ascent is negative and descent is positive, hence descent - ascent will give us\n\t\t * absolute text height (a positive number).\n\t\t */\n\t\ttextHeight = (int) Math.ceil(labelPaint.descent() - labelPaint.ascent());\n\t\t\n\t\t/*\n\t\t * Height should be the same as two rows of small text plus a row of medium text. This is a\n\t\t * rough approximation based on text sizes and the ratio between text size and actual height.\n\t\t */\n\t\tpreferredHeight = (int) (\n\t\t\t\t(2 * labelPaint.getTextSize() + context.getResources().getDimensionPixelSize(R.dimen.abc_text_size_medium_material))\n\t\t\t\t* textHeight / labelPaint.getTextSize()\n\t\t\t\t);\n\t}\n\n\t/**\n\t * Draws the grid lines and labels.\n\t */\n\tprivate void drawGrid(Canvas canvas) {\n\t\t//don't use Canvas.getWidth() and Canvas.getHeight() here, they may return incorrect values\n\t\tint w = getWidth();\n\t\tint h = getHeight();\n\n\t\t// left boundary\n\t\tcanvas.drawLine((float) gridStrokeWidth / 2, 0,\n\t\t\t\t(float) gridStrokeWidth / 2, h - textHeight, gridPaintStrong);\n\t\t\n\t\tint numBars = getNumBars();\n\t\t\n\t\tif (draw_1_32)\n\t\t\tdrawLabel(canvas, getContext().getResources().getString(R.string.title_nmea_001_032), 1, 32, numBars);\n\t\tif (draw_33_54)\n\t\t\tdrawLabel(canvas, getContext().getResources().getString(R.string.title_nmea_033_054), 33, 22, numBars);\n\t\tif (draw_55_64)\n\t\t\tdrawLabel(canvas, getContext().getResources().getString(R.string.title_nmea_055_064), 55, 10, numBars);\n\t\t\n\t\t// 65–88 is GLONASS, 89–96 is for possible future GLONASS extensions\n\t\tif (draw_65_88 && draw_89_96)\n\t\t\tdrawLabel(canvas, getContext().getResources().getString(R.string.title_nmea_065_088), 65, 32, numBars);\n\t\telse if (draw_65_88)\n\t\t\tdrawLabel(canvas, getContext().getResources().getString(R.string.title_nmea_065_088), 65, 24, numBars);\n\t\telse if (draw_89_96)\n\t\t\tdrawLabel(canvas, getContext().getResources().getString(R.string.title_nmea_065_088), 89, 8, numBars);\n\t\t\n\t\tif (draw_97_192)\n\t\t\tdrawLabel(canvas, getContext().getResources().getString(R.string.title_nmea_097_192), 97, 96, numBars);\n\t\t\n\t\t// 193–195 is QZSS, 196–200 is for possible future QZSS extensions\n\t\tif (draw_193_195 && draw_196_200)\n\t\t\tdrawLabel(canvas, getContext().getResources().getString(R.string.title_nmea_193_195), 193, 8, numBars);\n\t\telse if (draw_193_195)\n\t\t\tdrawLabel(canvas, getContext().getResources().getString(R.string.title_nmea_193_195), 193, 3, numBars);\n\t\telse if (draw_196_200)\n\t\t\tdrawLabel(canvas, getContext().getResources().getString(R.string.title_nmea_193_195), 196, 5, numBars);\n\t\t\n\t\tif (draw_201_235)\n\t\t\tdrawLabel(canvas, getContext().getResources().getString(R.string.title_nmea_201_235), 201, 35, numBars);\n\n\t\t// 236–300 (currently unused)\n\t\tif (draw_236_300)\n\t\t\tdrawLabel(canvas, getContext().getResources().getString(R.string.title_nmea_236_300), 236, 65, numBars);\n\n\t\t// 301–336 is Galileo\n\t\tif (draw_301_336)\n\t\t\tdrawLabel(canvas, getContext().getResources().getString(R.string.title_nmea_301_336), 301, 36, numBars);\n\n\t\t// range boundaries and auxiliary lines (after every 4th satellite)\n\t\tfor (int nmeaID = 1; nmeaID < MAX_NMEA_ID; nmeaID++) {\n\t\t\tint pos = getGridPos(nmeaID);\n\t\t\tif (pos > 0) {\n\t\t\t\tfloat x = (float) gridStrokeWidth / 2\n\t\t\t\t\t\t+ pos * (w - gridStrokeWidth) / numBars;\n\t\t\t\tPaint paint = gridPaintNone;\n\t\t\t\tswitch(nmeaID) {\n\t\t\t\tcase 32:\n\t\t\t\tcase 64:\n\t\t\t\tcase 96:\n\t\t\t\tcase 192:\n\t\t\t\tcase 200:\n\t\t\t\tcase 235:\n\t\t\t\tcase 300:\n\t\t\t\tcase 336:\n\t\t\t\t\tpaint = gridPaintStrong;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 54:\n\t\t\t\t\tif (!draw_55_64)\n\t\t\t\t\t\tpaint = gridPaintStrong;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 88:\n\t\t\t\t\tif (!draw_89_96)\n\t\t\t\t\t\tpaint = gridPaintStrong;\n\t\t\t\t\telse\n\t\t\t\t\t\tpaint = gridPaint;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 195:\n\t\t\t\t\tif (!draw_196_200)\n\t\t\t\t\t\tpaint = gridPaintStrong;\n\t\t\t\tdefault:\n\t\t\t\t\tif ((nmeaID % 4) == 0)\n\t\t\t\t\t\tpaint = gridPaint;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcanvas.drawLine(x, 0, x, h - textHeight, paint);\n\t\t\t}\n\t\t}\n\t\t\n\t\t// right boundary\n\t\tcanvas.drawLine(w - (float) gridStrokeWidth / 2, h - textHeight,\n\t\t\t\tw - (float) gridStrokeWidth / 2, 0, gridPaintStrong);\n\t\t\n\t\t// bottom line\n\t\tcanvas.drawLine(0, h - textHeight - (float) gridStrokeWidth / 2,\n\t\t\t\tw, h - textHeight - (float) gridStrokeWidth / 2, gridPaintStrong);\n\t}\n\t\n\t/**\n\t * Draws the label for a satellite range.\n\t * \n\t * @param canvas The {@code Canvas} on which the SNR view will appear.\n\t * @param label The text to be displayed (the description of the satellite range, such as \"GPS\", \"GLONASS\" or \"Beidou\")\n\t * @param startBar The NMEA ID of the first satellite in the range\n\t * @param rangeBars The number of NMEA IDs in the range (ranges must be contiguous)\n\t * @param numBars Total number of SNR bars being displayed, as returned by getNumBars()\n\t */\n\tprivate void drawLabel(Canvas canvas, String label, int startBar, int rangeBars, int numBars) {\n\t\tint offsetBars = getGridPos(startBar) - 1;\n\t\tint w = getWidth();\n\t\tint h = getHeight();\n\t\tPath labelPath = new Path();\n\n\t\tlabelPath.reset();\n\t\tlabelPath.moveTo(gridStrokeWidth + offsetBars * (w - gridStrokeWidth) / numBars, h);\n\t\tlabelPath.rLineTo(rangeBars * (w - gridStrokeWidth) / numBars - gridStrokeWidth, 0);\n\t\tcanvas.drawTextOnPath(label, labelPath, 0, -labelPaint.descent(), labelPaint);\n\t}\n\t\n\t/**\n\t * Draws the SNR bar for a satellite.\n\t * \n\t * @param canvas The {@code Canvas} on which the SNR view will appear.\n\t * @param nmeaID The NMEA ID of the satellite, as returned by {@link android.location.GpsSatellite#getPrn()}.\n\t * @param snr The signal-to-noise ratio (SNR) for the satellite.\n\t * @param used Whether the satellite is used in the fix.\n\t */\n\tprivate void drawSat(Canvas canvas, int nmeaID, float snr, boolean used) {\n\t\tint w = getWidth();\n\t\tint h = getHeight() - textHeight;\n\n\t\tint i = getGridPos(nmeaID);\n\n\t\tint x0 = (i - 1) * (w - gridStrokeWidth) / getNumBars() + gridStrokeWidth / 2;\n\t\tint x1 = i * (w - gridStrokeWidth) / getNumBars() - gridStrokeWidth / 2;\n\n\t\tint y0 = h - gridStrokeWidth;\n\t\tint y1 = (int) (y0 * (1 - Math.min(snr, 60) / 60));\n\n\t\tcanvas.drawRect(x0, y1, x1, h, used?activePaint:inactivePaint);\n\t}\n\n\t/**\n\t * Returns the position of the SNR bar for a satellite in the grid.\n\t * <p>\n\t * This function returns the position at which the SNR bar for the\n\t * satellite with the given {@code nmeaID} will appear in the grid, taking\n\t * into account the visibility of NMEA ID ranges.\n\t * \n\t * @param nmeaID The NMEA ID of the satellite, as returned by {@link android.location.GpsSatellite#getPrn()}.\n\t * @return The position of the SNR bar in the grid. The position of the first visible bar is 1. If {@code nmeaID} falls within a hidden range, -1 is returned. \n\t */\n\tprivate int getGridPos(int nmeaID) {\n\t\tif (nmeaID < 1) return -1;\n\t\t\n\t\tint skip = 0;\n\t\tif (nmeaID > 32) {\n\t\t\tif (!draw_1_32) skip+=32;\n\t\t\tif (nmeaID > 54) {\n\t\t\t\tif (!draw_33_54) skip+=22;\n\t\t\t\tif (nmeaID > 64) {\n\t\t\t\t\tif (!draw_55_64) skip+=10;\n\t\t\t\t\tif (nmeaID > 88) {\n\t\t\t\t\t\tif (!draw_65_88) skip+=24;\n\t\t\t\t\t\tif (nmeaID > 96) {\n\t\t\t\t\t\t\tif (!draw_89_96) skip+=8;\n\t\t\t\t\t\t\tif (nmeaID > 192) {\n\t\t\t\t\t\t\t\tif (!draw_97_192) skip+=96;\n\t\t\t\t\t\t\t\tif (nmeaID > 195) {\n\t\t\t\t\t\t\t\t\tif (!draw_193_195) skip+=3;\n\t\t\t\t\t\t\t\t\tif (nmeaID > 200) {\n\t\t\t\t\t\t\t\t\t\tif (!draw_196_200) skip+=5;\n\t\t\t\t\t\t\t\t\t\tif (nmeaID > 235) {\n\t\t\t\t\t\t\t\t\t\t\tif (!draw_201_235) skip+=35;\n\t\t\t\t\t\t\t\t\t\t\tif (nmeaID > 300) {\n\t\t\t\t\t\t\t\t\t\t\t\tif (nmeaID > MAX_NMEA_ID) return -1;\n\t\t\t\t\t\t\t\t\t\t\t\telse if (!draw_301_336) return -1;\n\t\t\t\t\t\t\t\t\t\t\t\telse if (!draw_236_300) skip+=65;\n\t\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t\t// 235 < nmeaID <= 300\n\t\t\t\t\t\t\t\t\t\t\t\tif (!draw_236_300) return -1;\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\t// 200 < nmeaID <= 235\n\t\t\t\t\t\t\t\t\t\t\tif (!draw_201_235) return -1;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t// 195 < nmeaID <= 200\n\t\t\t\t\t\t\t\t\t\tif (!draw_196_200) return -1;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t// 192 < nmeaID <= 195\n\t\t\t\t\t\t\t\t\tif (!draw_193_195) return -1;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// 96 < nmeaID <= 192\n\t\t\t\t\t\t\t\tif (!draw_97_192) return -1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// 88 < nmeaID <= 96\n\t\t\t\t\t\t\tif (!draw_89_96) return -1;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// 64 < nmeaID <= 88\n\t\t\t\t\t\tif (!draw_65_88) return -1;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// 54 < nmeaID <= 64\n\t\t\t\t\tif (!draw_55_64) return -1;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// 32 < nmeaID <= 54\n\t\t\t\tif (!draw_33_54) return -1;\n\t\t\t}\n\t\t} else {\n\t\t\t// nmeaID <= 32\n\t\t\tif (!draw_1_32) return -1;\n\t\t}\n\t\t\n\t\treturn nmeaID - skip;\n\t}\n\t\n\t/**\n\t * Returns the number of SNR bars to draw\n\t * \n\t * The number of bars to draw varies depending on the systems supported by the device. Common\n\t * numbers are 32 for a GPS-only receiver, 56 for a combined GPS/GLONASS receiver or 91 for a\n\t * combined GPS/GLONASS/Beidou receiver. Another 36 bars are needed for Galileo; some receivers\n\t * require additional bars for regional GNSS or assistance systems.\n\t * \n\t * @return The number of bars to draw\n\t */\n\tprivate int getNumBars() {\n\t\treturn (draw_1_32 ? 32 : 0) \n\t\t\t\t+ (draw_33_54 ? 22 : 0)\n\t\t\t\t+ (draw_55_64 ? 10 : 0)\n\t\t\t\t+ (draw_65_88 ? 24 : 0)\n\t\t\t\t+ (draw_89_96 ? 8 : 0)\n\t\t\t\t+ (draw_97_192 ? 96 : 0)\n\t\t\t\t+ (draw_193_195 ? 3 : 0)\n\t\t\t\t+ (draw_196_200 ? 5 : 0)\n\t\t\t\t+ (draw_201_235 ? 35 : 0)\n\t\t\t\t+ (draw_236_300 ? 65 : 0)\n\t\t\t\t+ (draw_301_336 ? 36 : 0);\n\t}\n\n\t/**\n\t * Initializes the SNR grid.\n\t * <p>\n\t * This method iterates through {@link #mSats} to determine which ranges of\n\t * NMEA IDs will be drawn. \n\t */\n\tprotected void initializeGrid() {\n\t\t// iterate through list to find out how many bars to draw\n\t\tif (mSats != null)\n\t\t\tfor (GpsSatellite sat : mSats) {\n\t\t\t\tint prn = sat.getPrn();\n\t\t\t\tif (prn < 1) {\n\t\t\t\t\tLog.wtf(TAG, String.format(\"Got satellite with invalid NMEA ID %d\", prn));\n\t\t\t\t} else if (prn <= 32) {\n\t\t\t\t\tdraw_1_32 = true;\n\t\t\t\t} else if (prn <= 54) {\n\t\t\t\t\tdraw_33_54 = true;\n\t\t\t\t} else if (prn <= 64) {\n\t\t\t\t\t// most likely an extended SBAS range, display the lower range, too\n\t\t\t\t\tdraw_33_54 = true;\n\t\t\t\t\tdraw_55_64 = true;\n\t\t\t\t} else if (prn <= 88) {\n\t\t\t\t\tdraw_65_88 = true;\n\t\t\t\t} else if (prn <= 96) {\n\t\t\t\t\t// most likely an extended GLONASS range, display the lower range, too\n\t\t\t\t\tdraw_65_88 = true;\n\t\t\t\t\tdraw_89_96 = true;\n\t\t\t\t} else if (prn <= 192) {\n\t\t\t\t\tdraw_97_192 = true; // TODO: do we really want to enable this huge 96-sat block?\n\t\t\t\t\tLog.w(TAG, String.format(\"Got satellite with NMEA ID %d (from the huge unassigned 97-192 range)\", prn));\n\t\t\t\t} else if (prn <= 195) {\n\t\t\t\t\tdraw_193_195 = true;\n\t\t\t\t} else if (prn <= 200) {\n\t\t\t\t\t// most likely an extended QZSS range, display the lower range, too\n\t\t\t\t\tdraw_193_195 = true;\n\t\t\t\t\tdraw_196_200 = true;\n\t\t\t\t} else if (prn <= 235) {\n\t\t\t\t\tdraw_201_235 = true;\n\t\t\t\t} else if (prn <= 300) {\n\t\t\t\t\tdraw_236_300 = true; // TODO: same as above, do we really want to enable this?\n\t\t\t\t} else if (prn <= 336) {\n\t\t\t\t\tdraw_301_336 = true;\n\t\t\t\t} else {\n\t\t\t\t\tLog.w(TAG, String.format(\"Got satellite with NMEA ID %d, possibly unsupported system\", prn));\n\t\t\t\t}\n\t\t\t}\n\t\t/*\n\t\t * If we didn't get any valid ranges, display at least the GPS range.\n\t\t * No need to check for extended ranges here - if they get drawn, so\n\t\t * will their corresponding base range.\n\t\t */\n\t\tif (!(draw_1_32 || draw_33_54 || draw_65_88 || draw_97_192 || draw_193_195 || draw_201_235\n\t\t\t\t|| draw_236_300 || draw_301_336))\n\t\t\tdraw_1_32 = true;\n\t}\n\t\n\t/**\n\t * Redraws the SNR view.\n\t * <p>\n\t * This method is called whenever the view needs to be redrawn. Besides the\n\t * usual cases of view creation/recreation, this also occurs when the\n\t * {@link #showSats(Iterable)} has been called to indicate new SNR data is\n\t * available.\n\t */\n\t@Override\n\tprotected void onDraw(Canvas canvas) {\n\t\tinitializeGrid();\n\t\t\n\t\t// draw the SNR bars\n\t\tif (mSats != null)\n\t\t\tfor (GpsSatellite sat : mSats)\n\t\t\t\tdrawSat(canvas, sat.getPrn(), sat.getSnr(), sat.usedInFix());\n\t\t\n\t\t// draw the grid on top\n\t\tdrawGrid(canvas);\n\t}\n\n\t@Override\n\tprotected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {\n\t\tsetMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), preferredHeight);\n\t}\n\n\t/**\n\t * Refreshes the SNR view with current data.\n\t * <p>\n\t * Call this method when new SNR data is available. It will update the SNR\n\t * view's internal list of {@code GpsSatellite}s and trigger a redraw.\n\t * \n\t * @param sats A list of satellites currently in view.\n\t */\n\tpublic void showSats(Iterable<GpsSatellite> sats) {\n\t\tmSats = sats;\n\t\tinvalidate();\n\t}\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/widgets/GpsStatusView.java",
    "content": "/*\n * Copyright © 2013–2016 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat.widgets;\n\nimport com.vonglasow.michael.satstat.R;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.Path;\nimport android.location.GpsSatellite;\nimport android.util.AttributeSet;\nimport android.util.DisplayMetrics;\n\npublic class GpsStatusView extends SquareView {\n\tprivate float mYaw = 0;\n\tprivate float mRotation = 0;\n\tprivate int mW = 0;\n\tprivate int mH = 0;\n\tprivate Iterable<GpsSatellite> mSats;\n\t\n\tprivate Paint activePaint;\n\tprivate Paint inactivePaint;\n\tprivate Paint northPaint;\n\tprivate Paint gridPaint;\n\tprivate Paint gridBorderPaint;\n\tprivate Paint labelPaint;\n\tprivate Path northArrow = new Path();\n\tprivate Path labelPathN = new Path();\n\tprivate Path labelPathE = new Path();\n\tprivate Path labelPathS = new Path();\n\tprivate Path labelPathW = new Path();\n\n\t\n\tprivate int gridStrokeWidth;\n\tprivate float snrScale;\n\tprivate float density;\n\t\n\t// Compensation for display rotation. Use Surface.ROTATION_* as index (0, 90, 180, 270 deg).\n\t@SuppressWarnings(\"boxing\")\n\tprivate final static Integer zeroYaw[] = {0, 90, 180, 270};\n\t\n\tpublic GpsStatusView(Context context) {\n\t\tsuper(context);\n\t\tdoInit(context);\n\t}\n\n\tpublic GpsStatusView(Context context, AttributeSet attrs) {\n\t\tsuper(context, attrs);\n\t\tdoInit(context);\n\t}\n\t\n\tpublic GpsStatusView(Context context, AttributeSet attrs, int defStyle) {\n\t\tsuper(context, attrs, defStyle);\n\t\tdoInit(context);\n\t}\n\t\n\tprivate void doInit(Context context) {\n\t\tDisplayMetrics metrics = context.getResources().getDisplayMetrics();\n\t\tdensity = metrics.density;\n\t\tsnrScale = 0.2f * density;\n\t\tgridStrokeWidth = Math.max(1, (int) (density));\n\t\t\n\t\tactivePaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n\t\tactivePaint.setColor(Color.parseColor(\"#FF80CBC4\")); // Teal 200\n\t\tactivePaint.setStyle(Paint.Style.FILL);\n\t\t\n\t\tinactivePaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n\t\tinactivePaint.setColor(Color.parseColor(\"#FFF44336\")); // Red 500\n\t\tinactivePaint.setStyle(Paint.Style.FILL);\n\t\t\n\t\tgridPaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n\t\tgridPaint.setColor(Color.parseColor(\"#FFFF9800\")); // Orange 500\n\t\tgridPaint.setStyle(Paint.Style.STROKE);\n\t\tgridPaint.setStrokeWidth(gridStrokeWidth);\n\t\t\n\t\tgridBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n\t\tgridBorderPaint.setColor(Color.parseColor(\"#50FF9800\")); // Orange 500 @ 30%\n\t\tgridBorderPaint.setStyle(Paint.Style.STROKE);\n\t\t\n\t\tnorthPaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n\t\tnorthPaint.setColor(Color.parseColor(\"#FFF44336\")); // Red 500\n\t\tnorthPaint.setStyle(Paint.Style.FILL);\n\t\t\n\t\tlabelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n\t\tlabelPaint.setColor(Color.parseColor(\"#FFFF9800\")); // Orange 500\n\t\tlabelPaint.setStyle(Paint.Style.FILL);\n\t\tlabelPaint.setTextAlign(Paint.Align.CENTER);\n\t}\n\t\n\t/*\n\t * Draws a satellite in the sky grid.\n\t */\n\tprivate void drawSat(Canvas canvas, int prn, float azimuth, float elevation, float snr, boolean used) {\n\n\t\tfloat r = (90 - elevation) * mW * 0.9f / 200;\n\t\tfloat x = (float) (r * Math.sin(azimuth * Math.PI / 180));\n\t\tfloat y = (float) -(r * Math.cos(azimuth * Math.PI / 180));\n\t\t\n\t\tcanvas.drawCircle(x, y, snr * snrScale, used?activePaint:inactivePaint);\n\t}\n\t\n\t@Override\n\tprotected void onDraw(Canvas canvas) {\n\t\tint cx = mW / 2;\n\t\tint cy = mH / 2;\n\n\t\t//Log.d(\"GpsStatusView\", String.format(\"Drawing on a %dx%d canvas\", w, h));\n\n\t\tcanvas.translate(cx, cy);\n\t\tcanvas.rotate(-mRotation);\n\t\t\n\t\tcanvas.drawCircle(0, 0, mW * 0.37125f, gridBorderPaint);\n\t\t\n\t\tcanvas.drawLine(-mW * 0.405f, 0, mW * 0.405f, 0, gridPaint);\n\t\tcanvas.drawLine(0, -mH * 0.405f, 0, mH * 0.405f, gridPaint);\n\t\t\n\t\tcanvas.drawCircle(0,  0,  mW * 0.405f, gridPaint);\n\t\tcanvas.drawCircle(0,  0,  mW * 0.27f, gridPaint);\n\t\tcanvas.drawCircle(0,  0,  mW * 0.135f, gridPaint);\n\t\t\n\t\tcanvas.drawPath(northArrow, northPaint);\n\t\t\n\t\tcanvas.drawTextOnPath(((Activity) getContext()).getString(R.string.value_N),\n\t\t\t\tlabelPathN, 0, -labelPaint.descent(), labelPaint);\n\n\t\tcanvas.drawTextOnPath(((Activity) getContext()).getString(R.string.value_S),\n\t\t\t\tlabelPathS, 0, -labelPaint.descent(), labelPaint);\n\n\t\tcanvas.drawTextOnPath(((Activity) getContext()).getString(R.string.value_E),\n\t\t\t\tlabelPathE, 0, -labelPaint.descent(), labelPaint);\n\n\t\tcanvas.drawTextOnPath(((Activity) getContext()).getString(R.string.value_W),\n\t\t\t\tlabelPathW, 0, -labelPaint.descent(), labelPaint);\n\t\t\n\t\tif (mSats != null) {\n\t\t\tfor (GpsSatellite sat : mSats) {\n\t\t\t\tdrawSat(canvas, sat.getPrn(), sat.getAzimuth(), sat.getElevation(), sat.getSnr(), sat.usedInFix());\n\t\t\t}\n\t\t}\n\t}\n\t\n\t@Override\n\tprotected void onSizeChanged (int w, int h, int oldw, int oldh) {\n\t\tmW = w;\n\t\tmH = h;\n\t\trefreshGeometries();\n\t}\n\t\n\tpublic void refreshGeometries() {\n\t\tgridBorderPaint.setStrokeWidth(mW * 0.0625f);\n\t\t\n\t\tfloat arrowWidth = 4 * density;\n\t\t\n\t\tnorthArrow.reset();\n\t\tnorthArrow.moveTo(-arrowWidth, - mH * 0.27f);\n\t\tnorthArrow.lineTo(arrowWidth, - mH * 0.27f);\n\t\tnorthArrow.lineTo(0, - mH * 0.405f - gridStrokeWidth * 2);\n\t\tnorthArrow.close();\n\n\t\tlabelPaint.setTextSize(mH * 0.045f);\n\t\t\n\t\tfloat offsetX = mW * 0.0275f * (float) Math.cos(Math.toRadians(mRotation + 90));\n\t\tfloat offsetY = mW * 0.0275f * (float) Math.sin(Math.toRadians(mRotation + 90));\n\t\tfloat relX = mW * (float) Math.cos(Math.toRadians(mRotation));\n\t\tfloat relY = mH * (float) Math.sin(Math.toRadians(mRotation));\n\t\t\n\t\tlabelPathN.reset();\n\t\tlabelPathN.moveTo(offsetX - relX, - mH * 0.4275f + offsetY - relY);\n\t\tlabelPathN.rLineTo(2 * relX, 2 * relY);\n\t\t\n\t\tlabelPathE.reset();\n\t\tlabelPathE.moveTo(mW * 0.4275f + offsetX - relX, offsetY - relY);\n\t\tlabelPathE.rLineTo(2 * relX, 2 * relY);\n\t\t\n\t\tlabelPathS.reset();\n\t\tlabelPathS.moveTo(offsetX - relX, mH * 0.4275f + offsetY - relY);\n\t\tlabelPathS.rLineTo(2 * relX, 2 * relY);\n\t\t\n\t\tlabelPathW.reset();\n\t\tlabelPathW.moveTo(- mW * 0.4275f + offsetX - relX, offsetY - relY);\n\t\tlabelPathW.rLineTo(2 * relX, 2 * relY);\n\t}\n\t\n\tpublic void setYaw(float yaw) {\n\t\tmYaw = yaw;\n\t\tmRotation = mYaw + zeroYaw[((Activity) getContext()).getWindowManager().getDefaultDisplay().getRotation()];\n\t\trefreshGeometries();\n\t\tinvalidate();\n\t}\n\t\n\tpublic void showSats(Iterable<GpsSatellite> sats) {\n\t\tmSats = sats;\n\t\tinvalidate();\n\t}\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/widgets/LocProviderPreference.java",
    "content": "package com.vonglasow.michael.satstat.widgets;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.vonglasow.michael.satstat.R;\n\nimport android.content.Context;\nimport android.location.LocationManager;\nimport android.preference.MultiSelectListPreference;\nimport android.util.AttributeSet;\n\npublic class LocProviderPreference extends MultiSelectListPreference {\n\n\tprivate Context mContext;\n\n\tpublic LocProviderPreference(Context context) {\n\t\tsuper(context);\n\t\tmContext = context;\n\t\tsetSummary(R.string.pref_loc_prov_summary);\n\t\tupdateProviders();\n\t}\n\n\tpublic LocProviderPreference(Context context, AttributeSet attrs) {\n\t\tsuper(context, attrs);\n\t\tmContext = context;\n\t\tsetSummary(R.string.pref_loc_prov_summary);\n\t\tupdateProviders();\n\t}\n\t\n\t/**\n\t * Regenerates the list of selectable location providers.\n\t */\n\tpublic void updateProviders() {\n\t\tList<String> providers = ((LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE)).getAllProviders();\n\t\tif (providers != null) {\n\t\t\tList<CharSequence> entries = new ArrayList<CharSequence>();\n\t\t\tList<CharSequence> values = new ArrayList<CharSequence>();\n\t\t\t\n\t\t\tfor (String pr : providers) {\n\t\t\t\tentries.add(pr);\n\t\t\t\tvalues.add(pr);\n\t\t\t}\n\t\t\t\n\t\t\tsetEntries(entries.toArray(new CharSequence[]{}));\n\t\t\tsetEntryValues(values.toArray(new CharSequence[]{}));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/widgets/MapViewPager.java",
    "content": "package com.vonglasow.michael.satstat.widgets;\n\nimport org.mapsforge.map.android.view.MapView;\n\nimport android.content.Context;\nimport android.support.v4.view.ViewPager;\nimport android.util.AttributeSet;\nimport android.view.View;\n\npublic class MapViewPager extends ViewPager {\n\n\tpublic MapViewPager(Context context) {\n\t\tsuper(context);\n\t}\n\n\tpublic MapViewPager(Context context, AttributeSet attrs) {\n\t\tsuper(context, attrs);\n\t}\n\t\n    /**\n     * Determines if a child view can be scrolled.\n     * \n     * If the child view is scrollable, then true will be returned, indicating\n     * that scroll operation should not be picked up by the MapViewPager but\n     * passed on to the child view in which it took place.\n     * \n     * This method will return true if the child view is a {@link MapView} and\n     * retain default behavior in all other cases.\n     */\n\t@Override\n    protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {\n        if (v instanceof MapView) {\n            return true;\n        }\n        return super.canScroll(v, checkV, dx, x, y);\n    }\n\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/widgets/NetworkTypePreference.java",
    "content": "package com.vonglasow.michael.satstat.widgets;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport android.content.Context;\nimport android.net.ConnectivityManager;\nimport android.net.NetworkInfo;\nimport android.preference.MultiSelectListPreference;\nimport android.util.AttributeSet;\n\npublic class NetworkTypePreference extends MultiSelectListPreference {\n\t\n\tprivate Context mContext;\n\n\tpublic NetworkTypePreference(Context context) {\n\t\tsuper(context);\n\t\tmContext = context;\n\t\tupdateNetworks();\n\t}\n\n\tpublic NetworkTypePreference(Context context, AttributeSet attrs) {\n\t\tsuper(context, attrs);\n\t\tmContext = context;\n\t\tupdateNetworks();\n\t}\n\t\n\t/**\n\t * Regenerates the list of selectable networks.\n\t */\n\tpublic void updateNetworks() {\n\t\tNetworkInfo[] allnetinfo = ((ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).getAllNetworkInfo();\n\t\tif (allnetinfo != null) {\n\t\t\tList<CharSequence> entries = new ArrayList<CharSequence>();\n\t\t\tList<CharSequence> values = new ArrayList<CharSequence>();\n\t\t\t\n\t\t\tfor (NetworkInfo ni : allnetinfo) {\n\t\t\t\tif ((ni.getType() < ConnectivityManager.TYPE_MOBILE_MMS) || (ni.getType() > ConnectivityManager.TYPE_MOBILE_HIPRI)) {\n\t\t\t\t\t// filter out specific mobile data connections, we'll catch those with the Mobile setting\n\t\t\t\t\tentries.add(ni.getTypeName());\n\t\t\t\t\t//entries.add(ni.getTypeName() + \" (\" + Integer.toString(ni.getType()) + \")\");\n\t\t\t\t\tvalues.add(Integer.toString(ni.getType()));\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tsetEntries(entries.toArray(new CharSequence[]{}));\n\t\t\tsetEntryValues(values.toArray(new CharSequence[]{}));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "src/com/vonglasow/michael/satstat/widgets/SquareView.java",
    "content": "/*\n * Copyright © 2013 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage com.vonglasow.michael.satstat.widgets;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.View;\n\npublic class SquareView extends View {\n\tprivate float mRelativeSize = 1;\n\t\n\tpublic SquareView(Context context) {\n\t\tsuper(context);\n\t}\n\n\tpublic SquareView(Context context, AttributeSet attrs) {\n\t\tsuper(context, attrs);\n\t}\n\t\n\tpublic SquareView(Context context, AttributeSet attrs, int defStyle) {\n\t\tsuper(context, attrs, defStyle);\n\t}\n\t\n\t@Override\n\tprotected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {\n\t\tint mSize = (int) (Math.min(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec)) * mRelativeSize);\n\t\tsetMeasuredDimension(mSize, mSize);\n\t}\n\t\n\tpublic void setRelativeSize(float size) {\n\t\tmRelativeSize = size;\n\t\tinvalidate();\n\t}\n}\n"
  },
  {
    "path": "src/pl/polidea/treeview/AbstractTreeViewAdapter.java",
    "content": "package pl.polidea.treeview;\n\nimport com.vonglasow.michael.satstat.R;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.database.DataSetObserver;\nimport android.graphics.drawable.Drawable;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.view.ViewGroup;\nimport android.widget.BaseAdapter;\nimport android.widget.FrameLayout;\nimport android.widget.FrameLayout.LayoutParams;\nimport android.widget.ImageView;\nimport android.widget.ImageView.ScaleType;\nimport android.widget.LinearLayout;\nimport android.widget.ListAdapter;\n\n/**\n * Adapter used to feed the table view.\n * \n * @param <T>\n *            class for ID of the tree\n */\npublic abstract class AbstractTreeViewAdapter<T> extends BaseAdapter implements\n        ListAdapter {\n    private static final String TAG = AbstractTreeViewAdapter.class\n            .getSimpleName();\n    private final TreeStateManager<T> treeStateManager;\n    private final int numberOfLevels;\n    private final LayoutInflater layoutInflater;\n\n    private int indentWidth = 0;\n    private int indicatorGravity = 0;\n    private Drawable collapsedDrawable;\n    private Drawable expandedDrawable;\n    private Drawable indicatorBackgroundDrawable;\n    private Drawable rowBackgroundDrawable;\n\n    private boolean collapsible;\n    private final Activity activity;\n\n    public Activity getActivity() {\n        return activity;\n    }\n\n    protected TreeStateManager<T> getManager() {\n        return treeStateManager;\n    }\n\n    protected void expandCollapse(final T id) {\n        final TreeNodeInfo<T> info = treeStateManager.getNodeInfo(id);\n        if (!info.isWithChildren()) {\n            // ignore - no default action\n            return;\n        }\n        if (info.isExpanded()) {\n            treeStateManager.collapseChildren(id);\n        } else {\n            treeStateManager.expandDirectChildren(id);\n        }\n    }\n\n    private void calculateIndentWidth() {\n        if (expandedDrawable != null) {\n            indentWidth = Math.max(getIndentWidth(),\n                    expandedDrawable.getIntrinsicWidth());\n        }\n        if (collapsedDrawable != null) {\n            indentWidth = Math.max(getIndentWidth(),\n                    collapsedDrawable.getIntrinsicWidth());\n        }\n    }\n\n    public AbstractTreeViewAdapter(final Activity activity,\n            final TreeStateManager<T> treeStateManager, final int numberOfLevels) {\n        this.activity = activity;\n        this.treeStateManager = treeStateManager;\n        this.layoutInflater = (LayoutInflater) activity\n                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);\n        this.numberOfLevels = numberOfLevels;\n        this.collapsedDrawable = null;\n        this.expandedDrawable = null;\n        this.rowBackgroundDrawable = null;\n        this.indicatorBackgroundDrawable = null;\n    }\n\n    @Override\n    public void registerDataSetObserver(final DataSetObserver observer) {\n        treeStateManager.registerDataSetObserver(observer);\n    }\n\n    @Override\n    public void unregisterDataSetObserver(final DataSetObserver observer) {\n        treeStateManager.unregisterDataSetObserver(observer);\n    }\n\n    @Override\n    public int getCount() {\n        return treeStateManager.getVisibleCount();\n    }\n\n    @Override\n    public Object getItem(final int position) {\n        return getTreeId(position);\n    }\n\n    public T getTreeId(final int position) {\n        return treeStateManager.getVisibleList().get(position);\n    }\n\n    public TreeNodeInfo<T> getTreeNodeInfo(final int position) {\n        return treeStateManager.getNodeInfo(getTreeId(position));\n    }\n\n    @Override\n    public boolean hasStableIds() { // NOPMD\n        return true;\n    }\n\n    @Override\n    public int getItemViewType(final int position) {\n        return getTreeNodeInfo(position).getLevel();\n    }\n\n    @Override\n    public int getViewTypeCount() {\n        return numberOfLevels;\n    }\n\n    @Override\n    public boolean isEmpty() {\n        return getCount() == 0;\n    }\n\n    @Override\n    public boolean areAllItemsEnabled() { // NOPMD\n        return true;\n    }\n\n    @Override\n    public boolean isEnabled(final int position) { // NOPMD\n        return true;\n    }\n\n    protected int getTreeListItemWrapperId() {\n        return R.layout.tree_list_item_wrapper;\n    }\n\n    @Override\n    public final View getView(final int position, final View convertView,\n            final ViewGroup parent) {\n        //Log.d(TAG, \"Creating a view based on \" + convertView\n        //        + \" with position \" + position);\n        final TreeNodeInfo<T> nodeInfo = getTreeNodeInfo(position);\n        if (convertView == null) {\n            //Log.d(TAG, \"Creating the view a new\");\n            final LinearLayout layout = (LinearLayout) layoutInflater.inflate(\n                    getTreeListItemWrapperId(), null);\n            return populateTreeItem(layout, getNewChildView(nodeInfo),\n                    nodeInfo, true);\n        } else {\n            //Log.d(TAG, \"Reusing the view\");\n            final LinearLayout linear = (LinearLayout) convertView;\n            final FrameLayout frameLayout = (FrameLayout) linear\n                    .findViewById(R.id.treeview_list_item_frame);\n            final View childView = frameLayout.getChildAt(0);\n            updateView(childView, nodeInfo);\n            return populateTreeItem(linear, childView, nodeInfo, false);\n        }\n    }\n\n    /**\n     * Called when new view is to be created.\n     * \n     * @param treeNodeInfo\n     *            node info\n     * @return view that should be displayed as tree content\n     */\n    public abstract View getNewChildView(TreeNodeInfo<T> treeNodeInfo);\n\n    /**\n     * Called when new view is going to be reused. You should update the view\n     * and fill it in with the data required to display the new information. You\n     * can also create a new view, which will mean that the old view will not be\n     * reused.\n     * \n     * @param view\n     *            view that should be updated with the new values\n     * @param treeNodeInfo\n     *            node info used to populate the view\n     * @return view to used as row indented content\n     */\n    public abstract View updateView(View view, TreeNodeInfo<T> treeNodeInfo);\n\n    /**\n     * Retrieves background drawable for the node.\n     * \n     * @param treeNodeInfo\n     *            node info\n     * @return drawable returned as background for the whole row. Might be null,\n     *         then default background is used\n     */\n    public Drawable getBackgroundDrawable(final TreeNodeInfo<T> treeNodeInfo) { // NOPMD\n        return null;\n    }\n\n    private Drawable getDrawableOrDefaultBackground(final Drawable r) {\n        if (r == null) {\n            return activity.getResources()\n                    .getDrawable(R.drawable.list_selector_background).mutate();\n        } else {\n            return r;\n        }\n    }\n\n    public final LinearLayout populateTreeItem(final LinearLayout layout,\n            final View childView, final TreeNodeInfo<T> nodeInfo,\n            final boolean newChildView) {\n        final Drawable individualRowDrawable = getBackgroundDrawable(nodeInfo);\n        layout.setBackgroundDrawable(individualRowDrawable == null ? getDrawableOrDefaultBackground(rowBackgroundDrawable)\n                : individualRowDrawable);\n        final LinearLayout.LayoutParams indicatorLayoutParams = new LinearLayout.LayoutParams(\n                calculateIndentation(nodeInfo), LayoutParams.FILL_PARENT);\n        final LinearLayout indicatorLayout = (LinearLayout) layout\n                .findViewById(R.id.treeview_list_item_image_layout);\n        indicatorLayout.setGravity(indicatorGravity);\n        indicatorLayout.setLayoutParams(indicatorLayoutParams);\n        final ImageView image = (ImageView) layout\n                .findViewById(R.id.treeview_list_item_image);\n        image.setImageDrawable(getDrawable(nodeInfo));\n        image.setScaleType(ScaleType.CENTER);\n        image.setTag(nodeInfo.getId());\n        image.setOnClickListener(null);\n        image.setClickable(false);\n        layout.setTag(nodeInfo.getId());\n        final FrameLayout frameLayout = (FrameLayout) layout\n                .findViewById(R.id.treeview_list_item_frame);\n        final FrameLayout.LayoutParams childParams = new FrameLayout.LayoutParams(\n                LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);\n        if (newChildView) {\n            frameLayout.addView(childView, childParams);\n        }\n        frameLayout.setTag(nodeInfo.getId());\n        return layout;\n    }\n\n    protected int calculateIndentation(final TreeNodeInfo<T> nodeInfo) {\n        return getIndentWidth() * (nodeInfo.getLevel() + (collapsible ? 1 : 0));\n    }\n\n    protected Drawable getDrawable(final TreeNodeInfo<T> nodeInfo) {\n        if (!nodeInfo.isWithChildren() || !collapsible) {\n            return getDrawableOrDefaultBackground(indicatorBackgroundDrawable);\n        }\n        if (nodeInfo.isExpanded()) {\n            return expandedDrawable;\n        } else {\n            return collapsedDrawable;\n        }\n    }\n\n    public void setIndicatorGravity(final int indicatorGravity) {\n        this.indicatorGravity = indicatorGravity;\n    }\n\n    public void setCollapsedDrawable(final Drawable collapsedDrawable) {\n        this.collapsedDrawable = collapsedDrawable;\n        calculateIndentWidth();\n    }\n\n    public void setExpandedDrawable(final Drawable expandedDrawable) {\n        this.expandedDrawable = expandedDrawable;\n        calculateIndentWidth();\n    }\n\n    public void setIndentWidth(final int indentWidth) {\n        this.indentWidth = indentWidth;\n        calculateIndentWidth();\n    }\n\n    public void setRowBackgroundDrawable(final Drawable rowBackgroundDrawable) {\n        this.rowBackgroundDrawable = rowBackgroundDrawable;\n    }\n\n    public void setIndicatorBackgroundDrawable(\n            final Drawable indicatorBackgroundDrawable) {\n        this.indicatorBackgroundDrawable = indicatorBackgroundDrawable;\n    }\n\n    public void setCollapsible(final boolean collapsible) {\n        this.collapsible = collapsible;\n    }\n\n    public void refresh() {\n        treeStateManager.refresh();\n    }\n\n    private int getIndentWidth() {\n        return indentWidth;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public void handleItemClick(final View view, final Object id) {\n        expandCollapse((T) id);\n    }\n\n}\n"
  },
  {
    "path": "src/pl/polidea/treeview/DownloadTreeStateManager.java",
    "content": "/*\n * Copyright (c) 2011, Polidea\n * Copyright © 2013–2016 Michael von Glasow.\n * \n * This file is part of LSRN Tools.\n *\n * LSRN Tools is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * LSRN Tools is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with LSRN Tools.  If not, see <http://www.gnu.org/licenses/>.\n */\n\npackage pl.polidea.treeview;\n\nimport java.util.List;\n\nimport com.vonglasow.michael.satstat.utils.RemoteFile;\n\n/**\n * In-memory manager of tree state, adapted for use with {@link com.conglasow.michael.satstat.utils.RemoteFile}\n * and branches loaded dynamically.\n */\npublic class DownloadTreeStateManager extends\n\t\tInMemoryTreeStateManager<RemoteFile> {\n    private static final long serialVersionUID = 1L;\n\n    @Override\n    public synchronized TreeNodeInfo<RemoteFile> getNodeInfo(final RemoteFile id) {\n        final InMemoryTreeNode<RemoteFile> node = getNodeFromTreeOrThrow(id);\n        final List<InMemoryTreeNode<RemoteFile>> children = node.getChildren();\n        boolean expanded = false;\n        if (!children.isEmpty() && children.get(0).isVisible()) {\n            expanded = true;\n        }\n        boolean hasChildren = id.isDirectory | !children.isEmpty();\n        return new TreeNodeInfo<RemoteFile>(id, node.getLevel(), hasChildren,\n                node.isVisible(), expanded);\n    }\n\n}\n"
  },
  {
    "path": "src/pl/polidea/treeview/InMemoryTreeNode.java",
    "content": "package pl.polidea.treeview;\n\nimport java.io.Serializable;\nimport java.util.LinkedList;\nimport java.util.List;\n\n/**\n * Node. It is package protected so that it cannot be used outside.\n * \n * @param <T>\n *            type of the identifier used by the tree\n */\nclass InMemoryTreeNode<T> implements Serializable {\n    private static final long serialVersionUID = 1L;\n    private final T id;\n    private final T parent;\n    private final int level;\n    private boolean visible = true;\n    private final List<InMemoryTreeNode<T>> children = new LinkedList<InMemoryTreeNode<T>>();\n    private List<T> childIdListCache = null;\n\n    public InMemoryTreeNode(final T id, final T parent, final int level,\n            final boolean visible) {\n        super();\n        this.id = id;\n        this.parent = parent;\n        this.level = level;\n        this.visible = visible;\n    }\n\n    public int indexOf(final T id) {\n        return getChildIdList().indexOf(id);\n    }\n\n    /**\n     * Cache is built lasily only if needed. The cache is cleaned on any\n     * structure change for that node!).\n     * \n     * @return list of ids of children\n     */\n    public synchronized List<T> getChildIdList() {\n        if (childIdListCache == null) {\n            childIdListCache = new LinkedList<T>();\n            for (final InMemoryTreeNode<T> n : children) {\n                childIdListCache.add(n.getId());\n            }\n        }\n        return childIdListCache;\n    }\n\n    public boolean isVisible() {\n        return visible;\n    }\n\n    public void setVisible(final boolean visible) {\n        this.visible = visible;\n    }\n\n    public int getChildrenListSize() {\n        return children.size();\n    }\n\n    public synchronized InMemoryTreeNode<T> add(final int index, final T child,\n            final boolean visible) {\n        childIdListCache = null;\n        // Note! top levell children are always visible (!)\n        final InMemoryTreeNode<T> newNode = new InMemoryTreeNode<T>(child,\n                getId(), getLevel() + 1, getId() == null ? true : visible);\n        children.add(index, newNode);\n        return newNode;\n    }\n\n    /**\n     * Note. This method should technically return unmodifiable collection, but\n     * for performance reason on small devices we do not do it.\n     * \n     * @return children list\n     */\n    public List<InMemoryTreeNode<T>> getChildren() {\n        return children;\n    }\n\n    public synchronized void clearChildren() {\n        children.clear();\n        childIdListCache = null;\n    }\n\n    public synchronized void removeChild(final T child) {\n        final int childIndex = indexOf(child);\n        if (childIndex != -1) {\n            children.remove(childIndex);\n            childIdListCache = null;\n        }\n    }\n\n    @Override\n    public String toString() {\n        return \"InMemoryTreeNode [id=\" + getId() + \", parent=\" + getParent()\n                + \", level=\" + getLevel() + \", visible=\" + visible\n                + \", children=\" + children + \", childIdListCache=\"\n                + childIdListCache + \"]\";\n    }\n\n    T getId() {\n        return id;\n    }\n\n    T getParent() {\n        return parent;\n    }\n\n    int getLevel() {\n        return level;\n    }\n\n}"
  },
  {
    "path": "src/pl/polidea/treeview/InMemoryTreeStateManager.java",
    "content": "package pl.polidea.treeview;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport android.database.DataSetObserver;\nimport android.util.Log;\n\n/**\n * In-memory manager of tree state.\n * \n * @param <T>\n *            type of identifier\n */\npublic class InMemoryTreeStateManager<T> implements TreeStateManager<T> {\n    private static final String TAG = InMemoryTreeStateManager.class\n            .getSimpleName();\n    private static final long serialVersionUID = 1L;\n    private final Map<T, InMemoryTreeNode<T>> allNodes = new HashMap<T, InMemoryTreeNode<T>>();\n    private final InMemoryTreeNode<T> topSentinel = new InMemoryTreeNode<T>(\n            null, null, -1, true);\n    private transient List<T> visibleListCache = null; // lasy initialised\n    private transient List<T> unmodifiableVisibleList = null;\n    private boolean visibleByDefault = true;\n    private transient Set<DataSetObserver> observers = null;\n\n    private synchronized void internalDataSetChanged() {\n        visibleListCache = null;\n        unmodifiableVisibleList = null;\n        if (observers != null)\n        \tfor (final DataSetObserver observer : observers) {\n        \t\tobserver.onChanged();\n        \t}\n    }\n\n    /**\n     * If true new nodes are visible by default.\n     * \n     * @param visibleByDefault\n     *            if true, then newly added nodes are expanded by default\n     */\n    public void setVisibleByDefault(final boolean visibleByDefault) {\n        this.visibleByDefault = visibleByDefault;\n    }\n\n    protected InMemoryTreeNode<T> getNodeFromTreeOrThrow(final T id) {\n        if (id == null) {\n            throw new NodeNotInTreeException(\"(null)\");\n        }\n        final InMemoryTreeNode<T> node = allNodes.get(id);\n        if (node == null) {\n            throw new NodeNotInTreeException(id.toString());\n        }\n        return node;\n    }\n\n    private InMemoryTreeNode<T> getNodeFromTreeOrThrowAllowRoot(final T id) {\n        if (id == null) {\n            return topSentinel;\n        }\n        return getNodeFromTreeOrThrow(id);\n    }\n\n    private void expectNodeNotInTreeYet(final T id) {\n        final InMemoryTreeNode<T> node = allNodes.get(id);\n        if (node != null) {\n            throw new NodeAlreadyInTreeException(id.toString(), node.toString());\n        }\n    }\n\n    @Override\n    public synchronized TreeNodeInfo<T> getNodeInfo(final T id) {\n        final InMemoryTreeNode<T> node = getNodeFromTreeOrThrow(id);\n        final List<InMemoryTreeNode<T>> children = node.getChildren();\n        boolean expanded = false;\n        if (!children.isEmpty() && children.get(0).isVisible()) {\n            expanded = true;\n        }\n        return new TreeNodeInfo<T>(id, node.getLevel(), !children.isEmpty(),\n                node.isVisible(), expanded);\n    }\n\n    @Override\n    public synchronized List<T> getChildren(final T id) {\n        final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);\n        return node.getChildIdList();\n    }\n\n    @Override\n    public synchronized T getParent(final T id) {\n        final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);\n        return node.getParent();\n    }\n\n    private boolean getChildrenVisibility(final InMemoryTreeNode<T> node) {\n        boolean visibility;\n        final List<InMemoryTreeNode<T>> children = node.getChildren();\n        if (children.isEmpty()) {\n            visibility = visibleByDefault;\n        } else {\n            visibility = children.get(0).isVisible();\n        }\n        return visibility;\n    }\n\n    @Override\n    public synchronized void addBeforeChild(final T parent, final T newChild,\n            final T beforeChild) {\n        expectNodeNotInTreeYet(newChild);\n        final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(parent);\n        final boolean visibility = getChildrenVisibility(node);\n        // top nodes are always expanded.\n        if (beforeChild == null) {\n            final InMemoryTreeNode<T> added = node.add(0, newChild, visibility);\n            allNodes.put(newChild, added);\n        } else {\n            final int index = node.indexOf(beforeChild);\n            final InMemoryTreeNode<T> added = node.add(index == -1 ? 0 : index,\n                    newChild, visibility);\n            allNodes.put(newChild, added);\n        }\n        if (visibility) {\n            internalDataSetChanged();\n        }\n    }\n\n    @Override\n    public synchronized void addAfterChild(final T parent, final T newChild,\n            final T afterChild) {\n        expectNodeNotInTreeYet(newChild);\n        final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(parent);\n        final boolean visibility = getChildrenVisibility(node);\n        if (afterChild == null) {\n            final InMemoryTreeNode<T> added = node.add(\n                    node.getChildrenListSize(), newChild, visibility);\n            allNodes.put(newChild, added);\n        } else {\n            final int index = node.indexOf(afterChild);\n            final InMemoryTreeNode<T> added = node.add(\n                    index == -1 ? node.getChildrenListSize() : index + 1, newChild,\n                    visibility);\n            allNodes.put(newChild, added);\n        }\n        if (visibility) {\n            internalDataSetChanged();\n        }\n    }\n\n    @Override\n    public synchronized void removeNodeRecursively(final T id) {\n        final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);\n        final boolean visibleNodeChanged = removeNodeRecursively(node);\n        final T parent = node.getParent();\n        final InMemoryTreeNode<T> parentNode = getNodeFromTreeOrThrowAllowRoot(parent);\n        parentNode.removeChild(id);\n        if (visibleNodeChanged) {\n            internalDataSetChanged();\n        }\n    }\n\n    private boolean removeNodeRecursively(final InMemoryTreeNode<T> node) {\n        boolean visibleNodeChanged = false;\n        for (final InMemoryTreeNode<T> child : node.getChildren()) {\n            if (removeNodeRecursively(child)) {\n                visibleNodeChanged = true;\n            }\n        }\n        node.clearChildren();\n        if (node.getId() != null) {\n            allNodes.remove(node.getId());\n            if (node.isVisible()) {\n                visibleNodeChanged = true;\n            }\n        }\n        return visibleNodeChanged;\n    }\n\n    private void setChildrenVisibility(final InMemoryTreeNode<T> node,\n            final boolean visible, final boolean recursive) {\n        for (final InMemoryTreeNode<T> child : node.getChildren()) {\n            child.setVisible(visible);\n            if (recursive) {\n                setChildrenVisibility(child, visible, true);\n            }\n        }\n    }\n\n    @Override\n    public synchronized void expandDirectChildren(final T id) {\n        Log.d(TAG, \"Expanding direct children of \" + id);\n        final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);\n        setChildrenVisibility(node, true, false);\n        internalDataSetChanged();\n    }\n\n    @Override\n    public synchronized void expandEverythingBelow(final T id) {\n        Log.d(TAG, \"Expanding all children below \" + id);\n        final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);\n        setChildrenVisibility(node, true, true);\n        internalDataSetChanged();\n    }\n\n    @Override\n    public synchronized void collapseChildren(final T id) {\n        final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);\n        if (node == topSentinel) {\n            for (final InMemoryTreeNode<T> n : topSentinel.getChildren()) {\n                setChildrenVisibility(n, false, true);\n            }\n        } else {\n            setChildrenVisibility(node, false, true);\n        }\n        internalDataSetChanged();\n    }\n\n    @Override\n    public synchronized T getNextSibling(final T id) {\n        final T parent = getParent(id);\n        final InMemoryTreeNode<T> parentNode = getNodeFromTreeOrThrowAllowRoot(parent);\n        boolean returnNext = false;\n        for (final InMemoryTreeNode<T> child : parentNode.getChildren()) {\n            if (returnNext) {\n                return child.getId();\n            }\n            if (child.getId().equals(id)) {\n                returnNext = true;\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public synchronized T getPreviousSibling(final T id) {\n        final T parent = getParent(id);\n        final InMemoryTreeNode<T> parentNode = getNodeFromTreeOrThrowAllowRoot(parent);\n        T previousSibling = null;\n        for (final InMemoryTreeNode<T> child : parentNode.getChildren()) {\n            if (child.getId().equals(id)) {\n                return previousSibling;\n            }\n            previousSibling = child.getId();\n        }\n        return null;\n    }\n\n    @Override\n    public synchronized boolean isInTree(final T id) {\n        return allNodes.containsKey(id);\n    }\n\n    @Override\n    public synchronized int getVisibleCount() {\n        return getVisibleList().size();\n    }\n\n    @Override\n    public synchronized List<T> getVisibleList() {\n        T currentId = null;\n        if (visibleListCache == null) {\n            visibleListCache = new ArrayList<T>(allNodes.size());\n            do {\n                currentId = getNextVisible(currentId);\n                if (currentId == null) {\n                    break;\n                } else {\n                    visibleListCache.add(currentId);\n                }\n            } while (true);\n        }\n        if (unmodifiableVisibleList == null) {\n            unmodifiableVisibleList = Collections\n                    .unmodifiableList(visibleListCache);\n        }\n        return unmodifiableVisibleList;\n    }\n\n    public synchronized T getNextVisible(final T id) {\n        final InMemoryTreeNode<T> node = getNodeFromTreeOrThrowAllowRoot(id);\n        if (!node.isVisible()) {\n            return null;\n        }\n        final List<InMemoryTreeNode<T>> children = node.getChildren();\n        if (!children.isEmpty()) {\n            final InMemoryTreeNode<T> firstChild = children.get(0);\n            if (firstChild.isVisible()) {\n                return firstChild.getId();\n            }\n        }\n        final T sibl = getNextSibling(id);\n        if (sibl != null) {\n            return sibl;\n        }\n        T parent = node.getParent();\n        do {\n            if (parent == null) {\n                return null;\n            }\n            final T parentSibling = getNextSibling(parent);\n            if (parentSibling != null) {\n                return parentSibling;\n            }\n            parent = getNodeFromTreeOrThrow(parent).getParent();\n        } while (true);\n    }\n\n    @Override\n    public synchronized void registerDataSetObserver(\n            final DataSetObserver observer) {\n    \tif (observers == null)\n    \t\tobservers = new HashSet<DataSetObserver>();\n        observers.add(observer);\n    }\n\n    @Override\n    public synchronized void unregisterDataSetObserver(\n            final DataSetObserver observer) {\n    \tif (observers != null)\n    \t\tobservers.remove(observer);\n    }\n\n    @Override\n    public int getLevel(final T id) {\n        return getNodeFromTreeOrThrow(id).getLevel();\n    }\n\n    @Override\n    public Integer[] getHierarchyDescription(final T id) {\n        final int level = getLevel(id);\n        final Integer[] hierarchy = new Integer[level + 1];\n        int currentLevel = level;\n        T currentId = id;\n        T parent = getParent(currentId);\n        while (currentLevel >= 0) {\n            hierarchy[currentLevel--] = getChildren(parent).indexOf(currentId);\n            currentId = parent;\n            parent = getParent(parent);\n        }\n        return hierarchy;\n    }\n\n    private void appendToSb(final StringBuilder sb, final T id) {\n        if (id != null) {\n            final TreeNodeInfo<T> node = getNodeInfo(id);\n            final int indent = node.getLevel() * 4;\n            final char[] indentString = new char[indent];\n            Arrays.fill(indentString, ' ');\n            sb.append(indentString);\n            sb.append(node.toString());\n            sb.append(Arrays.asList(getHierarchyDescription(id)).toString());\n            sb.append(\"\\n\");\n        }\n        final List<T> children = getChildren(id);\n        for (final T child : children) {\n            appendToSb(sb, child);\n        }\n    }\n\n    @Override\n    public synchronized String toString() {\n        final StringBuilder sb = new StringBuilder();\n        appendToSb(sb, null);\n        return sb.toString();\n    }\n\n    @Override\n    public synchronized void clear() {\n        allNodes.clear();\n        topSentinel.clearChildren();\n        internalDataSetChanged();\n    }\n\n    @Override\n    public void refresh() {\n        internalDataSetChanged();\n    }\n\n}\n"
  },
  {
    "path": "src/pl/polidea/treeview/NodeAlreadyInTreeException.java",
    "content": "package pl.polidea.treeview;\n\n/**\n * The node being added is already in the tree.\n * \n */\npublic class NodeAlreadyInTreeException extends RuntimeException {\n    private static final long serialVersionUID = 1L;\n\n    public NodeAlreadyInTreeException(final String id, final String oldNode) {\n        super(\"The node has already been added to the tree: \" + id + \". Old node is:\" + oldNode);\n    }\n\n}\n"
  },
  {
    "path": "src/pl/polidea/treeview/NodeNotInTreeException.java",
    "content": "package pl.polidea.treeview;\n\n/**\n * This exception is thrown when the tree does not contain node requested.\n * \n */\npublic class NodeNotInTreeException extends RuntimeException {\n\n    private static final long serialVersionUID = 1L;\n\n    public NodeNotInTreeException(final String id) {\n        super(\"The tree does not contain the node specified: \" + id);\n    }\n\n}\n"
  },
  {
    "path": "src/pl/polidea/treeview/TreeBuilder.java",
    "content": "package pl.polidea.treeview;\n\nimport android.util.Log;\n\n/**\n * Allows to build tree easily in sequential mode (you have to know levels of\n * all the tree elements upfront). You should rather use this class rather than\n * manager if you build initial tree from some external data source.\n * <p>\n * Note, that all ids must be unique. IDs are used to find nodes in the whole\n * tree, so they cannot repeat even if they are in different \n * sub-trees. \n * \n * @param <T>\n */\npublic class TreeBuilder<T> {\n    private static final String TAG = TreeBuilder.class.getSimpleName();\n\n    private final TreeStateManager<T> manager;\n\n    private T lastAddedId = null;\n    private int lastLevel = -1;\n\n    public TreeBuilder(final TreeStateManager<T> manager) {\n        this.manager = manager;\n    }\n\n    public void clear() {\n        manager.clear();\n        lastAddedId = null;\n        lastLevel = -1;\n    }\n\n    /**\n     * Adds new relation to existing tree. Child is set as the last child of the\n     * parent node. Parent has to already exist in the tree, child cannot yet\n     * exist. This method is mostly useful in case you add entries layer by\n     * layer - i.e. first top level entries, then children for all parents, then\n     * grand-children and so on.\n     * \n     * @param parent\n     *            parent id\n     * @param child\n     *            child id\n     */\n    public synchronized void addRelation(final T parent, final T child) {\n        //Log.d(TAG, \"Adding relation parent:\" + parent + \" -> child: \" + child);\n        manager.addAfterChild(parent, child, null);\n        lastAddedId = child;\n        lastLevel = manager.getLevel(child);\n    }\n\n    /**\n     * Adds sequentially new node. Using this method is the simplest way of\n     * building tree - if you have all the elements in the sequence as they\n     * should be displayed in fully-expanded tree. You can combine it with add\n     * relation - for example you can add information about few levels using\n     * {@link addRelation} and then after the right level is added as parent,\n     * you can continue adding them using sequential operation.\n     * \n     * @param id\n     *            id of the node\n     * @param level\n     *            its level\n     */\n    public synchronized void sequentiallyAddNextNode(final T id, final int level) {\n        //Log.d(TAG, \"Adding sequentiall node \" + id + \" at level \" + level);\n        if (lastAddedId == null) {\n            addNodeToParentOneLevelDown(null, id, level);\n        } else {\n            if (level <= lastLevel) {\n                final T parent = findParentAtLevel(lastAddedId, level - 1);\n                addNodeToParentOneLevelDown(parent, id, level);\n            } else {\n                addNodeToParentOneLevelDown(lastAddedId, id, level);\n            }\n        }\n    }\n\n    /**\n     * Find parent of the node at the level specified.\n     * \n     * @param node\n     *            node from which we start\n     * @param levelToFind\n     *            level which we are looking for\n     * @return the node found (null if it is topmost node).\n     */\n    private T findParentAtLevel(final T node, final int levelToFind) {\n        T parent = manager.getParent(node);\n        while (parent != null) {\n            if (manager.getLevel(parent) == levelToFind) {\n                break;\n            }\n            parent = manager.getParent(parent);\n        }\n        return parent;\n    }\n\n    /**\n     * Adds note to parent at the level specified. But it verifies that the\n     * level is one level down than the parent!\n     * \n     * @param parent\n     *            parent parent\n     * @param id\n     *            new node id\n     * @param level\n     *            should always be parent's level + 1\n     */\n    private void addNodeToParentOneLevelDown(final T parent, final T id,\n            final int level) {\n        if (parent == null && level != 0) {\n            throw new TreeConfigurationException(\"Trying to add new id \" + id\n                    + \" to top level with level != 0 (\" + level + \")\");\n        }\n        if (parent != null && manager.getLevel(parent) != level - 1) {\n            throw new TreeConfigurationException(\"Trying to add new id \" + id\n                    + \" <\" + level + \"> to \" + parent + \" <\"\n                    + manager.getLevel(parent)\n                    + \">. The difference in levels up is bigger than 1.\");\n        }\n        manager.addAfterChild(parent, id, null);\n        setLastAdded(id, level);\n    }\n\n    private void setLastAdded(final T id, final int level) {\n        lastAddedId = id;\n        lastLevel = level;\n    }\n\n}\n"
  },
  {
    "path": "src/pl/polidea/treeview/TreeConfigurationException.java",
    "content": "package pl.polidea.treeview;\n\n/**\n * Exception thrown when there is a problem with configuring tree.\n * \n */\npublic class TreeConfigurationException extends RuntimeException {\n\n    private static final long serialVersionUID = 1L;\n\n    public TreeConfigurationException(final String detailMessage) {\n        super(detailMessage);\n    }\n\n}\n"
  },
  {
    "path": "src/pl/polidea/treeview/TreeNodeInfo.java",
    "content": "package pl.polidea.treeview;\n\n/**\n * Information about the node.\n * \n * @param <T>\n *            type of the id for the tree\n */\npublic class TreeNodeInfo<T> {\n    private final T id;\n    private final int level;\n    private final boolean withChildren;\n    private final boolean visible;\n    private final boolean expanded;\n\n    /**\n     * Creates the node information.\n     * \n     * @param id\n     *            id of the node\n     * @param level\n     *            level of the node\n     * @param withChildren\n     *            whether the node has children.\n     * @param visible\n     *            whether the tree node is visible.\n     * @param expanded\n     *            whether the tree node is expanded\n     * \n     */\n    public TreeNodeInfo(final T id, final int level,\n            final boolean withChildren, final boolean visible,\n            final boolean expanded) {\n        super();\n        this.id = id;\n        this.level = level;\n        this.withChildren = withChildren;\n        this.visible = visible;\n        this.expanded = expanded;\n    }\n\n    public T getId() {\n        return id;\n    }\n\n    public boolean isWithChildren() {\n        return withChildren;\n    }\n\n    public boolean isVisible() {\n        return visible;\n    }\n\n    public boolean isExpanded() {\n        return expanded;\n    }\n\n    public int getLevel() {\n        return level;\n    }\n\n    @Override\n    public String toString() {\n        return \"TreeNodeInfo [id=\" + id + \", level=\" + level\n                + \", withChildren=\" + withChildren + \", visible=\" + visible\n                + \", expanded=\" + expanded + \"]\";\n    }\n\n}"
  },
  {
    "path": "src/pl/polidea/treeview/TreeStateManager.java",
    "content": "package pl.polidea.treeview;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport android.database.DataSetObserver;\n\n/**\n * Manages information about state of the tree. It only keeps information about\n * tree elements, not the elements themselves.\n * \n * @param <T>\n *            type of the identifier for nodes in the tree\n */\npublic interface TreeStateManager<T> extends Serializable {\n\n    /**\n     * Returns array of integers showing the location of the node in hierarchy.\n     * It corresponds to heading numbering. {0,0,0} in 3 level node is the first\n     * node {0,0,1} is second leaf (assuming that there are two leaves in first\n     * subnode of the first node).\n     * \n     * @param id\n     *            id of the node\n     * @return textual description of the hierarchy in tree for the node.\n     */\n    Integer[] getHierarchyDescription(T id);\n\n    /**\n     * Returns level of the node.\n     * \n     * @param id\n     *            id of the node\n     * @return level in the tree\n     */\n    int getLevel(T id);\n\n    /**\n     * Returns information about the node.\n     * \n     * @param id\n     *            node id\n     * @return node info\n     */\n    TreeNodeInfo<T> getNodeInfo(T id);\n\n    /**\n     * Returns children of the node.\n     * \n     * @param id\n     *            id of the node or null if asking for top nodes\n     * @return children of the node\n     */\n    List<T> getChildren(T id);\n\n    /**\n     * Returns parent of the node.\n     * \n     * @param id\n     *            id of the node\n     * @return parent id or null if no parent\n     */\n    T getParent(T id);\n\n    /**\n     * Adds the node before child or at the beginning.\n     * \n     * @param parent\n     *            id of the parent node. If null - adds at the top level\n     * @param newChild\n     *            new child to add if null - adds at the beginning.\n     * @param beforeChild\n     *            child before which to add the new child\n     */\n    void addBeforeChild(T parent, T newChild, T beforeChild);\n\n    /**\n     * Adds the node after child or at the end.\n     * \n     * @param parent\n     *            id of the parent node. If null - adds at the top level.\n     * @param newChild\n     *            new child to add. If null - adds at the end.\n     * @param afterChild\n     *            child after which to add the new child\n     */\n    void addAfterChild(T parent, T newChild, T afterChild);\n\n    /**\n     * Removes the node and all children from the tree.\n     * \n     * @param id\n     *            id of the node to remove or null if all nodes are to be\n     *            removed.\n     */\n    void removeNodeRecursively(T id);\n\n    /**\n     * Expands all children of the node.\n     * \n     * @param id\n     *            node which children should be expanded. cannot be null (top\n     *            nodes are always expanded!).\n     */\n    void expandDirectChildren(T id);\n\n    /**\n     * Expands everything below the node specified. Might be null - then expands\n     * all.\n     * \n     * @param id\n     *            node which children should be expanded or null if all nodes\n     *            are to be expanded.\n     */\n    void expandEverythingBelow(T id);\n\n    /**\n     * Collapse children.\n     * \n     * @param id\n     *            id collapses everything below node specified. If null,\n     *            collapses everything but top-level nodes.\n     */\n    void collapseChildren(T id);\n\n    /**\n     * Returns next sibling of the node (or null if no further sibling).\n     * \n     * @param id\n     *            node id\n     * @return the sibling (or null if no next)\n     */\n    T getNextSibling(T id);\n\n    /**\n     * Returns previous sibling of the node (or null if no previous sibling).\n     * \n     * @param id\n     *            node id\n     * @return the sibling (or null if no previous)\n     */\n    T getPreviousSibling(T id);\n\n    /**\n     * Checks if given node is already in tree.\n     * \n     * @param id\n     *            id of the node\n     * @return true if node is already in tree.\n     */\n    boolean isInTree(T id);\n\n    /**\n     * Count visible elements.\n     * \n     * @return number of currently visible elements.\n     */\n    int getVisibleCount();\n\n    /**\n     * Returns visible node list.\n     * \n     * @return return the list of all visible nodes in the right sequence\n     */\n    List<T> getVisibleList();\n\n    /**\n     * Registers observers with the manager.\n     * \n     * @param observer\n     *            observer\n     */\n    void registerDataSetObserver(final DataSetObserver observer);\n\n    /**\n     * Unregisters observers with the manager.\n     * \n     * @param observer\n     *            observer\n     */\n    void unregisterDataSetObserver(final DataSetObserver observer);\n\n    /**\n     * Cleans tree stored in manager. After this operation the tree is empty.\n     * \n     */\n    void clear();\n\n    /**\n     * Refreshes views connected to the manager.\n     */\n    void refresh();\n}\n"
  },
  {
    "path": "src/pl/polidea/treeview/TreeViewList.java",
    "content": "package pl.polidea.treeview;\n\nimport com.vonglasow.michael.satstat.R;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.drawable.Drawable;\nimport android.util.AttributeSet;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.ListAdapter;\nimport android.widget.ListView;\n\n/**\n * Tree view, expandable multi-level.\n * \n * <pre>\n * attr ref pl.polidea.treeview.R.styleable#TreeViewList_collapsible\n * attr ref pl.polidea.treeview.R.styleable#TreeViewList_src_expanded\n * attr ref pl.polidea.treeview.R.styleable#TreeViewList_src_collapsed\n * attr ref pl.polidea.treeview.R.styleable#TreeViewList_indent_width\n * attr ref pl.polidea.treeview.R.styleable#TreeViewList_handle_trackball_press\n * attr ref pl.polidea.treeview.R.styleable#TreeViewList_indicator_gravity\n * attr ref pl.polidea.treeview.R.styleable#TreeViewList_indicator_background\n * attr ref pl.polidea.treeview.R.styleable#TreeViewList_row_background\n * </pre>\n */\npublic class TreeViewList extends ListView {\n    private static final int DEFAULT_COLLAPSED_RESOURCE = R.drawable.collapsed;\n    private static final int DEFAULT_EXPANDED_RESOURCE = R.drawable.expanded;\n    private static final int DEFAULT_INDENT = 0;\n    private static final int DEFAULT_GRAVITY = Gravity.LEFT\n            | Gravity.CENTER_VERTICAL;\n    private Drawable expandedDrawable;\n    private Drawable collapsedDrawable;\n    private Drawable rowBackgroundDrawable;\n    private Drawable indicatorBackgroundDrawable;\n    private int indentWidth = 0;\n    private int indicatorGravity = 0;\n    private AbstractTreeViewAdapter< ? > treeAdapter;\n    private boolean collapsible;\n    private boolean handleTrackballPress;\n\n    public TreeViewList(final Context context, final AttributeSet attrs) {\n        this(context, attrs, R.style.treeViewListStyle);\n    }\n\n    public TreeViewList(final Context context) {\n        this(context, null);\n    }\n\n    public TreeViewList(final Context context, final AttributeSet attrs,\n            final int defStyle) {\n        super(context, attrs, defStyle);\n        parseAttributes(context, attrs);\n    }\n\n    private void parseAttributes(final Context context, final AttributeSet attrs) {\n        final TypedArray a = context.obtainStyledAttributes(attrs,\n                R.styleable.TreeViewList);\n        expandedDrawable = a.getDrawable(R.styleable.TreeViewList_src_expanded);\n        if (expandedDrawable == null) {\n            expandedDrawable = context.getResources().getDrawable(\n                    DEFAULT_EXPANDED_RESOURCE);\n        }\n        collapsedDrawable = a\n                .getDrawable(R.styleable.TreeViewList_src_collapsed);\n        if (collapsedDrawable == null) {\n            collapsedDrawable = context.getResources().getDrawable(\n                    DEFAULT_COLLAPSED_RESOURCE);\n        }\n        indentWidth = a.getDimensionPixelSize(\n                R.styleable.TreeViewList_indent_width, DEFAULT_INDENT);\n        indicatorGravity = a.getInteger(\n                R.styleable.TreeViewList_indicator_gravity, DEFAULT_GRAVITY);\n        indicatorBackgroundDrawable = a\n                .getDrawable(R.styleable.TreeViewList_indicator_background);\n        rowBackgroundDrawable = a\n                .getDrawable(R.styleable.TreeViewList_row_background);\n        collapsible = a.getBoolean(R.styleable.TreeViewList_collapsible, true);\n        handleTrackballPress = a.getBoolean(\n                R.styleable.TreeViewList_handle_trackball_press, true);\n    }\n\n    @Override\n    public void setAdapter(final ListAdapter adapter) {\n        if (!(adapter instanceof AbstractTreeViewAdapter)) {\n            throw new TreeConfigurationException(\n                    \"The adapter is not of TreeViewAdapter type\");\n        }\n        treeAdapter = (AbstractTreeViewAdapter< ? >) adapter;\n        syncAdapter();\n        super.setAdapter(treeAdapter);\n    }\n\n    private void syncAdapter() {\n        treeAdapter.setCollapsedDrawable(collapsedDrawable);\n        treeAdapter.setExpandedDrawable(expandedDrawable);\n        treeAdapter.setIndicatorGravity(indicatorGravity);\n        treeAdapter.setIndentWidth(indentWidth);\n        treeAdapter.setIndicatorBackgroundDrawable(indicatorBackgroundDrawable);\n        treeAdapter.setRowBackgroundDrawable(rowBackgroundDrawable);\n        treeAdapter.setCollapsible(collapsible);\n        if (handleTrackballPress) {\n            setOnItemClickListener(new OnItemClickListener() {\n                @Override\n                public void onItemClick(final AdapterView< ? > parent,\n                        final View view, final int position, final long id) {\n                    treeAdapter.handleItemClick(view, view.getTag());\n                }\n            });\n        } else {\n            setOnClickListener(null);\n        }\n\n    }\n\n    public void setExpandedDrawable(final Drawable expandedDrawable) {\n        this.expandedDrawable = expandedDrawable;\n        syncAdapter();\n        treeAdapter.refresh();\n    }\n\n    public void setCollapsedDrawable(final Drawable collapsedDrawable) {\n        this.collapsedDrawable = collapsedDrawable;\n        syncAdapter();\n        treeAdapter.refresh();\n    }\n\n    public void setRowBackgroundDrawable(final Drawable rowBackgroundDrawable) {\n        this.rowBackgroundDrawable = rowBackgroundDrawable;\n        syncAdapter();\n        treeAdapter.refresh();\n    }\n\n    public void setIndicatorBackgroundDrawable(\n            final Drawable indicatorBackgroundDrawable) {\n        this.indicatorBackgroundDrawable = indicatorBackgroundDrawable;\n        syncAdapter();\n        treeAdapter.refresh();\n    }\n\n    public void setIndentWidth(final int indentWidth) {\n        this.indentWidth = indentWidth;\n        syncAdapter();\n        treeAdapter.refresh();\n    }\n\n    public void setIndicatorGravity(final int indicatorGravity) {\n        this.indicatorGravity = indicatorGravity;\n        syncAdapter();\n        treeAdapter.refresh();\n    }\n\n    public void setCollapsible(final boolean collapsible) {\n        this.collapsible = collapsible;\n        syncAdapter();\n        treeAdapter.refresh();\n    }\n\n    public void setHandleTrackballPress(final boolean handleTrackballPress) {\n        this.handleTrackballPress = handleTrackballPress;\n        syncAdapter();\n        treeAdapter.refresh();\n    }\n\n    public Drawable getExpandedDrawable() {\n        return expandedDrawable;\n    }\n\n    public Drawable getCollapsedDrawable() {\n        return collapsedDrawable;\n    }\n\n    public Drawable getRowBackgroundDrawable() {\n        return rowBackgroundDrawable;\n    }\n\n    public Drawable getIndicatorBackgroundDrawable() {\n        return indicatorBackgroundDrawable;\n    }\n\n    public int getIndentWidth() {\n        return indentWidth;\n    }\n\n    public int getIndicatorGravity() {\n        return indicatorGravity;\n    }\n\n    public boolean isCollapsible() {\n        return collapsible;\n    }\n\n    public boolean isHandleTrackballPress() {\n        return handleTrackballPress;\n    }\n\n}\n"
  },
  {
    "path": "src/pl/polidea/treeview/overview.html",
    "content": "<html>\n<body>\nThis is a small utility that provides quite configurable tree view list.\nIt is based on standard android list view. It separates out different\naspects of the tree: there is a separate list view, tree adapter, tree\nstate manager and tree state builder.\n<p>\n<ul>\n\t<li>Tree view provides the frame to display the view.</li>\n\t<li>Adapter allows to create visual representation of each tree\n\tnode.</li>\n\t<li>State manager provides storage for tree state (connections\n\tbetween parents and children, collapsed/expanded state). It provides\n\tall the low-level tree manipulation methods.</li>\n\t<li>Tree builder allows to build tree easily providing higher\n\tlevel methods. The tree can be build either from prepared sequentially\n\tprepared list of nodes (node id, level) or using (parent/child\n\trelationships).</li>\n\t<p>For now only in-memory state manager is provided, but Tree State\n\tManger interface is done in the way that database tree manager even for\n\tlarge trees is potentially supported.\n</ul>\n</body>\n</html>"
  },
  {
    "path": "src/pl/polidea/treeview/package-info.java",
    "content": "/**\n * Provides expandable Tree View implementation.\n */\npackage pl.polidea.treeview;"
  },
  {
    "path": "src/uk/me/jstott/jcoord/CoordinateSystem.java",
    "content": "package uk.me.jstott.jcoord;\n\nimport uk.me.jstott.jcoord.datum.Datum;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Superclass for classes defining co-ordinate systems.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic abstract class CoordinateSystem {\n  \n  private Datum datum;\n  \n  public CoordinateSystem(Datum datum) {\n    setDatum(datum);\n  }\n\n  /**\n   * Convert a co-ordinate in the co-ordinate system to a point represented\n   * by a latitude and longitude and a perpendicular height above (or below) a\n   * reference ellipsoid.\n   * \n   * @return a LatLng representation of a point in a co-ordinate system.\n   * @since 1.1\n   */\n  public abstract LatLng toLatLng();\n  \n  \n  /**\n   * Set the datum.\n   * \n   * @param datum the datum.\n   * @since 1.1\n   */\n  public void setDatum(Datum datum) {\n    this.datum = datum;\n  }\n  \n  \n  /**\n   * Get the datum.\n   * \n   * @return the datum.\n   * @since 1.1\n   */\n  public Datum getDatum() {\n    return datum;\n  }\n  \n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ECEFRef.java",
    "content": "package uk.me.jstott.jcoord;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.datum.WGS84Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * ECEF (earth-centred, earth-fixed) Cartesian co-ordinates are used to define a\n * point in three-dimensional space. ECEF co-ordinates are defined relative to\n * an x-axis (the intersection of the equatorial plane and the plane defined by\n * the prime meridian), a y-axis (at 90&deg; to the x-axis and its intersection\n * with the equator) and a z-axis (intersecting the North Pole). All the axes\n * intersect at the point defined by the centre of mass of the Earth.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class ECEFRef extends CoordinateSystem {\n\n  /**\n   * x co-ordinate in metres.\n   */\n  private double x;\n\n  /**\n   * y co-ordinate in metres.\n   */\n  private double y;\n\n  /**\n   * z co-ordinate in metres.\n   */\n  private double z;\n\n\n  /**\n   * Create a new earth-centred, earth-fixed (ECEF) reference from the given\n   * parameters using the WGS84 reference ellipsoid.\n   * \n   * @param x\n   *          the x co-ordinate.\n   * @param y\n   *          the y co-ordinate.\n   * @param z\n   *          the z co-ordinate.\n   */\n  public ECEFRef(double x, double y, double z) {\n    \n    this(x, y, z, new WGS84Datum());\n    \n  }\n\n\n  /**\n   * Create a new earth-centred, earth-fixed (ECEF) reference from the given\n   * parameters and the given reference ellipsoid.\n   * \n   * @param x\n   *          the x co-ordinate.\n   * @param y\n   *          the y co-ordinate.\n   * @param z\n   *          the z co-ordinate.\n   * @param datum\n   *          the datum.\n   * @since 1.1\n   */\n  public ECEFRef(double x, double y, double z, Datum datum) {\n    \n    super(datum);\n    setX(x);\n    setY(y);\n    setZ(z);\n    \n  }\n\n\n  /**\n   * Create a new earth-centred, earth-fixed reference from the given latitude\n   * and longitude.\n   * \n   * @param ll\n   *          latitude and longitude.\n   * @since 1.1\n   */\n  public ECEFRef(LatLng ll) {\n\n    super(ll.getDatum());\n    \n    Ellipsoid ellipsoid = getDatum().getReferenceEllipsoid();\n\n    double phi = Math.toRadians(ll.getLatitude());\n    double lambda = Math.toRadians(ll.getLongitude());\n    double h = ll.getHeight();\n    double a = ellipsoid.getSemiMajorAxis();\n    double f = ellipsoid.getFlattening();\n    double eSquared = (2 * f) - (f * f);\n    double nphi = a / Math.sqrt(1 - eSquared * Util.sinSquared(phi));\n\n    setX((nphi + h) * Math.cos(phi) * Math.cos(lambda));\n    setY((nphi + h) * Math.cos(phi) * Math.sin(lambda));\n    setZ((nphi * (1 - eSquared) + h) * Math.sin(phi));\n    \n  }\n\n\n  /**\n   * Convert this ECEFRef object to a LatLng object.\n   * \n   * @return the equivalent latitude and longitude.\n   * @since 1.1\n   */\n  public LatLng toLatLng() {\n\n    Ellipsoid ellipsoid = getDatum().getReferenceEllipsoid();\n    \n    double a = ellipsoid.getSemiMajorAxis();\n    double b = ellipsoid.getSemiMinorAxis();\n    double e2Squared = ((a * a) - (b * b)) / (b * b);\n    double f = ellipsoid.getFlattening();\n    double eSquared = (2 * f) - (f * f);\n    double p = Math.sqrt((x * x) + (y * y));\n    double theta = Math.atan((z * a) / (p * b));\n\n    double phi = Math.atan((z + (e2Squared * b * Util.sinCubed(theta)))\n        / (p - eSquared * a * Util.cosCubed(theta)));\n    double lambda = Math.atan2(y, x);\n\n    double nphi = a / Math.sqrt(1 - eSquared * Util.sinSquared(phi));\n    double h = (p / Math.cos(phi)) - nphi;\n\n    return new LatLng(Math.toDegrees(phi), Math.toDegrees(lambda), h,\n        new uk.me.jstott.jcoord.datum.WGS84Datum());\n  }\n\n\n  /**\n   * Get the x co-ordinate.\n   * \n   * @return the x co-ordinate.\n   * @since 1.1\n   */\n  public double getX() {\n    return x;\n  }\n\n\n  /**\n   * Get the y co-ordinate.\n   * \n   * @return the y co-ordinate.\n   * @since 1.1\n   */\n  public double getY() {\n    return y;\n  }\n\n\n  /**\n   * Get the z co-ordinate.\n   * \n   * @return the z co-ordinate.\n   * @since 1.1\n   */\n  public double getZ() {\n    return z;\n  }\n\n\n  /**\n   * Set the x co-ordinate.\n   * \n   * @param x\n   *          the new x co-ordinate.\n   * @since 1.1\n   */\n  public void setX(double x) {\n    this.x = x;\n  }\n\n\n  /**\n   * Set the y co-ordinate.\n   * \n   * @param y\n   *          the y co-ordinate.\n   * @since 1.1\n   */\n  public void setY(double y) {\n    this.y = y;\n  }\n\n\n  /**\n   * Set the z co-ordinate.\n   * \n   * @param z\n   *          the z co-ordinate.\n   * @since 1.1\n   */\n  public void setZ(double z) {\n    this.z = z;\n  }\n\n\n  /**\n   * Get a String representation of this ECEF reference.\n   * \n   * @return a String representation of this ECEF reference.\n   * @since 1.1\n   */\n  public String toString() {\n    return \"(\" + x + \",\" + y + \",\" + z + \")\";\n  }\n\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/IrishRef.java",
    "content": "package uk.me.jstott.jcoord;\n\nimport uk.me.jstott.jcoord.datum.Ireland1965Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class to represent an Irish National Grid reference.\n * </p>\n * \n * <p>\n * <b>Irish National Grid</b><br>\n * <ul>\n * <li>Projection: Transverse Mercator</li>\n * <li>Reference ellipsoid: Modified Airy</li>\n * <li>Units: metres</li>\n * <li>Origin: 53&deg;30'N, 8&deg;W</li>\n * <li>False co-ordinates of origin: 200000m east, 250000m north</li>\n * </ul>\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 11-02-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class IrishRef extends CoordinateSystem {\n\n  /**\n   * The easting in metres relative to the origin of the British National Grid.\n   */\n  private double easting;\n\n  /**\n   * The northing in metres relative to the origin of the British National Grid.\n   */\n  private double northing;\n  \n  public static final double SCALE_FACTOR = 1.000035;\n  \n  public static final double FALSE_ORIGIN_LATITUDE = 53.5;\n  \n  public static final double FALSE_ORIGIN_LONGITUDE = -8.0;\n  \n  public static final double FALSE_ORIGIN_EASTING = 200000.0;\n  \n  public static final double FALSE_ORIGIN_NORTHING = 250000.0;\n\n\n  /**\n   * Create a new Ordnance Survey grid reference using a given easting and\n   * northing. The easting and northing must be in metres and must be relative\n   * to the origin of the British National Grid.\n   * \n   * @param easting\n   *          the easting in metres. Must be greater than or equal to 0.0 and\n   *          less than 800000.0.\n   * @param northing\n   *          the northing in metres. Must be greater than or equal to 0.0 and\n   *          less than 1400000.0.\n   * @throws IllegalArgumentException\n   *           if either the easting or the northing are invalid.\n   * @since 1.1\n   */\n  public IrishRef(double easting, double northing) throws IllegalArgumentException {\n\n    super(Ireland1965Datum.getInstance());\n\n    setEasting(easting);\n    setNorthing(northing);\n\n  }\n\n\n  /**\n   * Take a string formatted as a six-figure OS grid reference (e.g. \"TG514131\")\n   * and create a new OSRef object that represents that grid reference. The\n   * first character must be H, N, S, O or T. The second character can be any\n   * uppercase character from A through Z excluding I.\n   * \n   * @param ref\n   *          a String representing a six-figure Ordnance Survey grid reference\n   *          in the form XY123456\n   * @throws IllegalArgumentException\n   *           if ref is not of the form XY123456\n   * @since 1.1\n   */\n  public IrishRef(String ref) throws IllegalArgumentException {\n\n    super(Ireland1965Datum.getInstance());\n\n    // if (ref.matches(\"\"))\n    // TODO 2006-02-05 : check format\n    char ch = ref.charAt(0);\n    // Thanks to Nick Holloway for pointing out the radix bug here\n    int east = Integer.parseInt(ref.substring(1, 4)) * 100;\n    int north = Integer.parseInt(ref.substring(4, 7)) * 100;\n    if (ch > 73)\n      ch--; // Adjust for no I\n    double nx = ((ch - 65) % 5) * 100000;\n    double ny = (4 - Math.floor((ch - 65) / 5)) * 100000;\n\n    setEasting(east + nx);\n    setNorthing(north + ny);\n\n  }\n\n\n  /**\n   * Create an IrishRef object from the given latitude and longitude.\n   * \n   * @since 1.1\n   */\n  public IrishRef(LatLng ll) {\n\n    super(Ireland1965Datum.getInstance());\n\n    Ellipsoid ellipsoid = getDatum().getReferenceEllipsoid();\n    double N0 = FALSE_ORIGIN_NORTHING;\n    double E0 = FALSE_ORIGIN_EASTING;\n    double phi0 = Math.toRadians(FALSE_ORIGIN_LATITUDE);\n    double lambda0 = Math.toRadians(FALSE_ORIGIN_LONGITUDE);\n    double a = ellipsoid.getSemiMajorAxis() * SCALE_FACTOR;\n    double b = ellipsoid.getSemiMinorAxis() * SCALE_FACTOR;\n    double eSquared = ellipsoid.getEccentricitySquared();\n    double phi = Math.toRadians(ll.getLatitude());\n    double lambda = Math.toRadians(ll.getLongitude());\n    double E = 0.0;\n    double N = 0.0;    \n    double n = (a - b) / (a + b);\n    double v = a\n        * Math.pow(1.0 - eSquared * Util.sinSquared(phi), -0.5);\n    double rho = a * (1.0 - eSquared)\n        * Math.pow(1.0 - eSquared * Util.sinSquared(phi), -1.5);\n    double etaSquared = (v / rho) - 1.0;\n    double M = b\n        * (((1 + n + ((5.0 / 4.0) * n * n) + ((5.0 / 4.0) * n * n * n)) * (phi - phi0))\n            - (((3 * n) + (3 * n * n) + ((21.0 / 8.0) * n * n * n))\n                * Math.sin(phi - phi0) * Math.cos(phi + phi0))\n            + ((((15.0 / 8.0) * n * n) + ((15.0 / 8.0) * n * n * n))\n                * Math.sin(2.0 * (phi - phi0)) * Math.cos(2.0 * (phi + phi0))) - (((35.0 / 24.0)\n            * n * n * n)\n            * Math.sin(3.0 * (phi - phi0)) * Math.cos(3.0 * (phi + phi0))));\n    double I = M + N0;\n    double II = (v / 2.0) * Math.sin(phi) * Math.cos(phi);\n    double III = (v / 24.0) * Math.sin(phi) * Math.pow(Math.cos(phi), 3.0)\n        * (5.0 - Util.tanSquared(phi) + (9.0 * etaSquared));\n    double IIIA = (v / 720.0) * Math.sin(phi) * Math.pow(Math.cos(phi), 5.0)\n        * (61.0 - (58.0 * Util.tanSquared(phi)) + Math.pow(Math.tan(phi), 4.0));\n    double IV = v * Math.cos(phi);\n    double V = (v / 6.0) * Math.pow(Math.cos(phi), 3.0)\n        * ((v / rho) - Util.tanSquared(phi));\n    double VI = (v / 120.0)\n        * Math.pow(Math.cos(phi), 5.0)\n        * (5.0 - (18.0 * Util.tanSquared(phi)) + (Math.pow(Math.tan(phi), 4.0))\n            + (14 * etaSquared) - (58 * Util.tanSquared(phi) * etaSquared));\n\n    N = I + (II * Math.pow(lambda - lambda0, 2.0))\n        + (III * Math.pow(lambda - lambda0, 4.0))\n        + (IIIA * Math.pow(lambda - lambda0, 6.0));\n    E = E0 + (IV * (lambda - lambda0)) + (V * Math.pow(lambda - lambda0, 3.0))\n        + (VI * Math.pow(lambda - lambda0, 5.0));\n\n    setEasting(E);\n    setNorthing(N);\n\n  }\n\n\n  /**\n   * Return a String representation of this Irish grid reference showing the\n   * easting and northing in metres.\n   * \n   * @return a String represenation of this Irish grid reference\n   * @since 1.1\n   */\n  public String toString() {\n    return \"(\" + easting + \", \" + northing + \")\";\n  }\n\n\n  /**\n   * Return a String representation of this Irish grid reference using the\n   * six-figure notation in the form X123456\n   * \n   * @return a String representing this Irish grid reference in six-figure\n   *         notation\n   * @since 1.0\n   */\n  public String toSixFigureString() {\n    int hundredkmE = (int) Math.floor(easting / 100000);\n    int hundredkmN = (int) Math.floor(northing / 100000);\n    \n    int charOffset = 4 - hundredkmN;\n    int index = 65 + (5 * charOffset) + hundredkmE;\n    if (index >= 73)\n      index++;\n    String letter = Character.toString((char) index);\n\n    int e = (int) Math.floor((easting - (100000 * hundredkmE)) / 100);\n    int n = (int) Math.floor((northing - (100000 * hundredkmN)) / 100);\n    String es = \"\" + e;\n    if (e < 100)\n      es = \"0\" + es;\n    if (e < 10)\n      es = \"0\" + es;\n    String ns = \"\" + n;\n    if (n < 100)\n      ns = \"0\" + ns;\n    if (n < 10)\n      ns = \"0\" + ns;\n\n    return letter + es + ns;\n  }\n\n\n  /**\n   * Convert this Irish grid reference to a latitude/longitude pair using the\n   * Ireland 1965 datum. Note that, the LatLng object may need to be converted to the\n   * WGS84 datum depending on the application.\n   * \n   * @return a LatLng object representing this Irish grid reference using the\n   *         Ireland 1965 datum\n   * @since 1.1\n   */\n  public LatLng toLatLng() {\n    double N0 = FALSE_ORIGIN_NORTHING;\n    double E0 = FALSE_ORIGIN_EASTING;\n    double phi0 = Math.toRadians(FALSE_ORIGIN_LATITUDE);\n    double lambda0 = Math.toRadians(FALSE_ORIGIN_LONGITUDE);\n    double a = getDatum().getReferenceEllipsoid().getSemiMajorAxis();\n    double b = getDatum().getReferenceEllipsoid().getSemiMinorAxis();\n    double eSquared = getDatum().getReferenceEllipsoid()\n        .getEccentricitySquared();\n    double phi = 0.0;\n    double lambda = 0.0;\n    double E = this.easting;\n    double N = this.northing;\n    double n = (a - b) / (a + b);\n    double M = 0.0;\n    double phiPrime = ((N - N0) / (a * SCALE_FACTOR)) + phi0;\n    do {\n      M = (b * SCALE_FACTOR)\n          * (((1 + n + ((5.0 / 4.0) * n * n) + ((5.0 / 4.0) * n * n * n)) * (phiPrime - phi0))\n              - (((3 * n) + (3 * n * n) + ((21.0 / 8.0) * n * n * n))\n                  * Math.sin(phiPrime - phi0) * Math.cos(phiPrime + phi0))\n              + ((((15.0 / 8.0) * n * n) + ((15.0 / 8.0) * n * n * n))\n                  * Math.sin(2.0 * (phiPrime - phi0)) * Math\n                  .cos(2.0 * (phiPrime + phi0))) - (((35.0 / 24.0) * n * n * n)\n              * Math.sin(3.0 * (phiPrime - phi0)) * Math\n              .cos(3.0 * (phiPrime + phi0))));\n      phiPrime += (N - N0 - M) / (a * SCALE_FACTOR);\n    } while ((N - N0 - M) >= 0.001);\n    double v = a * SCALE_FACTOR\n        * Math.pow(1.0 - eSquared * Util.sinSquared(phiPrime), -0.5);\n    double rho = a * SCALE_FACTOR * (1.0 - eSquared)\n        * Math.pow(1.0 - eSquared * Util.sinSquared(phiPrime), -1.5);\n    double etaSquared = (v / rho) - 1.0;\n    double VII = Math.tan(phiPrime) / (2 * rho * v);\n    double VIII = (Math.tan(phiPrime) / (24.0 * rho * Math.pow(v, 3.0)))\n        * (5.0 + (3.0 * Util.tanSquared(phiPrime)) + etaSquared - (9.0 * Util\n            .tanSquared(phiPrime) * etaSquared));\n    double IX = (Math.tan(phiPrime) / (720.0 * rho * Math.pow(v, 5.0)))\n        * (61.0 + (90.0 * Util.tanSquared(phiPrime)) + (45.0 * Util\n            .tanSquared(phiPrime) * Util.tanSquared(phiPrime)));\n    double X = Util.sec(phiPrime) / v;\n    double XI = (Util.sec(phiPrime) / (6.0 * v * v * v))\n        * ((v / rho) + (2 * Util.tanSquared(phiPrime)));\n    double XII = (Util.sec(phiPrime) / (120.0 * Math.pow(v, 5.0)))\n        * (5.0 + (28.0 * Util.tanSquared(phiPrime)) + (24.0 * Util\n            .tanSquared(phiPrime) * Util.tanSquared(phiPrime)));\n    double XIIA = (Util.sec(phiPrime) / (5040.0 * Math.pow(v, 7.0)))\n        * (61.0 + (662.0 * Util.tanSquared(phiPrime))\n            + (1320.0 * Util.tanSquared(phiPrime) * Util.tanSquared(phiPrime)) + (720.0\n            * Util.tanSquared(phiPrime) * Util.tanSquared(phiPrime) * Util\n            .tanSquared(phiPrime)));\n    phi = phiPrime - (VII * Math.pow(E - E0, 2.0))\n        + (VIII * Math.pow(E - E0, 4.0)) - (IX * Math.pow(E - E0, 6.0));\n    lambda = lambda0 + (X * (E - E0)) - (XI * Math.pow(E - E0, 3.0))\n        + (XII * Math.pow(E - E0, 5.0)) - (XIIA * Math.pow(E - E0, 7.0));\n\n    return new LatLng(Math.toDegrees(phi), Math.toDegrees(lambda));\n  }\n\n\n  /**\n   * Get the easting in metres relative the origin of the British National Grid.\n   * \n   * @return the easting in metres.\n   * @since 1.1\n   */\n  public double getEasting() {\n\n    return easting;\n\n  }\n\n\n  /**\n   * Get the northing in metres relative to the origin of the British National\n   * Grid.\n   * \n   * @return the northing in metres.\n   * @since 1.1\n   */\n  public double getNorthing() {\n\n    return northing;\n\n  }\n\n\n  /**\n   * Set the easting for this OSRef.\n   * \n   * @param easting\n   *          the easting in metres. Must be greater than or equal to 0.0 and\n   *          less than 400000.0.\n   * @throws IllegalArgumentException\n   *           if the easting is invalid.\n   * @since 1.1\n   */\n  public void setEasting(double easting) throws IllegalArgumentException {\n\n    if (easting < 0.0 || easting >= 400000.0) {\n      throw new IllegalArgumentException(\"Easting (\" + easting\n          + \") is invalid. Must be greather than or equal to 0.0 and \"\n          + \"less than 400000.0.\");\n    }\n\n    this.easting = easting;\n\n  }\n\n\n  /**\n   * Set the northing for this OSRef\n   * \n   * @param northing\n   *          the northing in metres. Must be greater than or equal to 0.0 and\n   *          less than or equal to 500000.0.\n   * @throws IllegalArgumentException\n   *           if either the northing is invalid.\n   * @since 1.1\n   */\n  public void setNorthing(double northing) throws IllegalArgumentException {\n\n    if (northing < 0.0 || northing > 500000.0) {\n      throw new IllegalArgumentException(\"Northing (\" + northing\n          + \") is invalid. Must be greather than or equal to 0.0 and less \"\n          + \"than or equal to 500000.0.\");\n    }\n\n    this.northing = northing;\n\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/LatLng.java",
    "content": "package uk.me.jstott.jcoord;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.datum.WGS84Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Airy1830Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class to represent a latitude/longitude pair based on a particular datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 11-02-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.0\n */\npublic class LatLng {\n\n  /**\n   * Latitude in degrees.\n   */\n  private double latitude;\n\n  /**\n   * Longitude in degrees.\n   */\n  private double longitude;\n\n  /**\n   * Height.\n   */\n  private double height;\n\n  /**\n   * Datum of this reference.\n   */\n  private Datum datum = new WGS84Datum();\n\n  /**\n   * Latitude is north of the equator.\n   */\n  public static final int NORTH = 1;\n\n  /**\n   * Latitude is south of the equator.\n   */\n  public static final int SOUTH = -1;\n\n  /**\n   * Longitude is east of the prime meridian.\n   */\n  public static final int EAST = 1;\n\n  /**\n   * Longitude is west of the prime meridian.\n   */\n  public static final int WEST = -1;\n\n\n  /**\n   * Create a new LatLng object to represent a latitude/longitude pair using the\n   * WGS84 datum.\n   * \n   * @param latitude\n   *          the latitude in degrees. Must be between -90.0 and 90.0 inclusive.\n   *          -90.0 and 90.0 are effectively equivalent.\n   * @param longitude\n   *          the longitude in degrees. Must be between -180.0 and 180.0\n   *          inclusive. -180.0 and 180.0 are effectively equivalent.\n   * @throws IllegalArgumentException\n   *           if either the given latitude or the given longitude are invalid.\n   * @since 1.0\n   */\n  public LatLng(double latitude, double longitude) {\n    this(latitude, longitude, 0, new WGS84Datum());\n  }\n\n\n  /**\n   * Create a new LatLng object to represent a latitude/longitude pair using the\n   * WGS84 datum.\n   * \n   * @param latitude\n   *          the latitude in degrees. Must be between -90.0 and 90.0 inclusive.\n   *          -90.0 and 90.0 are effectively equivalent.\n   * @param longitude\n   *          the longitude in degrees. Must be between -180.0 and 180.0\n   *          inclusive. -180.0 and 180.0 are effectively equivalent.\n   * @param height\n   *          the perpendicular height above the reference ellipsoid.\n   * @throws IllegalArgumentException\n   *           if either the given latitude or the given longitude are invalid.\n   * @since 1.1\n   */\n  public LatLng(double latitude, double longitude, double height) {\n    this(latitude, longitude, height, new WGS84Datum());\n  }\n\n\n  /**\n   * Create a new LatLng object to represent a latitude/longitude pair using the\n   * WGS84 datum.\n   * \n   * @param latitudeDegrees\n   *          the degrees part of the latitude. Must be 0 <= latitudeDegrees <=\n   *          90.0.\n   * @param latitudeMinutes\n   *          the minutes part of the latitude. Must be 0 <= latitudeMinutes <\n   *          60.0.\n   * @param latitudeSeconds\n   *          the seconds part of the latitude. Must be 0 <= latitudeSeconds <\n   *          60.0.\n   * @param northSouth\n   *          whether the latitude is north or south of the equator. One of\n   *          LatLng.NORTH or LatLng.SOUTH.\n   * @param longitudeDegrees\n   *          the degrees part of the longitude. Must be 0 <= longitudeDegrees <=\n   *          90.0.\n   * @param longitudeMinutes\n   *          the minutes part of the longitude. Must be 0 <= longitudeMinutes <\n   *          60.0.\n   * @param longitudeSeconds\n   *          the seconds part of the longitude. Must be 0 <= longitudeSeconds <\n   *          60.0.\n   * @param eastWest\n   *          whether the longitude is east or west of the prime meridian. One\n   *          of LatLng.EAST or LatLng.WEST.\n   * @throws IllegalArgumentException\n   *           if any of the parameters are invalid.\n   * @since 1.1\n   */\n  public LatLng(int latitudeDegrees, int latitudeMinutes,\n      double latitudeSeconds, int northSouth, int longitudeDegrees,\n      int longitudeMinutes, double longitudeSeconds, int eastWest)\n      throws IllegalArgumentException {\n    this(latitudeDegrees, latitudeMinutes, latitudeSeconds, northSouth,\n        longitudeDegrees, longitudeMinutes, longitudeSeconds, eastWest, 0.0,\n        new WGS84Datum());\n  }\n\n\n  /**\n   * Create a new LatLng object to represent a latitude/longitude pair using the\n   * WGS84 datum.\n   * \n   * @param latitudeDegrees\n   *          the degrees part of the latitude. Must be 0 <= latitudeDegrees <=\n   *          90.0.\n   * @param latitudeMinutes\n   *          the minutes part of the latitude. Must be 0 <= latitudeMinutes <\n   *          60.0.\n   * @param latitudeSeconds\n   *          the seconds part of the latitude. Must be 0 <= latitudeSeconds <\n   *          60.0.\n   * @param northSouth\n   *          whether the latitude is north or south of the equator. One of\n   *          LatLng.NORTH or LatLng.SOUTH.\n   * @param longitudeDegrees\n   *          the degrees part of the longitude. Must be 0 <= longitudeDegrees <=\n   *          90.0.\n   * @param longitudeMinutes\n   *          the minutes part of the longitude. Must be 0 <= longitudeMinutes <\n   *          60.0.\n   * @param longitudeSeconds\n   *          the seconds part of the longitude. Must be 0 <= longitudeSeconds <\n   *          60.0.\n   * @param eastWest\n   *          whether the longitude is east or west of the prime meridian. One\n   *          of LatLng.EAST or LatLng.WEST.\n   * @param height\n   *          the perpendicular height above the reference ellipsoid.\n   * @throws IllegalArgumentException\n   *           if any of the parameters are invalid.\n   * @since 1.1\n   */\n  public LatLng(int latitudeDegrees, int latitudeMinutes,\n      double latitudeSeconds, int northSouth, int longitudeDegrees,\n      int longitudeMinutes, double longitudeSeconds, int eastWest, double height)\n      throws IllegalArgumentException {\n    this(latitudeDegrees, latitudeMinutes, latitudeSeconds, northSouth,\n        longitudeDegrees, longitudeMinutes, longitudeSeconds, eastWest, height,\n        new WGS84Datum());\n  }\n\n\n  /**\n   * Create a new LatLng object to represent a latitude/longitude pair using the\n   * specified datum.\n   * \n   * @param latitudeDegrees\n   *          the degrees part of the latitude. Must be 0 <= latitudeDegrees <=\n   *          90.0.\n   * @param latitudeMinutes\n   *          the minutes part of the latitude. Must be 0 <= latitudeMinutes <\n   *          60.0.\n   * @param latitudeSeconds\n   *          the seconds part of the latitude. Must be 0 <= latitudeSeconds <\n   *          60.0.\n   * @param northSouth\n   *          whether the latitude is north or south of the equator. One of\n   *          LatLng.NORTH or LatLng.SOUTH.\n   * @param longitudeDegrees\n   *          the degrees part of the longitude. Must be 0 <= longitudeDegrees <=\n   *          90.0.\n   * @param longitudeMinutes\n   *          the minutes part of the longitude. Must be 0 <= longitudeMinutes <\n   *          60.0.\n   * @param longitudeSeconds\n   *          the seconds part of the longitude. Must be 0 <= longitudeSeconds <\n   *          60.0.\n   * @param eastWest\n   *          whether the longitude is east or west of the prime meridian. One\n   *          of LatLng.EAST or LatLng.WEST.\n   * @param height\n   *          the perpendicular height above the reference ellipsoid.\n   * @param datum\n   *          the datum that this reference is based on.\n   * @throws IllegalArgumentException\n   *           if any of the parameters are invalid.\n   * @since 1.1\n   */\n  public LatLng(int latitudeDegrees, int latitudeMinutes,\n      double latitudeSeconds, int northSouth, int longitudeDegrees,\n      int longitudeMinutes, double longitudeSeconds, int eastWest,\n      double height, Datum datum) throws IllegalArgumentException {\n\n    if (latitudeDegrees < 0.0 || latitudeDegrees > 90.0\n        || latitudeMinutes < 0.0 || latitudeMinutes >= 60.0\n        || latitudeSeconds < 0.0 || latitudeSeconds >= 60.0\n        || (northSouth != SOUTH && northSouth != NORTH)) {\n      throw new IllegalArgumentException(\"Invalid latitude\");\n    }\n\n    if (longitudeDegrees < 0.0 || longitudeDegrees > 90.0\n        || longitudeMinutes < 0.0 || longitudeMinutes >= 60.0\n        || longitudeSeconds < 0.0 || longitudeSeconds >= 60.0\n        || (eastWest != SOUTH && eastWest != NORTH)) {\n      throw new IllegalArgumentException(\"Invalid longitude\");\n    }\n\n    this.latitude = northSouth\n        * (latitudeDegrees + (latitudeMinutes / 60.0) + (latitudeSeconds / 3600.0));\n    this.longitude = eastWest\n        * (longitudeDegrees + (longitudeMinutes / 60.0) + (longitudeSeconds / 3600.0));\n    this.datum = datum;\n\n  }\n\n\n  /**\n   * Create a new LatLng object to represent a latitude/longitude pair using the\n   * specified datum.\n   * \n   * @param latitude\n   *          the latitude in degrees. Must be between -90.0 and 90.0 inclusive.\n   *          -90.0 and 90.0 are effectively equivalent.\n   * @param longitude\n   *          the longitude in degrees. Must be between -180.0 and 180.0\n   *          inclusive. -180.0 and 180.0 are effectively equivalent.\n   * @param height\n   *          the perpendicular height above the reference ellipsoid.\n   * @param datum\n   *          the datum that this reference is based on.\n   * @throws IllegalArgumentException\n   *           if either the given latitude or the given longitude are invalid.\n   * @since 1.1\n   */\n  public LatLng(double latitude, double longitude, double height, Datum datum)\n      throws IllegalArgumentException {\n\n    if (latitude < -90.0 || latitude > 90.0) {\n      throw new IllegalArgumentException(\"Latitude (\" + latitude\n          + \") is invalid. Must be between -90.0 and 90.0 inclusive.\");\n    }\n\n    if (longitude < -180.0 || longitude > 180.0) {\n      throw new IllegalArgumentException(\"Longitude (\" + longitude\n          + \") is invalid. Must be between -180.0 and 180.0 inclusive.\");\n    }\n\n    this.latitude = latitude;\n    this.longitude = longitude;\n    this.height = height;\n    this.datum = datum;\n  }\n\n\n  /**\n   * Get a String representation of this LatLng object.\n   * \n   * @return a String representation of this LatLng object.\n   * @since 1.0\n   */\n  public String toString() {\n    return \"(\" + this.latitude + \", \" + this.longitude + \")\";\n  }\n\n\n  /**\n   * Return a String representation of this LatLng object in\n   * degrees-minutes-seconds format. The returned format will be like this: DD\n   * MM SS.SSS N DD MM SS.SSS E where DD is the number of degrees, MM is the\n   * number of minutes, SS.SSS is the number of seconds, N is either N or S to\n   * indicate north or south of the equator and E is either E or W to indicate\n   * east or west of the prime meridian.\n   * \n   * @return a String representation of this LatLng object in DMS format.\n   * @since 1.1\n   */\n  public String toDMSString() {\n    String ret = formatLatitude() + \" \" + formatLongitude();\n\n    return ret;\n  }\n\n\n  /**\n   * Format the latitude into degrees-minutes-seconds format.\n   * \n   * @return the formatted String\n   * @since 1.1\n   */\n  private String formatLatitude() {\n    String ns = getLatitude() >= 0 ? \"N\" : \"S\";\n    return Math.abs(getLatitudeDegrees()) + \" \" + getLatitudeMinutes() + \" \"\n        + getLatitudeSeconds() + \" \" + ns;\n  }\n\n\n  /**\n   * Format the longitude into degrees-minutes-seconds format.\n   * \n   * @return the formatted String.\n   * @since 1.1\n   */\n  private String formatLongitude() {\n    String ew = getLongitude() >= 0 ? \"E\" : \"W\";\n    return Math.abs(getLongitudeDegrees()) + \" \" + getLongitudeMinutes() + \" \"\n        + getLongitudeSeconds() + \" \" + ew;\n  }\n\n\n  /**\n   * Convert this latitude and longitude into an OSGB (Ordnance Survey of Great\n   * Britain) grid reference.\n   * \n   * @return the converted OSGB grid reference.\n   * @since 1.0\n   */\n  public OSRef toOSRef() {\n    Airy1830Ellipsoid airy1830 = Airy1830Ellipsoid.getInstance();\n    double OSGB_F0 = 0.9996012717;\n    double N0 = -100000.0;\n    double E0 = 400000.0;\n    double phi0 = Math.toRadians(49.0);\n    double lambda0 = Math.toRadians(-2.0);\n    double a = airy1830.getSemiMajorAxis();\n    double b = airy1830.getSemiMinorAxis();\n    double eSquared = airy1830.getEccentricitySquared();\n    double phi = Math.toRadians(getLat());\n    double lambda = Math.toRadians(getLng());\n    double E = 0.0;\n    double N = 0.0;\n    double n = (a - b) / (a + b);\n    double v = a * OSGB_F0\n        * Math.pow(1.0 - eSquared * Util.sinSquared(phi), -0.5);\n    double rho = a * OSGB_F0 * (1.0 - eSquared)\n        * Math.pow(1.0 - eSquared * Util.sinSquared(phi), -1.5);\n    double etaSquared = (v / rho) - 1.0;\n    double M = (b * OSGB_F0)\n        * (((1 + n + ((5.0 / 4.0) * n * n) + ((5.0 / 4.0) * n * n * n)) * (phi - phi0))\n            - (((3 * n) + (3 * n * n) + ((21.0 / 8.0) * n * n * n))\n                * Math.sin(phi - phi0) * Math.cos(phi + phi0))\n            + ((((15.0 / 8.0) * n * n) + ((15.0 / 8.0) * n * n * n))\n                * Math.sin(2.0 * (phi - phi0)) * Math.cos(2.0 * (phi + phi0))) - (((35.0 / 24.0)\n            * n * n * n)\n            * Math.sin(3.0 * (phi - phi0)) * Math.cos(3.0 * (phi + phi0))));\n    double I = M + N0;\n    double II = (v / 2.0) * Math.sin(phi) * Math.cos(phi);\n    double III = (v / 24.0) * Math.sin(phi) * Math.pow(Math.cos(phi), 3.0)\n        * (5.0 - Util.tanSquared(phi) + (9.0 * etaSquared));\n    double IIIA = (v / 720.0) * Math.sin(phi) * Math.pow(Math.cos(phi), 5.0)\n        * (61.0 - (58.0 * Util.tanSquared(phi)) + Math.pow(Math.tan(phi), 4.0));\n    double IV = v * Math.cos(phi);\n    double V = (v / 6.0) * Math.pow(Math.cos(phi), 3.0)\n        * ((v / rho) - Util.tanSquared(phi));\n    double VI = (v / 120.0)\n        * Math.pow(Math.cos(phi), 5.0)\n        * (5.0 - (18.0 * Util.tanSquared(phi)) + (Math.pow(Math.tan(phi), 4.0))\n            + (14 * etaSquared) - (58 * Util.tanSquared(phi) * etaSquared));\n\n    N = I + (II * Math.pow(lambda - lambda0, 2.0))\n        + (III * Math.pow(lambda - lambda0, 4.0))\n        + (IIIA * Math.pow(lambda - lambda0, 6.0));\n    E = E0 + (IV * (lambda - lambda0)) + (V * Math.pow(lambda - lambda0, 3.0))\n        + (VI * Math.pow(lambda - lambda0, 5.0));\n\n    return new OSRef(E, N);\n  }\n\n\n  /**\n   * Convert this latitude and longitude to a UTM reference.\n   * \n   * @return the converted UTM reference.\n   * @throws NotDefinedOnUTMGridException\n   *           if an attempt is made to convert a LatLng that falls outside the\n   *           area covered by the UTM grid. The UTM grid is only defined for\n   *           latitudes south of 84&deg;N and north of 80&deg;S.\n   * @since 1.0\n   */\n  public UTMRef toUTMRef() throws NotDefinedOnUTMGridException {\n\n    if (getLatitude() < -80 || getLatitude() > 84) {\n      throw new NotDefinedOnUTMGridException(\"Latitude (\" + getLatitude()\n          + \") falls outside the UTM grid.\");\n    }\n\n    if (this.longitude == 180.0) {\n      this.longitude = -180.0;\n    }\n\n    double UTM_F0 = 0.9996;\n    double a = uk.me.jstott.jcoord.ellipsoid.WGS84Ellipsoid.getInstance().getSemiMajorAxis();\n    double eSquared = uk.me.jstott.jcoord.ellipsoid.WGS84Ellipsoid.getInstance().getEccentricitySquared();\n    double longitude = this.longitude;\n    double latitude = this.latitude;\n\n    double latitudeRad = latitude * (Math.PI / 180.0);\n    double longitudeRad = longitude * (Math.PI / 180.0);\n    int longitudeZone = (int) Math.floor((longitude + 180.0) / 6.0) + 1;\n\n    // Special zone for Norway\n    if (latitude >= 56.0 && latitude < 64.0 && longitude >= 3.0\n        && longitude < 12.0) {\n      longitudeZone = 32;\n    }\n\n    // Special zones for Svalbard\n    if (latitude >= 72.0 && latitude < 84.0) {\n      if (longitude >= 0.0 && longitude < 9.0) {\n        longitudeZone = 31;\n      } else if (longitude >= 9.0 && longitude < 21.0) {\n        longitudeZone = 33;\n      } else if (longitude >= 21.0 && longitude < 33.0) {\n        longitudeZone = 35;\n      } else if (longitude >= 33.0 && longitude < 42.0) {\n        longitudeZone = 37;\n      }\n    }\n\n    double longitudeOrigin = (longitudeZone - 1) * 6 - 180 + 3;\n    double longitudeOriginRad = longitudeOrigin * (Math.PI / 180.0);\n\n    char UTMZone = UTMRef.getUTMLatitudeZoneLetter(latitude);\n\n    double ePrimeSquared = (eSquared) / (1 - eSquared);\n\n    double n = a\n        / Math.sqrt(1 - eSquared * Math.sin(latitudeRad)\n            * Math.sin(latitudeRad));\n    double t = Math.tan(latitudeRad) * Math.tan(latitudeRad);\n    double c = ePrimeSquared * Math.cos(latitudeRad) * Math.cos(latitudeRad);\n    double A = Math.cos(latitudeRad) * (longitudeRad - longitudeOriginRad);\n\n    double M = a\n        * ((1 - eSquared / 4 - 3 * eSquared * eSquared / 64 - 5 * eSquared\n            * eSquared * eSquared / 256)\n            * latitudeRad\n            - (3 * eSquared / 8 + 3 * eSquared * eSquared / 32 + 45 * eSquared\n                * eSquared * eSquared / 1024)\n            * Math.sin(2 * latitudeRad)\n            + (15 * eSquared * eSquared / 256 + 45 * eSquared * eSquared\n                * eSquared / 1024) * Math.sin(4 * latitudeRad) - (35 * eSquared\n            * eSquared * eSquared / 3072)\n            * Math.sin(6 * latitudeRad));\n\n    double UTMEasting = (UTM_F0\n        * n\n        * (A + (1 - t + c) * Math.pow(A, 3.0) / 6 + (5 - 18 * t + t * t + 72\n            * c - 58 * ePrimeSquared)\n            * Math.pow(A, 5.0) / 120) + 500000.0);\n\n    double UTMNorthing = (UTM_F0 * (M + n\n        * Math.tan(latitudeRad)\n        * (A * A / 2 + (5 - t + (9 * c) + (4 * c * c)) * Math.pow(A, 4.0) / 24 + (61\n            - (58 * t) + (t * t) + (600 * c) - (330 * ePrimeSquared))\n            * Math.pow(A, 6.0) / 720)));\n\n    // Adjust for the southern hemisphere\n    if (latitude < 0) {\n      UTMNorthing += 10000000.0;\n    }\n\n    return new UTMRef(longitudeZone, UTMZone, UTMEasting, UTMNorthing);\n  }\n\n\n  /**\n   * Convert this latitude and longitude to an MGRS reference.\n   * \n   * @return the converted MGRS reference\n   * @since 1.1\n   */\n  public MGRSRef toMGRSRef() {\n    UTMRef utm = toUTMRef();\n    return new MGRSRef(utm);\n  }\n\n\n  /**\n   * Convert this LatLng from the OSGB36 datum to the WGS84 datum using an\n   * approximate Helmert transformation.\n   * \n   * @since 1.0\n   */\n  public void toWGS84() {\n    double a = Airy1830Ellipsoid.getInstance().getSemiMajorAxis();\n    double eSquared = Airy1830Ellipsoid.getInstance().getEccentricitySquared();\n    double phi = Math.toRadians(latitude);\n    double lambda = Math.toRadians(longitude);\n    double v = a / (Math.sqrt(1 - eSquared * Util.sinSquared(phi)));\n    double H = 0; // height\n    double x = (v + H) * Math.cos(phi) * Math.cos(lambda);\n    double y = (v + H) * Math.cos(phi) * Math.sin(lambda);\n    double z = ((1 - eSquared) * v + H) * Math.sin(phi);\n\n    double tx = 446.448;\n    // ty : Incorrect value in v1.0 (-124.157). Corrected in v1.1.\n    double ty = -125.157;\n    double tz = 542.060;\n    double s = -0.0000204894;\n    double rx = Math.toRadians(0.00004172222);\n    double ry = Math.toRadians(0.00006861111);\n    double rz = Math.toRadians(0.00023391666);\n\n    double xB = tx + (x * (1 + s)) + (-rx * y) + (ry * z);\n    double yB = ty + (rz * x) + (y * (1 + s)) + (-rx * z);\n    double zB = tz + (-ry * x) + (rx * y) + (z * (1 + s));\n\n    a = uk.me.jstott.jcoord.ellipsoid.WGS84Ellipsoid.getInstance().getSemiMajorAxis();\n    eSquared = uk.me.jstott.jcoord.ellipsoid.WGS84Ellipsoid.getInstance()\n        .getEccentricitySquared();\n\n    double lambdaB = Math.toDegrees(Math.atan(yB / xB));\n    double p = Math.sqrt((xB * xB) + (yB * yB));\n    double phiN = Math.atan(zB / (p * (1 - eSquared)));\n    for (int i = 1; i < 10; i++) {\n      v = a / (Math.sqrt(1 - eSquared * Util.sinSquared(phiN)));\n      double phiN1 = Math.atan((zB + (eSquared * v * Math.sin(phiN))) / p);\n      phiN = phiN1;\n    }\n\n    double phiB = Math.toDegrees(phiN);\n\n    latitude = phiB;\n    longitude = lambdaB;\n  }\n\n\n  /**\n   * \n   * \n   * @param d\n   * @since 1.1\n   */\n  public void toDatum(Datum d) {\n\n    double invert = 1;\n\n    if (!(datum instanceof WGS84Datum) && !(d instanceof WGS84Datum)) {\n      toDatum(new WGS84Datum());\n    } else {\n      if (d instanceof WGS84Datum) {\n        // Don't do anything if datum and d are both WGS84.\n        return;\n      }\n      invert = -1;\n    }\n\n    double a = datum.getReferenceEllipsoid().getSemiMajorAxis();\n    double eSquared = datum.getReferenceEllipsoid().getEccentricitySquared();\n    double phi = Math.toRadians(latitude);\n    double lambda = Math.toRadians(longitude);\n    double v = a / (Math.sqrt(1 - eSquared * Util.sinSquared(phi)));\n    double H = height; // height\n    double x = (v + H) * Math.cos(phi) * Math.cos(lambda);\n    double y = (v + H) * Math.cos(phi) * Math.sin(lambda);\n    double z = ((1 - eSquared) * v + H) * Math.sin(phi);\n\n    double dx = invert * d.getDx();// 446.448;\n    double dy = invert * d.getDy();// -125.157;\n    double dz = invert * d.getDz();// 542.060;\n    double ds = invert * d.getDs() / 1000000.0;// -0.0000204894;\n    double rx = invert * Math.toRadians(d.getRx() / 3600.0);// Math.toRadians(0.00004172222);\n    double ry = invert * Math.toRadians(d.getRy() / 3600.0);// Math.toRadians(0.00006861111);\n    double rz = invert * Math.toRadians(d.getRz() / 3600.0);// Math.toRadians(0.00023391666);\n\n    double sc = 1 + ds;\n    double xB = dx + (x * sc) + ((-rx * y) * sc) + ((ry * z) * sc);\n    double yB = dy + ((rz * x) * sc) + (y * sc) + ((-rx * z) * sc);\n    double zB = dz + ((-ry * x) * sc) + ((rx * y) * sc) + (z * sc);\n\n    a = d.getReferenceEllipsoid().getSemiMajorAxis();\n    eSquared = d.getReferenceEllipsoid().getEccentricitySquared();\n\n    double lambdaB = Math.toDegrees(Math.atan(yB / xB));\n    double p = Math.sqrt((xB * xB) + (yB * yB));\n    double phiN = Math.atan(zB / (p * (1 - eSquared)));\n    for (int i = 1; i < 10; i++) {\n      v = a / (Math.sqrt(1 - eSquared * Util.sinSquared(phiN)));\n      double phiN1 = Math.atan((zB + (eSquared * v * Math.sin(phiN))) / p);\n      phiN = phiN1;\n    }\n\n    double phiB = Math.toDegrees(phiN);\n\n    latitude = phiB;\n    longitude = lambdaB;\n  }\n\n\n  /**\n   * Convert this LatLng from the WGS84 datum to the OSGB36 datum using an\n   * approximate Helmert transformation.\n   * \n   * @since 1.0\n   */\n  public void toOSGB36() {\n    uk.me.jstott.jcoord.ellipsoid.WGS84Ellipsoid wgs84 = uk.me.jstott.jcoord.ellipsoid.WGS84Ellipsoid\n        .getInstance();\n    double a = wgs84.getSemiMajorAxis();\n    double eSquared = wgs84.getEccentricitySquared();\n    double phi = Math.toRadians(this.latitude);\n    double lambda = Math.toRadians(this.longitude);\n    double v = a / (Math.sqrt(1 - eSquared * Util.sinSquared(phi)));\n    double H = 0; // height\n    double x = (v + H) * Math.cos(phi) * Math.cos(lambda);\n    double y = (v + H) * Math.cos(phi) * Math.sin(lambda);\n    double z = ((1 - eSquared) * v + H) * Math.sin(phi);\n\n    double tx = -446.448;\n    // ty : Incorrect value in v1.0 (124.157). Corrected in v1.1.\n    double ty = 125.157;\n    double tz = -542.060;\n    double s = 0.0000204894;\n    double rx = Math.toRadians(-0.00004172222);\n    double ry = Math.toRadians(-0.00006861111);\n    double rz = Math.toRadians(-0.00023391666);\n\n    double xB = tx + (x * (1 + s)) + (-rx * y) + (ry * z);\n    double yB = ty + (rz * x) + (y * (1 + s)) + (-rx * z);\n    double zB = tz + (-ry * x) + (rx * y) + (z * (1 + s));\n\n    a = Airy1830Ellipsoid.getInstance().getSemiMajorAxis();\n    eSquared = Airy1830Ellipsoid.getInstance().getEccentricitySquared();\n\n    double lambdaB = Math.toDegrees(Math.atan(yB / xB));\n    double p = Math.sqrt((xB * xB) + (yB * yB));\n    double phiN = Math.atan(zB / (p * (1 - eSquared)));\n    for (int i = 1; i < 10; i++) {\n      v = a / (Math.sqrt(1 - eSquared * Util.sinSquared(phiN)));\n      double phiN1 = Math.atan((zB + (eSquared * v * Math.sin(phiN))) / p);\n      phiN = phiN1;\n    }\n\n    double phiB = Math.toDegrees(phiN);\n\n    latitude = phiB;\n    longitude = lambdaB;\n  }\n\n\n  /**\n   * Calculate the surface distance in kilometres from this LatLng to the given\n   * LatLng.\n   * \n   * @param ll\n   *          the LatLng object to measure the distance to.\n   * @return the surface distance in kilometres.\n   * @since 1.0\n   */\n  public double distance(LatLng ll) {\n    double er = 6366.707;\n\n    double latFrom = Math.toRadians(getLat());\n    double latTo = Math.toRadians(ll.getLat());\n    double lngFrom = Math.toRadians(getLng());\n    double lngTo = Math.toRadians(ll.getLng());\n\n    double d = Math.acos(Math.sin(latFrom) * Math.sin(latTo)\n        + Math.cos(latFrom) * Math.cos(latTo) * Math.cos(lngTo - lngFrom))\n        * er;\n\n    return d;\n  }\n\n\n  /**\n   * Calculate the surface distance in miles from this LatLng to the given\n   * LatLng.\n   * \n   * @param ll\n   *          the LatLng object to measure the distance to.\n   * @return the surface distance in miles.\n   * @since 1.1\n   */\n  public double distanceMiles(LatLng ll) {\n    return distance(ll) / 1.609344;\n  }\n\n\n  /**\n   * Return the latitude in degrees.\n   * \n   * @return the latitude in degrees.\n   * @since 1.0\n   * @deprecated Use {@link #getLatitude() getLatitude()} instead.\n   */\n  public double getLat() {\n    return latitude;\n  }\n\n\n  /**\n   * Return the latitude in degrees.\n   * \n   * @return the latitude in degrees.\n   * @since 1.1\n   */\n  public double getLatitude() {\n    return latitude;\n  }\n\n\n  /**\n   * \n   * \n   * @return\n   * @since 1.1\n   */\n  public int getLatitudeDegrees() {\n    double ll = getLatitude();\n    int deg = (int) Math.floor(ll);\n    double minx = ll - deg;\n    if (ll < 0 && minx != 0.0) {\n      deg++;\n    }\n    return deg;\n  }\n\n\n  /**\n   * \n   * \n   * @return\n   * @since 1.1\n   */\n  public int getLatitudeMinutes() {\n    double ll = getLatitude();\n    int deg = (int) Math.floor(ll);\n    double minx = ll - deg;\n    if (ll < 0 && minx != 0.0) {\n      minx = 1 - minx;\n    }\n    int min = (int) Math.floor(minx * 60);\n    return min;\n  }\n\n\n  /**\n   * \n   * \n   * @return\n   * @since 1.1\n   */\n  public double getLatitudeSeconds() {\n    double ll = getLatitude();\n    int deg = (int) Math.floor(ll);\n    double minx = ll - deg;\n    if (ll < 0 && minx != 0.0) {\n      minx = 1 - minx;\n    }\n    int min = (int) Math.floor(minx * 60);\n    double sec = ((minx * 60) - min) * 60;\n    return sec;\n  }\n\n\n  /**\n   * Return the longitude in degrees.\n   * \n   * @return the longitude in degrees.\n   * @since 1.0\n   * @deprecated Use {@link #getLongitude() getLongitude()} instead.\n   */\n  public double getLng() {\n    return longitude;\n  }\n\n\n  /**\n   * Return the longitude in degrees.\n   * \n   * @return the longitude in degrees.\n   * @since 1.0\n   */\n  public double getLongitude() {\n    return longitude;\n  }\n\n\n  /**\n   * \n   * \n   * @return\n   * @since 1.1\n   */\n  public int getLongitudeDegrees() {\n    double ll = getLongitude();\n    int deg = (int) Math.floor(ll);\n    double minx = ll - deg;\n    if (ll < 0 && minx != 0.0) {\n      deg++;\n    }\n    return deg;\n  }\n\n\n  /**\n   * \n   * \n   * @return\n   * @since 1.1\n   */\n  public int getLongitudeMinutes() {\n    double ll = getLongitude();\n    int deg = (int) Math.floor(ll);\n    double minx = ll - deg;\n    if (ll < 0 && minx != 0.0) {\n      minx = 1 - minx;\n    }\n    int min = (int) Math.floor(minx * 60);\n    return min;\n  }\n\n\n  /**\n   * \n   * \n   * @return\n   * @since 1.1\n   */\n  public double getLongitudeSeconds() {\n    double ll = getLongitude();\n    int deg = (int) Math.floor(ll);\n    double minx = ll - deg;\n    if (ll < 0 && minx != 0.0) {\n      minx = 1 - minx;\n    }\n    int min = (int) Math.floor(minx * 60);\n    double sec = ((minx * 60) - min) * 60;\n    return sec;\n  }\n\n\n  /**\n   * Get the height.\n   * \n   * @return the height.\n   * @since 1.1\n   */\n  public double getHeight() {\n    return height;\n  }\n\n\n  /**\n   * Get the datum.\n   * \n   * @return the datum.\n   * @since 1.1\n   */\n  public Datum getDatum() {\n    return datum;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/MGRSRef.java",
    "content": "package uk.me.jstott.jcoord;\n\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport uk.me.jstott.jcoord.datum.WGS84Datum;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class to represent a Military Grid Reference System (MGRS) reference.\n * </p>\n * \n * <p>\n * <h3>Military Grid Reference System (MGRS)</h3>\n * </p>\n * \n * <p>\n * The Military Grid Reference System (MGRS) is an extension of the Universal\n * Transverse Mercator (UTM) reference system. An MGRS reference is made from 5\n * parts:\n * </p>\n * \n * <h4>UTM Longitude Zone</h4>\n * \n * <p>\n * This is a number indicating which UTM longitude zone the reference falls\n * into. Zones are numbered from 1 (starting at 180&deg;W) through 60. Each zone\n * is 6&deg; wide.\n * </p>\n * \n * <h4>UTM Latitude Zone</h4>\n * \n * <p>\n * Latitude is split into regions that are 8&deg; high, starting at 80&deg;S.\n * Latitude zones are lettered using C through X, but omitting I and O as they\n * can easily be confused with the numbers 1 and 0.\n * </p>\n * \n * <h4>100,000m Square identification</h4>\n * \n * <p>\n * Each UTM zone is treated as a square 100,000m to a side. The 50,000m easting\n * is centred on the centre-point of the UTM zone. 100,000m squares are\n * identified using two characters - one to identify the row and one to identify\n * the column.\n * </p>\n * \n * <p>\n * Row identifiers use the characters A through V (omitting I and O again). The\n * sequence is repeated every 2,000,000m from the equator. If the UTM longitude\n * zone is odd, then the lettering is advanced by five characters to start at F.\n * </p>\n * \n * <p>\n * Column identifiers use the characters A through Z (again omitting I and O).\n * </p>\n * \n * <h4>Easting and northing</h4>\n * \n * <p>\n * Each 100,000m grid square is further divided into smaller squares\n * representing 1m, 10m, 100m, 1,000m and 10,000m precision. The easting and\n * northing are given using the numeric row and column reference of the square,\n * starting at the bottom-left corner of the square.\n * </p>\n * \n * <h4>MGRS Reference Example</h4>\n * \n * <p>\n * 18SUU8362601432 is an example of an MGRS reference. '18' is the UTM longitude\n * zone, 'S' is the UTM latitude zone, 'UU' is the 100,000m square\n * identification, 83626 is the easting reference to 1m precision and 01432 is\n * the northing reference to 1m precision.\n * </p>\n * \n * <h3>MGRSRef</h3>\n * \n * <p>\n * Methods are provided to query an <code>MGRSRef</code> object for its\n * parameters. As MGRS references are related to UTM references, a\n * <code>{@link MGRSRef#toUTMRef() toUTMRef()}</code> method is provided to\n * convert an <code>MGRSRef</code> object into a <code>{@link UTMRef}</code>\n * object. The reverse conversion can be made using the\n * <code>{@link #MGRSRef(UTMRef) MGRSRef(UTMRef)}</code> constructor.\n * </p>\n * \n * <p>\n * <code>MGRSRef</code> objects can be converted to\n * <code>{@link LatLng LatLng}</code> objects using the\n * <code>{@link MGRSRef#toLatLng() toLatLng()}</code> method. The reverse\n * conversion is made using the\n * <code>{@link LatLng#toMGRSRef() LatLng.toMGRSRef()}</code> method.\n * </p>\n * \n * <p>\n * Some MGRS references use the Bessel 1841 ellipsoid rather than the Geodetic\n * Reference System 1980 (GRS 1980), International or World Geodetic System 1984\n * (WGS84) ellipsoids. Use the constructors with the optional boolean parameter\n * to be able to specify whether your MGRS reference uses the Bessel 1841\n * ellipsoid. Note that no automatic determination of the correct ellipsoid to\n * use is made.\n * </p>\n * \n * <p>\n * <b>Important note</b>: There is currently no support for MGRS references in\n * polar regions north of 84&deg;N and south of 80&deg;S. There is also no\n * account made for UTM zones with slightly different sizes to normal around\n * Svalbard and Norway.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 25-Feb-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class MGRSRef extends CoordinateSystem {\n\n  /**\n   * \n   */\n  private int utmZoneNumber;\n\n  /**\n   * \n   */\n  private char utmZoneChar;\n\n  /**\n   * \n   */\n  private char eastingID;\n\n  /**\n   * \n   */\n  private char northingID;\n\n  /**\n   * \n   */\n  private int easting;\n\n  /**\n   * \n   */\n  private int northing;\n\n  /**\n   * The initial precision of this MGRS reference. Must be one of\n   * MGRSRef.PRECISION_1M, MGRSRef.PRECISION_10M, MGRSRef.PRECISION_100M,\n   * MGRSRef.PRECISION_1000M or MGRSRef.PRECISION_10000M.\n   */\n  private int precision;\n\n  /**\n   * \n   */\n  private boolean isBessel;\n\n  /**\n   * Used to indicate a required precision of 10000m (10km).\n   */\n  public static final int PRECISION_10000M = 10000;\n\n  /**\n   * Used to indicate a required precision of 1000m (1km).\n   */\n  public static final int PRECISION_1000M = 1000;\n\n  /**\n   * Used to indicate a required precision of 100m.\n   */\n  public static final int PRECISION_100M = 100;\n\n  /**\n   * Used to indicate a required precision of 10m.\n   */\n  public static final int PRECISION_10M = 10;\n\n  /**\n   * Used to indicate a required precision of 1m.\n   */\n  public static final int PRECISION_1M = 1;\n\n  /**\n   * Northing characters\n   */\n  private static final char[] northingIDs =\n      new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M',\n          'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V' };\n\n\n  /**\n   * Create a new MGRS reference object from the given UTM reference. It is\n   * assumed that the UTMRef object is valid.\n   * \n   * @param utm\n   *          a UTM reference.\n   * @since 1.1\n   */\n  public MGRSRef(UTMRef utm) {\n    this(utm, false);\n  }\n\n\n  /**\n   * Create a new MGRS reference object from the given UTM reference. It is\n   * assumed that this MGRS reference represents a point using the GRS 1980,\n   * International or WGS84 ellipsoids. It is assumed that the UTMRef object is\n   * valid.\n   * \n   * @param utm\n   *          a UTM reference.\n   * @param isBessel\n   *          true if the parameters represent an MGRS reference using the\n   *          Bessel 1841 ellipsoid; false is the parameters represent an MGRS\n   *          reference using the GRS 1980, International or WGS84 ellipsoids.\n   * @since 1.1\n   */\n  public MGRSRef(UTMRef utm, boolean isBessel) {\n    \n    super(WGS84Datum.getInstance());\n\n    int lngZone = utm.getLngZone();\n    int set = ((lngZone - 1) % 6) + 1;\n    int eID =\n        (int) Math.floor(utm.getEasting() / 100000.0) + (8 * ((set - 1) % 3));\n    int nID = (int) Math.floor((utm.getNorthing() % 2000000) / 100000.0);\n\n    if (eID > 8)\n      eID++; // Offset for no I character\n    if (eID > 14)\n      eID++; // Offset for no O character\n\n    char eIDc = (char) (eID + 64);\n\n    // Northing ID offset for sets 2, 4 and 6\n    if (set % 2 == 0) {\n      nID += 5;\n    }\n\n    if (isBessel) {\n      nID += 10;\n    }\n    \n    if (nID > 19) {\n      nID -= 20;\n    }\n\n    char nIDc = northingIDs[nID];\n\n    this.utmZoneNumber = lngZone;\n    this.utmZoneChar = utm.getLatZone();\n    this.eastingID = eIDc;\n    this.northingID = nIDc;\n    this.easting = (int) Math.round(utm.getEasting()) % 100000;\n    this.northing = (int) Math.round(utm.getNorthing()) % 100000;\n    this.precision = PRECISION_1M;\n    this.isBessel = isBessel;\n\n  }\n\n\n  /**\n   * Create a new MGRS reference object from the given paramaters. It is assumed\n   * that this MGRS reference represents a point using the GRS 1980,\n   * International or WGS84 ellipsoids. An IllegalArgumentException is thrown if\n   * any of the parameters are invalid.\n   * \n   * @param utmZoneNumber\n   *          the UTM zone number representing the longitude.\n   * @param utmZoneChar\n   *          the UTM zone character representing the latitude.\n   * @param eastingID\n   *          character representing the 100,000km easting square.\n   * @param northingID\n   *          character representing the 100,000km easting square.\n   * @param easting\n   *          easting in metres.\n   * @param northing\n   *          northing in metres.\n   * @param precision\n   *          the precision of the given easting and northing. Must be one of\n   *          MGRSRef.PRECISION_1M, MGRSRef.PRECISION_10M,\n   *          MGRSRef.PRECISION_100M, MGRSRef.PRECISION_1000M or\n   *          MGRSRef.PRECISION_10000M.\n   * @throws IllegalArgumentException\n   *           if any of the given parameters are invalid.\n   * @since 1.1\n   */\n  public MGRSRef(int utmZoneNumber, char utmZoneChar, char eastingID,\n      char northingID, int easting, int northing, int precision)\n      throws IllegalArgumentException {\n    this(utmZoneNumber, utmZoneChar, eastingID, northingID, easting, northing,\n        precision, false);\n  }\n\n\n  /**\n   * Create a new MGRS reference object from the given paramaters. An\n   * IllegalArgumentException is thrown if any of the parameters are invalid.\n   * \n   * @param utmZoneNumber\n   *          the UTM zone number representing the longitude.\n   * @param utmZoneChar\n   *          the UTM zone character representing the latitude.\n   * @param eastingID\n   *          character representing the 100,000km easting square.\n   * @param northingID\n   *          character representing the 100,000km easting square.\n   * @param easting\n   *          easting in metres.\n   * @param northing\n   *          northing in metres.\n   * @param precision\n   *          the precision of the given easting and northing. Must be one of\n   *          MGRSRef.PRECISION_1M, MGRSRef.PRECISION_10M,\n   *          MGRSRef.PRECISION_100M, MGRSRef.PRECISION_1000M or\n   *          MGRSRef.PRECISION_10000M.\n   * @param isBessel\n   *          true if the parameters represent an MGRS reference using the\n   *          Bessel 1841 ellipsoid; false is the parameters represent an MGRS\n   *          reference using the GRS 1980, International or WGS84 ellipsoids.\n   * @throws IllegalArgumentException\n   *           if any of the given parameters are invalid. Note that the\n   *           parameters are only checked for the range of values that they can\n   *           take on. Being able to create an MGRSRef object does not\n   *           necessarily imply that the reference is valid.\n   * @since 1.1\n   */\n  public MGRSRef(int utmZoneNumber, char utmZoneChar, char eastingID,\n      char northingID, int easting, int northing, int precision,\n      boolean isBessel) throws IllegalArgumentException {\n    \n    super(WGS84Datum.getInstance());\n\n    if (utmZoneNumber < 1 || utmZoneNumber > 60) {\n      throw new IllegalArgumentException(\"Invalid utmZoneNumber (\"\n          + utmZoneNumber + \")\");\n    }\n    if (utmZoneChar < 'A' || utmZoneChar > 'Z') {\n      throw new IllegalArgumentException(\"Invalid utmZoneChar (\" + utmZoneChar\n          + \")\");\n    }\n    if (eastingID < 'A' || eastingID > 'Z' || eastingID == 'I'\n        || eastingID == 'O') {\n      throw new IllegalArgumentException(\"Invalid eastingID (\" + eastingID\n          + \")\");\n    }\n    if (northingID < 'A' || northingID > 'Z' || northingID == 'I'\n        || northingID == 'O') {\n      throw new IllegalArgumentException(\"Invalid northingID (\" + northingID\n          + \")\");\n    }\n    if (easting < 0 || easting > 99999) {\n      throw new IllegalArgumentException(\"Invalid easting (\" + easting + \")\");\n    }\n    if (northing < 0 || northing > 99999) {\n      throw new IllegalArgumentException(\"Invalid northing (\" + northing + \")\");\n    }\n    if (precision != PRECISION_1M && precision != PRECISION_10M\n        && precision != PRECISION_100M && precision != PRECISION_1000M\n        && precision != PRECISION_10000M) {\n      throw new IllegalArgumentException(\"Invalid precision (\" + precision\n          + \")\");\n    }\n\n    this.utmZoneNumber = utmZoneNumber;\n    this.utmZoneChar = utmZoneChar;\n    this.eastingID = eastingID;\n    this.northingID = northingID;\n    this.easting = easting;\n    this.northing = northing;\n    this.precision = precision;\n    this.isBessel = isBessel;\n  }\n\n\n  /**\n   * Create a new MGRS reference object from the given String. Must be correctly\n   * formatted otherwise an IllegalArgumentException will be thrown. It is\n   * assumed that this MGRS reference represents a point using the GRS 1980,\n   * International or WGS84 ellipsoids.\n   * \n   * @param ref\n   *          a String to create an MGRS reference from.\n   * @throws IllegalArgumentException\n   *           if the given String is not correctly. formatted\n   * @since 1.1\n   */\n  public MGRSRef(String ref) throws IllegalArgumentException {\n    this(ref, false);\n  }\n\n\n  /**\n   * Create a new MGRS reference object from the given String. Must be correctly\n   * formatted otherwise an IllegalArgumentException will be thrown.\n   * \n   * @param ref\n   *          a String to create an MGRS reference from.\n   * @param isBessel\n   *          true if the parameters represent an MGRS reference using the\n   *          Bessel 1841 ellipsoid; false is the parameters represent an MGRS\n   *          reference using the GRS 1980, International or WGS84 ellipsoids.\n   * @throws IllegalArgumentException\n   *           if the given String is not correctly. formatted\n   * @since 1.1\n   */\n  public MGRSRef(String ref, boolean isBessel) throws IllegalArgumentException {\n    \n    super(WGS84Datum.getInstance());\n    \n    Pattern p =\n        Pattern\n            .compile(\"(\\\\d{1,2})([C-X&&[^IO]])([A-Z&&[^IO]])([A-Z&&[^IO]])(\\\\d{2,10})\");\n    Matcher m = p.matcher(ref);\n\n    if (!m.matches()) {\n      throw new IllegalArgumentException(\"Invalid MGRS reference (\" + ref + \")\");\n    }\n\n    this.utmZoneNumber = Integer.parseInt(m.group(1));\n    this.utmZoneChar = m.group(2).charAt(0);\n    this.eastingID = m.group(3).charAt(0);\n    this.northingID = m.group(4).charAt(0);\n    String en = m.group(5);\n    int enl = en.length();\n    if (enl % 2 != 0) {\n      throw new IllegalArgumentException(\"Invalid MGRS reference (\" + ref + \")\");\n    } else {\n      this.precision = (int) Math.pow(10, 5 - (enl / 2));\n      this.easting =\n          Integer.parseInt(en.substring(0, enl / 2)) * this.precision;\n      this.northing = Integer.parseInt(en.substring(enl / 2)) * this.precision;\n    }\n  }\n\n\n  /**\n   * Convert this MGRS reference to an equivelent UTM reference.\n   * \n   * @return the equivalent UTM reference.\n   * @since 1.1\n   */\n  public UTMRef toUTMRef() {\n\n    int set = ((utmZoneNumber - 1) % 6) + 1;\n    int e = (int) eastingID - 65;\n    if (e > 15)\n      e--;\n    if (e > 9)\n      e--;\n\n    int ex = (easting + ((e % 8 + 1) * 100000)) % 1000000;\n\n    // TODO: take account of Bessel ellipsoid\n    // TODO: take account of odd zone sizes near Norway and Svalbard\n    int n = (int) northingID - 64;\n    if (n > 15)\n      n--;\n    if (n > 9)\n      n--;\n    if ((set % 2) == 0)\n      n -= 5;\n    if (n < 0)\n      n += 16;\n\n    int nx = 0;\n\n    boolean isOffset = ((set % 2) == 0);\n\n    switch (utmZoneChar) {\n    case 'Q':\n      if ((!isOffset && northingID < 'T')\n          || (isOffset && (northingID < 'C' || northingID > 'E'))) {\n        nx += 2000000;\n      }\n      break;\n    case 'R':\n      nx += 2000000;\n      break;\n    case 'S':\n      if ((!isOffset && northingID < 'R') || (isOffset && (northingID > 'E'))) {\n        nx += 4000000;\n      } else {\n        nx += 2000000;\n      }\n      break;\n    case 'T':\n      nx += 4000000;\n      break;\n    case 'U':\n      if ((!isOffset && northingID < 'P') || (isOffset && (northingID < 'U'))) {\n        nx += 6000000;\n      } else {\n        nx += 4000000;\n      }\n      break;\n    case 'V':\n    case 'W':\n      nx += 6000000;\n      break;\n    case 'X':\n      if (true) {\n        nx += 8000000;\n      } else {\n        nx += 6000000;\n      }\n    }\n\n    nx += (100000 * (n - 1)) + northing;\n\n    return new UTMRef(utmZoneNumber, utmZoneChar, (double) ex, (double) nx);\n\n  }\n\n\n  /**\n   * Convert this MGRS reference to a latitude and longitude.\n   * \n   * @return the converted latitude and longitude.\n   * @since 1.1\n   */\n  public LatLng toLatLng() {\n    return toUTMRef().toLatLng();\n  }\n\n\n  /**\n   * Return a String representation of this MGRS Reference to whatever precision\n   * this reference is set to.\n   * \n   * @return a String representation of this MGRS reference to whatever\n   *         precision this reference is set to.\n   * @since 1.1\n   */\n  public String toString() {\n    return toString(precision);\n  }\n\n\n  /**\n   * Return a String representation of this MGRS reference to 1m, 10m, 100m,\n   * 1000m or 10000m precision.\n   * \n   * @param precision\n   *          One of MGRSRef.PRECISION_1M, MGRSRef.PRECISION_10M,\n   *          MGRSRef.PRECISION_100M, MGRSRef.PRECISION_1000M,\n   *          MGRSRef.PRECISION_10000M.\n   * @return a String representation of this MGRS reference to the required\n   *         precision.\n   * @since 1.1\n   */\n  public String toString(int precision) {\n\n    if (precision != PRECISION_1M && precision != PRECISION_10M\n        && precision != PRECISION_100M && precision != PRECISION_1000M\n        && precision != PRECISION_10000M) {\n      throw new IllegalArgumentException(\"Precision (\" + precision\n          + \") must be 1m, 10m, 100m, 1000m or 10000m\");\n    }\n\n    int eastingR = (int) Math.floor(easting / precision);\n    int northingR = (int) Math.floor(northing / precision);\n\n    int padding = 5;\n\n    switch (precision) {\n    case PRECISION_10M:\n      padding = 4;\n      break;\n    case PRECISION_100M:\n      padding = 3;\n      break;\n    case PRECISION_1000M:\n      padding = 2;\n      break;\n    case PRECISION_10000M:\n      padding = 1;\n      break;\n    }\n\n    String eastingRs = Integer.toString(eastingR);\n    int ez = padding - eastingRs.length();\n    while (ez > 0) {\n      eastingRs = \"0\" + eastingRs;\n      ez--;\n    }\n\n    String northingRs = Integer.toString(northingR);\n    int nz = padding - northingRs.length();\n    while (nz > 0) {\n      northingRs = \"0\" + northingRs;\n      nz--;\n    }\n\n    String utmZonePadding = \"\";\n    if (utmZoneNumber < 10) {\n      utmZonePadding = \"0\";\n    }\n\n    return utmZonePadding + utmZoneNumber + Character.toString(utmZoneChar)\n        + Character.toString(eastingID) + Character.toString(northingID)\n        + eastingRs + northingRs;\n  }\n\n\n  /**\n   * \n   * \n   * @return the easting\n   * @since 1.1\n   */\n  public int getEasting() {\n    return easting;\n  }\n\n\n  /**\n   * \n   * \n   * @return the eastingID\n   * @since 1.1\n   */\n  public char getEastingID() {\n    return eastingID;\n  }\n\n\n  /**\n   * \n   * \n   * @return isBessel\n   * @since 1.1\n   */\n  public boolean isBessel() {\n    return isBessel;\n  }\n\n\n  /**\n   * \n   * \n   * @return the northing\n   * @since 1.1\n   */\n  public int getNorthing() {\n    return northing;\n  }\n\n\n  /**\n   * \n   * \n   * @return the northingID\n   * @since 1.1\n   */\n  public char getNorthingID() {\n    return northingID;\n  }\n\n\n  /**\n   * \n   * \n   * @return the precision\n   * @since 1.1\n   */\n  public int getPrecision() {\n    return precision;\n  }\n\n\n  /**\n   * \n   * \n   * @return the utmZoneChar\n   * @since 1.1\n   */\n  public char getUtmZoneChar() {\n    return utmZoneChar;\n  }\n\n\n  /**\n   * \n   * \n   * @return the utmZoneNumber\n   * @since 1.1\n   */\n  public int getUtmZoneNumber() {\n    return utmZoneNumber;\n  }\n\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/NotDefinedOnUTMGridException.java",
    "content": "package uk.me.jstott.jcoord;\n\n/**\n * <p>\n * This exception is thrown when\n * </p> \n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n *\n * <p>\n * Created on 12-Mar-2006\n * </p>\n *\n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NotDefinedOnUTMGridException extends RuntimeException {\n\n  /**\n   * Serial version UID\n   */\n  private static final long serialVersionUID = 5699420767622348737L;\n\n  \n  /**\n   * NotDefinedOnUTMGridException constructor.\n   */\n  public NotDefinedOnUTMGridException() {\n    super();\n  }\n\n  \n  /**\n   * NotDefinedOnUTMGridException constructor with a message.\n   * \n   * @param message details of the exception.\n   */\n  public NotDefinedOnUTMGridException(String message) {\n    super(message);\n  }\n\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/OSRef.java",
    "content": "package uk.me.jstott.jcoord;\n\nimport uk.me.jstott.jcoord.datum.OSGB36Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Airy1830Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class to represent an Ordnance Survey of Great Britain (OSGB) grid reference.\n * </p>\n * \n * <p>\n * <b>British National Grid</b><br>\n * <ul>\n * <li>Projection: Transverse Mercator</li>\n * <li>Reference ellipsoid: Airy 1830</li>\n * <li>Units: metres</li>\n * <li>Origin: 49&deg;N, 2&deg;W</li>\n * <li>False co-ordinates of origin: 400000m east, -100000m north</li>\n * </ul>\n * </p>\n * \n * <p>\n * A full reference includes a two-character code identifying a particular\n * 100,000m grid square. The table below shows how the two-character 100,000m\n * grid squares are identified. The bottom left corner is at the false origin of\n * the grid. Squares without values fall outside the boundaries of the British\n * National Grid.\n * </p>\n * \n * <table border=\"1\">\n * <tr>\n * <th> km</th>\n * <th>0</th>\n * <th>100</th>\n * <th>200</th>\n * <th>300</th>\n * <th>400</th>\n * <th>500</th>\n * <th>600</th>\n * <th>700</th>\n * </tr>\n * <tr>\n * <th>1200</th>\n * <td>HL</td>\n * <td>HM</td>\n * <td>HN</td>\n * <td>HO</td>\n * <td>HP</td>\n * <td>JL</td>\n * <td>JM</td>\n * <td> </td>\n * </tr>\n * <tr>\n * <th>1100</th>\n * <td>HQ</td>\n * <td>HR</td>\n * <td>HS</td>\n * <td>HT</td>\n * <td>HU</td>\n * <td>JQ</td>\n * <td>JR</td>\n * <td> </td>\n * </tr>\n * <tr>\n * <th>1000</th>\n * <td>HV</td>\n * <td>HW</td>\n * <td>HX</td>\n * <td>HY</td>\n * <td>HZ</td>\n * <td>JV</td>\n * <td>JW</td>\n * <td> </td>\n * </tr>\n * <tr>\n * <th> 900</th>\n * <td>NA</td>\n * <td>NB</td>\n * <td>NC</td>\n * <td>ND</td>\n * <td>NE</td>\n * <td>OA</td>\n * <td>OB</td>\n * <td> </td>\n * </tr>\n * <tr>\n * <th> 800</th>\n * <td>NF</td>\n * <td>NG</td>\n * <td>NH</td>\n * <td>NJ</td>\n * <td>NK</td>\n * <td>OF</td>\n * <td>OG</td>\n * <td>OH</td>\n * </tr>\n * <tr>\n * <th> 700</th>\n * <td>NL</td>\n * <td>NM</td>\n * <td>NN</td>\n * <td>NO</td>\n * <td>NP</td>\n * <td>OL</td>\n * <td>OM</td>\n * <td>ON</td>\n * </tr>\n * <tr>\n * <th> 600</th>\n * <td>NQ</td>\n * <td>NR</td>\n * <td>NS</td>\n * <td>NT</td>\n * <td>NU</td>\n * <td>OQ</td>\n * <td>OR</td>\n * <td>OS</td>\n * </tr>\n * <tr>\n * <th> 500</th>\n * <td> </td>\n * <td>NW</td>\n * <td>NX</td>\n * <td>NY</td>\n * <td>NZ</td>\n * <td>OV</td>\n * <td>OW</td>\n * <td>OX</td>\n * </tr>\n * <tr>\n * <th> 400</th>\n * <td> </td>\n * <td>SB</td>\n * <td>SC</td>\n * <td>SD</td>\n * <td>SE</td>\n * <td>TA</td>\n * <td>TB</td>\n * <td>TC</td>\n * </tr>\n * <tr>\n * <th> 300</th>\n * <td> </td>\n * <td>SG</td>\n * <td>SH</td>\n * <td>SJ</td>\n * <td>SK</td>\n * <td>TF</td>\n * <td>TG</td>\n * <td>TH</td>\n * </tr>\n * <tr>\n * <th> 200</th>\n * <td> </td>\n * <td>SM</td>\n * <td>SN</td>\n * <td>SO</td>\n * <td>SP</td>\n * <td>TL</td>\n * <td>TM</td>\n * <td>TN</td>\n * </tr>\n * <tr>\n * <th> 100</th>\n * <td>SQ</td>\n * <td>SR</td>\n * <td>SS</td>\n * <td>ST</td>\n * <td>SU</td>\n * <td>TQ</td>\n * <td>TR</td>\n * <td>TS</td>\n * </tr>\n * <tr>\n * <th> 0</th>\n * <td>SV</td>\n * <td>SW</td>\n * <td>SX</td>\n * <td>SY</td>\n * <td>SZ</td>\n * <td>TV</td>\n * <td>TW</td>\n * <td> </td>\n * </tr>\n * </table>\n * \n * <p>\n * Within each 100,000m square, the grid is further subdivided into 1000m\n * squares. These 1km squares are shown on Ordnance Survey 1:25000 and 1:50000\n * mapping as the main grid. To reference a 1km square, give the easting and\n * then the northing, e.g. TR2266. In this example, TR represents the 100,000m\n * square, 22 represents the easting (in km) and 66 represents the northing (in\n * km). This is commonly called a four-figure grid reference.\n * </p>\n * \n * <p>\n * It is possible to extend the four-figure grid reference for more accuracy.\n * For example, a six-figure grid reference would be accurate to 100m and an\n * eight-figure grid reference would be accurate to 10m.\n * </p>\n * \n * <p>\n * When providing local references, the 2 characters representing the 100,000m\n * square are often omitted.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 11-02-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.0\n * @since 1.0\n */\npublic class OSRef extends CoordinateSystem {\n\n  /**\n   * The easting in metres relative to the origin of the British National Grid.\n   */\n  private double easting;\n\n  /**\n   * The northing in metres relative to the origin of the British National Grid.\n   */\n  private double northing;\n\n\n  /**\n   * Create a new Ordnance Survey grid reference using a given easting and\n   * northing. The easting and northing must be in metres and must be relative\n   * to the origin of the British National Grid.\n   * \n   * @param easting\n   *          the easting in metres. Must be greater than or equal to 0.0 and\n   *          less than 800000.0.\n   * @param northing\n   *          the northing in metres. Must be greater than or equal to 0.0 and\n   *          less than 1400000.0.\n   * @throws IllegalArgumentException\n   *           if either the easting or the northing are invalid.\n   * @since 1.0\n   */\n  public OSRef(double easting, double northing) throws IllegalArgumentException {\n\n    super(OSGB36Datum.getInstance());\n\n    setEasting(easting);\n    setNorthing(northing);\n\n  }\n\n\n  /**\n   * Take a string formatted as a six-figure OS grid reference (e.g. \"TG514131\")\n   * and create a new OSRef object that represents that grid reference. The\n   * first character must be H, N, S, O or T. The second character can be any\n   * uppercase character from A through Z excluding I.\n   * \n   * @param ref\n   *          a String representing a six-figure Ordnance Survey grid reference\n   *          in the form XY123456\n   * @throws IllegalArgumentException\n   *           if ref is not of the form XY123456\n   * @since 1.0\n   */\n  public OSRef(String ref) throws IllegalArgumentException {\n\n    super(OSGB36Datum.getInstance());\n\n    // if (ref.matches(\"\"))\n    // TODO 2006-02-05 : check format\n    char char1 = ref.charAt(0);\n    char char2 = ref.charAt(1);\n    // Thanks to Nick Holloway for pointing out the radix bug here\n    int east = Integer.parseInt(ref.substring(2, 5)) * 100;\n    int north = Integer.parseInt(ref.substring(5, 8)) * 100;\n    if (char1 == 'H') {\n      north += 1000000;\n    } else if (char1 == 'N') {\n      north += 500000;\n    } else if (char1 == 'O') {\n      north += 500000;\n      east += 500000;\n    } else if (char1 == 'T') {\n      east += 500000;\n    }\n    int char2ord = char2;\n    if (char2ord > 73)\n      char2ord--; // Adjust for no I\n    double nx = ((char2ord - 65) % 5) * 100000;\n    double ny = (4 - Math.floor((char2ord - 65) / 5)) * 100000;\n\n    setEasting(east + nx);\n    setNorthing(north + ny);\n\n  }\n\n\n  /**\n   * Convert this latitude and longitude into an OSGB (Ordnance Survey of Great\n   * Britain) grid reference.\n   * \n   * @return the converted OSGB grid reference.\n   * @since 1.1\n   */\n  public OSRef(LatLng ll) {\n\n    super(OSGB36Datum.getInstance());\n\n    Airy1830Ellipsoid airy1830 = Airy1830Ellipsoid.getInstance();\n    double OSGB_F0 = 0.9996012717;\n    double N0 = -100000.0;\n    double E0 = 400000.0;\n    double phi0 = Math.toRadians(49.0);\n    double lambda0 = Math.toRadians(-2.0);\n    double a = airy1830.getSemiMajorAxis();\n    double b = airy1830.getSemiMinorAxis();\n    double eSquared = airy1830.getEccentricitySquared();\n    double phi = Math.toRadians(ll.getLatitude());\n    double lambda = Math.toRadians(ll.getLongitude());\n    double E = 0.0;\n    double N = 0.0;\n    double n = (a - b) / (a + b);\n    double v = a * OSGB_F0\n        * Math.pow(1.0 - eSquared * Util.sinSquared(phi), -0.5);\n    double rho = a * OSGB_F0 * (1.0 - eSquared)\n        * Math.pow(1.0 - eSquared * Util.sinSquared(phi), -1.5);\n    double etaSquared = (v / rho) - 1.0;\n    double M = (b * OSGB_F0)\n        * (((1 + n + ((5.0 / 4.0) * n * n) + ((5.0 / 4.0) * n * n * n)) * (phi - phi0))\n            - (((3 * n) + (3 * n * n) + ((21.0 / 8.0) * n * n * n))\n                * Math.sin(phi - phi0) * Math.cos(phi + phi0))\n            + ((((15.0 / 8.0) * n * n) + ((15.0 / 8.0) * n * n * n))\n                * Math.sin(2.0 * (phi - phi0)) * Math.cos(2.0 * (phi + phi0))) - (((35.0 / 24.0)\n            * n * n * n)\n            * Math.sin(3.0 * (phi - phi0)) * Math.cos(3.0 * (phi + phi0))));\n    double I = M + N0;\n    double II = (v / 2.0) * Math.sin(phi) * Math.cos(phi);\n    double III = (v / 24.0) * Math.sin(phi) * Math.pow(Math.cos(phi), 3.0)\n        * (5.0 - Util.tanSquared(phi) + (9.0 * etaSquared));\n    double IIIA = (v / 720.0) * Math.sin(phi) * Math.pow(Math.cos(phi), 5.0)\n        * (61.0 - (58.0 * Util.tanSquared(phi)) + Math.pow(Math.tan(phi), 4.0));\n    double IV = v * Math.cos(phi);\n    double V = (v / 6.0) * Math.pow(Math.cos(phi), 3.0)\n        * ((v / rho) - Util.tanSquared(phi));\n    double VI = (v / 120.0)\n        * Math.pow(Math.cos(phi), 5.0)\n        * (5.0 - (18.0 * Util.tanSquared(phi)) + (Math.pow(Math.tan(phi), 4.0))\n            + (14 * etaSquared) - (58 * Util.tanSquared(phi) * etaSquared));\n\n    N = I + (II * Math.pow(lambda - lambda0, 2.0))\n        + (III * Math.pow(lambda - lambda0, 4.0))\n        + (IIIA * Math.pow(lambda - lambda0, 6.0));\n    E = E0 + (IV * (lambda - lambda0)) + (V * Math.pow(lambda - lambda0, 3.0))\n        + (VI * Math.pow(lambda - lambda0, 5.0));\n\n    setEasting(E);\n    setNorthing(N);\n\n  }\n\n\n  /**\n   * Return a String representation of this OSGB grid reference showing the\n   * easting and northing.\n   * \n   * @return a String represenation of this OSGB grid reference\n   * @since 1.0\n   */\n  public String toString() {\n    return \"(\" + easting + \", \" + northing + \")\";\n  }\n\n\n  /**\n   * Return a String representation of this OSGB grid reference using the\n   * six-figure notation in the form XY123456\n   * \n   * @return a String representing this OSGB grid reference in six-figure\n   *         notation\n   * @since 1.0\n   */\n  public String toSixFigureString() {\n    int hundredkmE = (int) Math.floor(easting / 100000);\n    int hundredkmN = (int) Math.floor(northing / 100000);\n    String firstLetter;\n    if (hundredkmN < 5) {\n      if (hundredkmE < 5) {\n        firstLetter = \"S\";\n      } else {\n        firstLetter = \"T\";\n      }\n    } else if (hundredkmN < 10) {\n      if (hundredkmE < 5) {\n        firstLetter = \"N\";\n      } else {\n        firstLetter = \"O\";\n      }\n    } else {\n      firstLetter = \"H\";\n    }\n\n    int index = 65 + ((4 - (hundredkmN % 5)) * 5) + (hundredkmE % 5);\n    // int ti = index;\n    if (index >= 73)\n      index++;\n    String secondLetter = Character.toString((char) index);\n\n    int e = (int) Math.floor((easting - (100000 * hundredkmE)) / 100);\n    int n = (int) Math.floor((northing - (100000 * hundredkmN)) / 100);\n    String es = \"\" + e;\n    if (e < 100)\n      es = \"0\" + es;\n    if (e < 10)\n      es = \"0\" + es;\n    String ns = \"\" + n;\n    if (n < 100)\n      ns = \"0\" + ns;\n    if (n < 10)\n      ns = \"0\" + ns;\n\n    return firstLetter + secondLetter + es + ns;\n  }\n\n\n  /**\n   * Convert this OSGB grid reference to a latitude/longitude pair using the\n   * OSGB36 datum. Note that, the LatLng object may need to be converted to the\n   * WGS84 datum depending on the application.\n   * \n   * @return a LatLng object representing this OSGB grid reference using the\n   *         OSGB36 datum\n   * @since 1.0\n   */\n  public LatLng toLatLng() {\n    double OSGB_F0 = 0.9996012717;\n    double N0 = -100000.0;\n    double E0 = 400000.0;\n    double phi0 = Math.toRadians(49.0);\n    double lambda0 = Math.toRadians(-2.0);\n    double a = getDatum().getReferenceEllipsoid().getSemiMajorAxis();\n    double b = getDatum().getReferenceEllipsoid().getSemiMinorAxis();\n    double eSquared = getDatum().getReferenceEllipsoid()\n        .getEccentricitySquared();\n    double phi = 0.0;\n    double lambda = 0.0;\n    double E = this.easting;\n    double N = this.northing;\n    double n = (a - b) / (a + b);\n    double M = 0.0;\n    double phiPrime = ((N - N0) / (a * OSGB_F0)) + phi0;\n    do {\n      M = (b * OSGB_F0)\n          * (((1 + n + ((5.0 / 4.0) * n * n) + ((5.0 / 4.0) * n * n * n)) * (phiPrime - phi0))\n              - (((3 * n) + (3 * n * n) + ((21.0 / 8.0) * n * n * n))\n                  * Math.sin(phiPrime - phi0) * Math.cos(phiPrime + phi0))\n              + ((((15.0 / 8.0) * n * n) + ((15.0 / 8.0) * n * n * n))\n                  * Math.sin(2.0 * (phiPrime - phi0)) * Math\n                  .cos(2.0 * (phiPrime + phi0))) - (((35.0 / 24.0) * n * n * n)\n              * Math.sin(3.0 * (phiPrime - phi0)) * Math\n              .cos(3.0 * (phiPrime + phi0))));\n      phiPrime += (N - N0 - M) / (a * OSGB_F0);\n    } while ((N - N0 - M) >= 0.001);\n    double v = a * OSGB_F0\n        * Math.pow(1.0 - eSquared * Util.sinSquared(phiPrime), -0.5);\n    double rho = a * OSGB_F0 * (1.0 - eSquared)\n        * Math.pow(1.0 - eSquared * Util.sinSquared(phiPrime), -1.5);\n    double etaSquared = (v / rho) - 1.0;\n    double VII = Math.tan(phiPrime) / (2 * rho * v);\n    double VIII = (Math.tan(phiPrime) / (24.0 * rho * Math.pow(v, 3.0)))\n        * (5.0 + (3.0 * Util.tanSquared(phiPrime)) + etaSquared - (9.0 * Util\n            .tanSquared(phiPrime) * etaSquared));\n    double IX = (Math.tan(phiPrime) / (720.0 * rho * Math.pow(v, 5.0)))\n        * (61.0 + (90.0 * Util.tanSquared(phiPrime)) + (45.0 * Util\n            .tanSquared(phiPrime) * Util.tanSquared(phiPrime)));\n    double X = Util.sec(phiPrime) / v;\n    double XI = (Util.sec(phiPrime) / (6.0 * v * v * v))\n        * ((v / rho) + (2 * Util.tanSquared(phiPrime)));\n    double XII = (Util.sec(phiPrime) / (120.0 * Math.pow(v, 5.0)))\n        * (5.0 + (28.0 * Util.tanSquared(phiPrime)) + (24.0 * Util\n            .tanSquared(phiPrime) * Util.tanSquared(phiPrime)));\n    double XIIA = (Util.sec(phiPrime) / (5040.0 * Math.pow(v, 7.0)))\n        * (61.0 + (662.0 * Util.tanSquared(phiPrime))\n            + (1320.0 * Util.tanSquared(phiPrime) * Util.tanSquared(phiPrime)) + (720.0\n            * Util.tanSquared(phiPrime) * Util.tanSquared(phiPrime) * Util\n            .tanSquared(phiPrime)));\n    phi = phiPrime - (VII * Math.pow(E - E0, 2.0))\n        + (VIII * Math.pow(E - E0, 4.0)) - (IX * Math.pow(E - E0, 6.0));\n    lambda = lambda0 + (X * (E - E0)) - (XI * Math.pow(E - E0, 3.0))\n        + (XII * Math.pow(E - E0, 5.0)) - (XIIA * Math.pow(E - E0, 7.0));\n\n    return new LatLng(Math.toDegrees(phi), Math.toDegrees(lambda));\n  }\n\n\n  /**\n   * Get the easting in metres relative the origin of the British National Grid.\n   * \n   * @return the easting in metres.\n   * @since 1.0\n   */\n  public double getEasting() {\n\n    return easting;\n\n  }\n\n\n  /**\n   * Get the northing in metres relative to the origin of the British National\n   * Grid.\n   * \n   * @return the northing in metres.\n   * @since 1.0\n   */\n  public double getNorthing() {\n\n    return northing;\n\n  }\n\n\n  /**\n   * Set the easting for this OSRef.\n   * \n   * @param easting\n   *          the easting in metres. Must be greater than or equal to 0.0 and\n   *          less than 800000.0.\n   * @throws IllegalArgumentException\n   *           if the easting is invalid.\n   */\n  public void setEasting(double easting) throws IllegalArgumentException {\n\n    if (easting < 0.0 || easting >= 800000.0) {\n      throw new IllegalArgumentException(\"Easting (\" + easting\n          + \") is invalid. Must be greather than or equal to 0.0 and \"\n          + \"less than 800000.0.\");\n    }\n\n    this.easting = easting;\n\n  }\n\n\n  /**\n   * Set the northing for this OSRef\n   * \n   * @param northing\n   *          the northing in metres. Must be greater than or equal to 0.0 and\n   *          less than 1400000.0.\n   * @throws IllegalArgumentException\n   *           if either the northing is invalid.\n   */\n  public void setNorthing(double northing) throws IllegalArgumentException {\n\n    if (northing < 0.0 || northing >= 1400000.0) {\n      throw new IllegalArgumentException(\"Northing (\" + northing\n          + \") is invalid. Must be greather than or equal to 0.0 and less \"\n          + \"than 1400000.0.\");\n    }\n\n    this.northing = northing;\n\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/RefEll.java",
    "content": "package uk.me.jstott.jcoord;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class to represent a reference ellipsoid. Also provides a number of\n * pre-determined reference ellipsoids as constants.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 11-Feb-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.0\n * @deprecated Use classes in the uk.me.jstott.jcoord.ellipsoid package instead.\n */\npublic class RefEll {\n\n  /**\n   * Airy 1830 Reference Ellipsoid.\n   * \n   * @since 1.0\n   */\n  public static final RefEll AIRY_1830           =\n                                                     new RefEll(6377563.396,\n                                                         6356256.909);\n\n  /**\n   * Bessel 1841 Reference Ellipsoid.\n   * \n   * @since 1.1\n   */\n  public static final RefEll BESSEL_1841         =\n                                                     new RefEll(6377397.155,\n                                                         299.1528128);\n\n  /**\n   * Clarke 1866 Reference Ellipsoid.\n   * \n   * @since 1.1\n   */\n  public static final RefEll CLARKE_1866         =\n                                                     new RefEll(6378206.4,\n                                                         294.9786982);\n\n  /**\n   * Clarke 1880 Reference Ellipsoid.\n   * \n   * @since 1.1\n   */\n  public static final RefEll CLARKE_1880         =\n                                                     new RefEll(6378249.145,\n                                                         293.465);\n\n  /**\n   * Everest 1830 Reference Ellipsoid.\n   * \n   * @since 1.1\n   */\n  public static final RefEll EVEREST_1830        =\n                                                     new RefEll(6377276.345,\n                                                         300.8017);\n\n  /**\n   * Fisher 1960 Reference Ellipsoid.\n   * \n   * @since 1.1\n   */\n  public static final RefEll FISCHER_1960        = new RefEll(6378166.0, 298.3);\n\n  /**\n   * Fischer 1968 Reference Ellipsoid.\n   * \n   * @since 1.1\n   */\n  public static final RefEll FISCHER_1968        = new RefEll(6378150.0, 298.3);\n\n  /**\n   * GRS 1967 Reference Ellipsoid.\n   * \n   * @since 1.1\n   */\n  public static final RefEll GRS_1967            =\n                                                     new RefEll(6378160.0,\n                                                         298.247167427);\n\n  /**\n   * GRS 1975 Reference Ellipsoid.\n   * \n   * @since 1.1\n   */\n  public static final RefEll GRS_1975            =\n                                                     new RefEll(6378140.0,\n                                                         298.257);\n\n  /**\n   * GRS 1980 Reference Ellipsoid.\n   * \n   * @since 1.1\n   */\n  public static final RefEll GRS_1980            =\n                                                     new RefEll(6378137.0,\n                                                         298.257222101);\n\n  /**\n   * Hough 1956 Reference Ellipsoid.\n   * \n   * @since 1.1\n   */\n  public static final RefEll HOUGH_1956          = new RefEll(6378270.0, 297.0);\n\n  /**\n   * International Reference Ellipsoid.\n   * \n   * @since 1.1\n   */\n  public static final RefEll INTERNATIONAL       = new RefEll(6378388.0, 297.0);\n\n  /**\n   * Krassovsky 1940 Reference Ellipsoid.\n   * \n   * @since 1.1\n   */\n  public static final RefEll KRASSOVSKY_1940     = new RefEll(6378245.0, 298.3);\n\n  /**\n   * South American 1969 Reference Ellipsoid\n   * \n   * @since 1.1\n   */\n  public static final RefEll SOUTH_AMERICAN_1969 =\n                                                     new RefEll(6378160.0,\n                                                         298.25);\n\n  /**\n   * WGS60 Reference Ellipsoid.\n   * \n   * @since 1.1\n   */\n  public static final RefEll WGS60               = new RefEll(6378165.0, 298.3);\n\n  /**\n   * WGS66 Reference Ellipsoid.\n   * \n   * @since 1.1\n   */\n  public static final RefEll WGS66               =\n                                                     new RefEll(6378145.0,\n                                                         298.25);\n\n  /**\n   * WGS72 Reference Ellipsoid.\n   * \n   * @since 1.1\n   */\n  public static final RefEll WGS72               =\n                                                     new RefEll(6378135.0,\n                                                         298.26);\n\n  /**\n   * WGS84 Reference Ellipsoid.\n   * \n   * @since 1.0\n   */\n  public static final RefEll WGS84               =\n                                                     new RefEll(6378137.000,\n                                                         6356752.3141);\n\n  /**\n   * Semi-major axis\n   * \n   * @since 1.0\n   */\n  private double             maj;\n\n  /**\n   * Semi-minor axis\n   * \n   * @since 1.0\n   */\n  private double             min;\n\n  /**\n   * Eccentricity\n   * \n   * @since 1.0\n   */\n  private double             ecc;\n\n\n  /**\n   * Create a new reference ellipsoid\n   * \n   * @param maj\n   *          semi-major axis\n   * @param min\n   *          semi-minor axis\n   * @since 1.0\n   */\n  public RefEll(double maj, double min) {\n    this.maj = maj;\n    this.min = min;\n    this.ecc = ((maj * maj) - (min * min)) / (maj * maj);\n  }\n\n\n  /**\n   * Return the semi-major axis.\n   * \n   * @return the semi-major axis\n   * @since 1.0\n   */\n  public double getMaj() {\n    return maj;\n  }\n\n\n  /**\n   * Return the semi-minor axis\n   * \n   * @return the semi-minor axis\n   * @since 1.0\n   */\n  public double getMin() {\n    return min;\n  }\n\n\n  /**\n   * Return the eccentricity.\n   * \n   * @return the eccentricity\n   * @since 1.0\n   */\n  public double getEcc() {\n    return ecc;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/Test.java",
    "content": "package uk.me.jstott.jcoord;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class to illustrate the use of the various functions of the classes in the\n * Jcoord package.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 11-Feb-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.0\n */\npublic class Test {\n\n  /**\n   * Main method\n   * \n   * @param args\n   *          not used\n   * @since 1.0\n   */\n  public static void main(String[] args) {\n\n    /*\n     * Calculate Surface Distance between two Latitudes/Longitudes\n     * \n     * The distance() function takes a reference to a LatLng object as a\n     * parameter and calculates the surface distance between the the given\n     * object and this object in kilometres:\n     */\n\n    System.out\n        .println(\"Calculate Surface Distance between two Latitudes/Longitudes\");\n    LatLng lld1 = new LatLng(40.718119, -73.995667); // New York\n    System.out.println(\"New York Lat/Long: \" + lld1.toString());\n    LatLng lld2 = new LatLng(51.499981, -0.125313); // London\n    System.out.println(\"London Lat/Long: \" + lld2.toString());\n    double d = lld1.distance(lld2);\n    System.out.println(\"Surface Distance between New York and London: \" + d\n        + \"km\");\n    System.out.println();\n\n    /*\n     * Convert OS Grid Reference to Latitude/Longitude\n     * \n     * Note that the OSGB-Latitude/Longitude conversions use the OSGB36 datum by\n     * default. The majority of applications use the WGS84 datum, for which the\n     * appropriate conversions need to be added. See the examples below to see\n     * the difference between the two data.\n     */\n\n    System.out.println(\"Convert OS Grid Reference to Latitude/Longitude\");\n    // Using OSGB36 (convert an OSGB grid reference to a latitude and longitude\n    // using the OSGB36 datum):\n    System.out.println(\"Using OSGB36\");\n    OSRef os1 = new OSRef(651409.903, 313177.270);\n    System.out.println(\"OS Grid Reference: \" + os1.toString() + \" - \"\n        + os1.toSixFigureString());\n    LatLng ll1 = os1.toLatLng();\n    System.out.println(\"Converted to Lat/Long: \" + ll1.toString());\n    System.out.println();\n\n    // Using WGS84 (convert an OSGB grid reference to a latitude and longitude\n    // using the WGS84 datum):\n    System.out.println(\"Using WGS84\");\n    OSRef os1w = new OSRef(651409.903, 313177.270);\n    System.out.println(\"OS Grid Reference: \" + os1w.toString() + \" - \"\n        + os1w.toSixFigureString());\n    LatLng ll1w = os1w.toLatLng();\n    ll1w.toWGS84();\n    System.out.println(\"Converted to Lat/Long: \" + ll1w.toString());\n    System.out.println();\n\n    /*\n     * Convert Latitude/Longitude to OS Grid Reference\n     * \n     * Note that the OSGB-Latitude/Longitude conversions use the OSGB36 datum by\n     * default. The majority of applications use the WGS84 datum, for which the\n     * appropriate conversions need to be added. See the examples below to see\n     * the difference between the two data.\n     */\n\n    System.out.println(\"Convert Latitude/Longitude to OS Grid Reference\");\n    // Using OSGB36 (convert a latitude and longitude using the OSGB36 datum to\n    // an OSGB grid reference):\n    System.out.println(\"Using OSGB36\");\n    LatLng ll2 = new LatLng(52.657570301933, 1.7179215806451);\n    System.out.println(\"Latitude/Longitude: \" + ll2.toString());\n    OSRef os2 = ll2.toOSRef();\n    System.out.println(\"Converted to OS Grid Ref: \" + os2.toString() + \" - \"\n        + os2.toSixFigureString());\n    System.out.println();\n\n    // Using WGS84 (convert a latitude and longitude using the WGS84 datum to an\n    // OSGB grid reference):\n    System.out.println(\"Using WGS84\");\n    LatLng ll2w = new LatLng(52.657570301933, 1.7179215806451);\n    System.out.println(\"Latitude/Longitude: \" + ll2.toString() + \" : \" + ll2.toDMSString());\n    ll2w.toOSGB36();\n    OSRef os2w = ll2w.toOSRef();\n    System.out.println(\"Converted to OS Grid Ref: \" + os2w.toString() + \" - \"\n        + os2w.toSixFigureString());\n    System.out.println();\n\n    /*\n     * Convert Six-Figure OS Grid Reference String to an OSRef Object\n     * \n     * To convert a string representing a six-figure OSGB grid reference:\n     */\n\n    System.out\n        .println(\"Convert Six-Figure OS Grid Reference String to an OSRef Object\");\n    String os6 = \"TG514131\";\n    System.out.println(\"Six figure string: \" + os6);\n    OSRef os6x = new OSRef(os6);\n    System.out.println(\"Converted to OS Grid Ref: \" + os6x.toString() + \" - \"\n        + os6x.toSixFigureString());\n    System.out.println();\n\n    /*\n     * Convert UTM Reference to Latitude/Longitude\n     */\n\n    System.out.println(\"Convert UTM Reference to Latitude/Longitude\");\n    UTMRef utm1 = new UTMRef(12, 'E', 456463.99, 3335334.05);\n    System.out.println(\"UTM Reference: \" + utm1.toString());\n    LatLng ll3 = utm1.toLatLng();\n    System.out.println(\"Converted to Lat/Long: \" + ll3.toString());\n    System.out.println();\n\n    /*\n     * Convert Latitude/Longitude to UTM Reference\n     */\n\n    System.out.println(\"Convert Latitude/Longitude to UTM Reference\");\n    LatLng ll4 = new LatLng(-60.1167, -111.7833);\n    System.out.println(\"Latitude/Longitude: \" + ll4.toString());\n    UTMRef utm2 = ll4.toUTMRef();\n    System.out.println(\"Converted to UTM Ref: \" + utm2.toString());\n    System.out.println();\n\n    mgrsTests();\n  }\n\n\n  /**\n   * Test the <code>{@link MGRSRef MGRSRef}</code> class.\n   * \n   * @since 1.1\n   */\n  public static void mgrsTests() {\n\n    /*\n     * Convert UTM reference to MGRS reference\n     */\n    System.out.println(\"Convert UTM Reference to MGRS Reference\");\n    UTMRef utm1 = new UTMRef(13, 'S', 443575.71, 4349755.98);\n    System.out.println(\"UTM Reference: \" + utm1.toString());\n    MGRSRef mgrs1 = new MGRSRef(utm1);\n    System.out.println(\"MGRS Reference: \" + mgrs1.toString());\n    System.out.println();\n\n    /*\n     * Convert MGRS reference to UTM reference\n     */\n    System.out.println(\"Convert MGRS reference to UTM reference\");\n    // MGRSRef mgrs2 = new MGRSRef(13, 'S', 'D', 'D', 43576, 49756);\n    MGRSRef mgrs2 =\n        new MGRSRef(10, 'U', 'E', 'U', 0, 16300, MGRSRef.PRECISION_1M);\n    // 10UEU0000016300\n    System.out.println(\"MGRS Reference: \" + mgrs2.toString());\n    UTMRef utm2 = mgrs2.toUTMRef();\n    System.out.println(\"UTM Reference: \" + utm2.toString());\n    System.out.println();\n\n    /*\n     * Convert MGRS reference to Latitude/Longitude\n     */\n    System.out.println(\"Convert MGRS reference to latitude/longitude\");\n    MGRSRef mgrs3 =\n        new MGRSRef(13, 'S', 'D', 'D', 43575, 49756, MGRSRef.PRECISION_1M);\n    System.out.println(\"MGRS Reference: \" + mgrs3.toString());\n    UTMRef utm5 = mgrs3.toUTMRef();\n    System.out.println(\"UTM Reference: \" + utm5.toString());\n    LatLng ll1 = mgrs3.toLatLng();\n    System.out.println(\"Latitude/Longitude: \" + ll1.toString());\n    System.out.println();\n\n    /*\n     * Convert latitude/longitude to MGRS reference\n     */\n    System.out.println(\"Convert latitude/longitude to MGRS reference\");\n    LatLng ll2 = new LatLng(39.295339, -105.654342);\n    //LatLng ll2 = new LatLng(48.9833, 8.2);\n    System.out.println(\"Latitude/Longitude: \" + ll2.toString());\n    UTMRef utm3 = ll2.toUTMRef();\n    System.out.println(\"UTM Reference: \" + utm3.toString());\n    MGRSRef mgrs4 = ll2.toMGRSRef();\n    System.out.println(\"MGRS Reference: \" + mgrs4.toString());\n    System.out.println();\n\n    /*\n     * Create an MGRS reference from a String\n     */\n    System.out.println(\"Create an MGRS reference from a String\");\n    //MGRSRef mgrs5 = new MGRSRef(\"13SDD4357549756\");\n    MGRSRef mgrs5 = new MGRSRef(\"32UMU1078\");\n    System.out.println(mgrs5.toString(MGRSRef.PRECISION_1M));\n    UTMRef utm4 = mgrs5.toUTMRef();\n    System.out.println(\"UTM Reference: \" + utm4.toString());\n    LatLng ll3 = mgrs5.toLatLng();\n    System.out.println(\"Latitude/Longitude: \" + ll3.toString());\n    System.out.println();\n    \n\n    //System.out.println(\"Convert MGRS references to UTM reference\");\n    //for (char i = 'A'; i <= 'Z'; i++) {\n    //  for (char j = 'A'; j <= 'V'; j++) {\n    //    try {\n    //      MGRSRef mgrs = new MGRSRef(1, 'A', i, j, 0, 0, MGRSRef.PRECISION_1M);\n    //      UTMRef utm = mgrs.toUTMRef();\n        //  System.out.println(\"MGRS: \" + mgrs.toString());\n         // System.out.println(\" -->: \" + utm.toString());\n    //    } catch (IllegalArgumentException e) {\n       //   System.out.println(e.getMessage());\n    //    }\n    //  }\n    //}\n  }\n\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/UTMRef.java",
    "content": "package uk.me.jstott.jcoord;\n\nimport uk.me.jstott.jcoord.datum.WGS84Datum;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class to represent a Universal Transverse Mercator (UTM) reference.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 11-Feb-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.0\n * @since 1.0\n */\npublic class UTMRef extends CoordinateSystem {\n\n  /**\n   * Easting\n   */\n  private double easting;\n\n  /**\n   * Northing\n   */\n  private double northing;\n\n  /**\n   * Latitude zone character\n   */\n  private char latZone;\n\n  /**\n   * Longitude zone number\n   */\n  private int lngZone;\n\n\n  /**\n   * Create a new UTM reference object. Checks are made to make sure that the\n   * given parameters are roughly valid, but the checks are not exhaustive with\n   * regards to the easting value. Catching a NotDefinedOnUTMGridException does\n   * not necessarily mean that the UTM reference is well-formed. This is because\n   * that valid values for the easting vary depending on the latitude.\n   * \n   * @param easting\n   *          the easting in metres.\n   * @param northing\n   *          the northing in metres.\n   * @param latZone\n   *          the latitude zone character.\n   * @param lngZone\n   *          the longitude zone number.\n   * @throws NotDefinedOnUTMGridException\n   *           if any of the parameters are invalid. Be careful that a valid\n   *           value for the easting does not necessarily mean that the UTM\n   *           reference is well-formed. The current checks do not take into\n   *           account the varying range of valid values for the easting for\n   *           different latitudes.\n   * @since 1.0\n   * @deprecated Use <code>{@link #UTMRef(int, char, double, double)}</code>\n   *             instead.\n   */\n  public UTMRef(double easting, double northing, char latZone, int lngZone)\n      throws NotDefinedOnUTMGridException {\n    this(lngZone, latZone, easting, northing);\n  }\n\n\n  /**\n   * Create a new UTM reference object. Checks are made to make sure that the\n   * given parameters are roughly valid, but the checks are not exhaustive with\n   * regards to the easting value. Catching a NotDefinedOnUTMGridException does\n   * not necessarily mean that the UTM reference is well-formed. This is because\n   * that valid values for the easting vary depending on the latitude.\n   * \n   * @param lngZone\n   *          the longitude zone number.\n   * @param latZone\n   *          the latitude zone character.\n   * @param easting\n   *          the easting in metres.\n   * @param northing\n   *          the northing in metres.\n   * @throws NotDefinedOnUTMGridException\n   *           if any of the parameters are invalid. Be careful that a valid\n   *           value for the easting does not necessarily mean that the UTM\n   *           reference is well-formed. The current checks do not take into\n   *           account the varying range of valid values for the easting for\n   *           different latitudes.\n   * @since 1.1\n   */\n  public UTMRef(int lngZone, char latZone, double easting, double northing)\n      throws NotDefinedOnUTMGridException {\n    \n    super(WGS84Datum.getInstance());\n\n    if (lngZone < 1 || lngZone > 60) {\n      throw new NotDefinedOnUTMGridException(\"Longitude zone (\" + lngZone\n          + \") is not defined on the UTM grid.\");\n    }\n\n    if (latZone < 'C' || latZone > 'X') {\n      throw new NotDefinedOnUTMGridException(\"Latitude zone (\" + latZone\n          + \") is not defined on the UTM grid.\");\n    }\n\n    if (easting < 0.0 || easting > 1000000.0) {\n      throw new NotDefinedOnUTMGridException(\"Easting (\" + easting\n          + \") is not defined on the UTM grid.\");\n    }\n\n    if (northing < 0.0 || northing > 10000000.0) {\n      throw new NotDefinedOnUTMGridException(\"Northing (\" + northing\n          + \") is not defined on the UTM grid.\");\n    }\n\n    this.easting = easting;\n    this.northing = northing;\n    this.latZone = latZone;\n    this.lngZone = lngZone;\n  }\n\n\n  /**\n   * Convert this UTM reference to a latitude and longitude.\n   * \n   * @return the converted latitude and longitude\n   * @since 1.0\n   */\n  public LatLng toLatLng() {\n    double UTM_F0 = 0.9996;\n    double a = getDatum().getReferenceEllipsoid().getSemiMajorAxis();\n    double eSquared = getDatum().getReferenceEllipsoid()\n        .getEccentricitySquared();\n    double ePrimeSquared = eSquared / (1.0 - eSquared);\n    double e1 = (1 - Math.sqrt(1 - eSquared)) / (1 + Math.sqrt(1 - eSquared));\n    double x = easting - 500000.0;\n    ;\n    double y = northing;\n    int zoneNumber = lngZone;\n    char zoneLetter = latZone;\n\n    double longitudeOrigin = (zoneNumber - 1.0) * 6.0 - 180.0 + 3.0;\n\n    // Correct y for southern hemisphere\n    if ((zoneLetter - 'N') < 0) {\n      y -= 10000000.0;\n    }\n\n    double m = y / UTM_F0;\n    double mu = m\n        / (a * (1.0 - eSquared / 4.0 - 3.0 * eSquared * eSquared / 64.0 - 5.0 * Math\n            .pow(eSquared, 3.0) / 256.0));\n\n    double phi1Rad = mu + (3.0 * e1 / 2.0 - 27.0 * Math.pow(e1, 3.0) / 32.0)\n        * Math.sin(2.0 * mu)\n        + (21.0 * e1 * e1 / 16.0 - 55.0 * Math.pow(e1, 4.0) / 32.0)\n        * Math.sin(4.0 * mu) + (151.0 * Math.pow(e1, 3.0) / 96.0)\n        * Math.sin(6.0 * mu);\n\n    double n = a\n        / Math.sqrt(1.0 - eSquared * Math.sin(phi1Rad) * Math.sin(phi1Rad));\n    double t = Math.tan(phi1Rad) * Math.tan(phi1Rad);\n    double c = ePrimeSquared * Math.cos(phi1Rad) * Math.cos(phi1Rad);\n    double r = a * (1.0 - eSquared)\n        / Math.pow(1.0 - eSquared * Math.sin(phi1Rad) * Math.sin(phi1Rad), 1.5);\n    double d = x / (n * UTM_F0);\n\n    double latitude = (phi1Rad - (n * Math.tan(phi1Rad) / r)\n        * (d\n            * d\n            / 2.0\n            - (5.0 + (3.0 * t) + (10.0 * c) - (4.0 * c * c) - (9.0 * ePrimeSquared))\n            * Math.pow(d, 4.0) / 24.0 + (61.0 + (90.0 * t) + (298.0 * c)\n            + (45.0 * t * t) - (252.0 * ePrimeSquared) - (3.0 * c * c))\n            * Math.pow(d, 6.0) / 720.0))\n        * (180.0 / Math.PI);\n\n    double longitude = longitudeOrigin\n        + ((d - (1.0 + 2.0 * t + c) * Math.pow(d, 3.0) / 6.0 + (5.0 - (2.0 * c)\n            + (28.0 * t) - (3.0 * c * c) + (8.0 * ePrimeSquared) + (24.0 * t * t))\n            * Math.pow(d, 5.0) / 120.0) / Math.cos(phi1Rad))\n        * (180.0 / Math.PI);\n\n    return new LatLng(latitude, longitude);\n  }\n\n\n  /**\n   * Work out the UTM latitude zone from the latitude.\n   * \n   * @param latitude\n   *          the latitude to find the UTM latitude zone for\n   * @return the UTM latitude zone for the given latitude\n   * @since 1.0\n   */\n  public static char getUTMLatitudeZoneLetter(double latitude) {\n    if ((84 >= latitude) && (latitude >= 72))\n      return 'X';\n    else if ((72 > latitude) && (latitude >= 64))\n      return 'W';\n    else if ((64 > latitude) && (latitude >= 56))\n      return 'V';\n    else if ((56 > latitude) && (latitude >= 48))\n      return 'U';\n    else if ((48 > latitude) && (latitude >= 40))\n      return 'T';\n    else if ((40 > latitude) && (latitude >= 32))\n      return 'S';\n    else if ((32 > latitude) && (latitude >= 24))\n      return 'R';\n    else if ((24 > latitude) && (latitude >= 16))\n      return 'Q';\n    else if ((16 > latitude) && (latitude >= 8))\n      return 'P';\n    else if ((8 > latitude) && (latitude >= 0))\n      return 'N';\n    else if ((0 > latitude) && (latitude >= -8))\n      return 'M';\n    else if ((-8 > latitude) && (latitude >= -16))\n      return 'L';\n    else if ((-16 > latitude) && (latitude >= -24))\n      return 'K';\n    else if ((-24 > latitude) && (latitude >= -32))\n      return 'J';\n    else if ((-32 > latitude) && (latitude >= -40))\n      return 'H';\n    else if ((-40 > latitude) && (latitude >= -48))\n      return 'G';\n    else if ((-48 > latitude) && (latitude >= -56))\n      return 'F';\n    else if ((-56 > latitude) && (latitude >= -64))\n      return 'E';\n    else if ((-64 > latitude) && (latitude >= -72))\n      return 'D';\n    else if ((-72 > latitude) && (latitude >= -80))\n      return 'C';\n    else\n      return 'Z';\n  }\n\n\n  /**\n   * Convert this UTM reference to a String representation for printing out.\n   * \n   * @return a String representation of this UTM reference\n   * @since 1.0\n   */\n  public String toString() {\n    return lngZone + Character.toString(latZone) + \" \" + easting + \" \"\n        + northing;\n  }\n\n\n  /**\n   * Get the easting.\n   * \n   * @return the easting\n   * @since 1.0\n   */\n  public double getEasting() {\n    return easting;\n  }\n\n\n  /**\n   * Get the northing.\n   * \n   * @return the northing\n   * @since 1.0\n   */\n  public double getNorthing() {\n    return northing;\n  }\n\n\n  /**\n   * Get the latitude zone character.\n   * \n   * @return the latitude zone character\n   * @since 1.0\n   */\n  public char getLatZone() {\n    return latZone;\n  }\n\n\n  /**\n   * Get the longitude zone number.\n   * \n   * @return the longitude zone number\n   * @since 1.0\n   */\n  public int getLngZone() {\n    return lngZone;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/Util.java",
    "content": "package uk.me.jstott.jcoord;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Some utility functions used by classes in the uk.me.jstott.jcoord package.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 11-Feb-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.0\n * @since 1.0\n */\nclass Util {\n\n  /**\n   * Calculate sin^2(x).\n   * \n   * @param x\n   *          x\n   * @return sin^2(x)\n   * @since 1.0\n   */\n  protected static double sinSquared(double x) {\n    return Math.sin(x) * Math.sin(x);\n  }\n  \n  \n  /**\n   * Calculate sin^3(x).\n   * \n   * @param x\n   *          x\n   * @return sin^3(x)\n   * @since 1.1\n   */\n  protected static double sinCubed(double x) {\n    return sinSquared(x) * Math.sin(x);\n  }\n\n\n  /**\n   * Calculate cos^2(x).\n   * \n   * @param x\n   *          x\n   * @return cos^2(x)\n   * @since 1.0\n   */\n  protected static double cosSquared(double x) {\n    return Math.cos(x) * Math.cos(x);\n  }\n  \n  \n  /**\n   * Calculate cos^3(x).\n   * \n   * @param x\n   *          x\n   * @return cos^3(x)\n   * @since 1.1\n   */\n  protected static double cosCubed(double x) {\n    return cosSquared(x) * Math.cos(x);\n  }\n\n\n  /**\n   * Calculate tan^2(x).\n   * \n   * @param x\n   *          x\n   * @return tan^2(x)\n   * @since 1.0\n   */\n  protected static double tanSquared(double x) {\n    return Math.tan(x) * Math.tan(x);\n  }\n\n\n  /**\n   * Calculate sec(x).\n   * \n   * @param x\n   *          x\n   * @return sec(x)\n   * @since 1.0\n   */\n  protected static double sec(double x) {\n    return 1.0 / Math.cos(x);\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/Datum.java",
    "content": "package uk.me.jstott.jcoord.datum;\n\nimport uk.me.jstott.jcoord.ellipsoid.Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * The Datum class represents a set of parameters for describing a particular\n * datum, including a name, the reference ellipsoid used and the seven\n * parameters required to translate co-ordinates in this datum to the WGS84\n * datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 05-Mar-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic abstract class Datum {\n\n  protected String name;\n\n  protected Ellipsoid ellipsoid;\n\n  /**\n   * Translation along the x-axis for use in 7-parameter Helmert\n   * transformations. This value should be used to convert a co-ordinate in a\n   * given datum to the WGS84 datum.\n   */\n  protected double dx;\n\n  /**\n   * Translation along the y-axis for use in 7-parameter Helmert\n   * transformations. This value should be used to convert a co-ordinate in a\n   * given datum to the WGS84 datum.\n   */\n  protected double dy;\n\n  /**\n   * Translation along the z-axis for use in 7-parameter Helmert\n   * transformations. This value should be used to convert a co-ordinate in a\n   * given datum to the WGS84 datum.\n   */\n  protected double dz;\n\n  /**\n   * Scale factor for use in 7-parameter Helmert transformations. This value\n   * should be used to convert a co-ordinate in a given datum to the WGS84\n   * datum.\n   */\n  protected double ds;\n\n  /**\n   * Rotation about the x-axis for use in 7-parameter Helmert transformations.\n   * This value should be used to convert a co-ordinate in a given datum to the\n   * WGS84 datum.\n   */\n  protected double rx;\n\n  /**\n   * Rotation about the y-axis for use in 7-parameter Helmert transformations.\n   * This value should be used to convert a co-ordinate in a given datum to the\n   * WGS84 datum.\n   */\n  protected double ry;\n\n  /**\n   * Rotation about the z-axis for use in 7-parameter Helmert transformations.\n   * This value should be used to convert a co-ordinate in a given datum to the\n   * WGS84 datum.\n   */\n  protected double rz;\n\n\n  /**\n   * Get the name of this Datum.\n   * \n   * @return the name of this Datum.\n   * @since 1.1\n   */\n  public String getName() {\n    return name;\n  }\n\n\n  /**\n   * Get the reference ellipsoid associated with this Datum.\n   * \n   * @return the reference ellipsoid associated with this Datum.\n   * @since 1.1\n   */\n  public Ellipsoid getReferenceEllipsoid() {\n    return ellipsoid;\n  }\n\n\n  /**\n   * \n   * \n   * @return the ds\n   * @since 1.1\n   */\n  public double getDs() {\n    return ds;\n  }\n\n\n  /**\n   * \n   * \n   * @return the dx\n   * @since 1.1\n   */\n  public double getDx() {\n    return dx;\n  }\n\n\n  /**\n   * \n   * \n   * @return the dy\n   * @since 1.1\n   */\n  public double getDy() {\n    return dy;\n  }\n\n\n  /**\n   * \n   * \n   * @return the dz\n   * @since 1.1\n   */\n  public double getDz() {\n    return dz;\n  }\n\n\n  /**\n   * \n   * \n   * @return the rx\n   * @since 1.1\n   */\n  public double getRx() {\n    return rx;\n  }\n\n\n  /**\n   * \n   * \n   * @return the ry\n   * @since 1.1\n   */\n  public double getRy() {\n    return ry;\n  }\n\n\n  /**\n   * \n   * \n   * @return the rz\n   * @since 1.1\n   */\n  public double getRz() {\n    return rz;\n  }\n\n\n  /**\n   * Get a String representation of the parameters of a Datum object.\n   * \n   * @return a String representation of the parameters of a Datum object.\n   * @since 1.1\n   */\n  public String toString() {\n    return getName() + \" \" + ellipsoid.toString() + \" dx=\" + dx + \" dy=\" + dy\n        + \" dz=\" + dz + \" ds=\" + ds + \" rx=\" + rx + \" ry=\" + ry + \" rz=\" + rz;\n  }\n\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/ETRF89Datum.java",
    "content": "package uk.me.jstott.jcoord.datum;\n\nimport uk.me.jstott.jcoord.ellipsoid.WGS84Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 03-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class ETRF89Datum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static ETRF89Datum ref = null;\n  \n\n  /**\n   * Create a new ETRF89Datum object.\n   * \n   * @since 1.1\n   */\n  private ETRF89Datum() {\n    name = \"European Terrestrial Reference Frame (ETRF89)\";\n    ellipsoid = WGS84Ellipsoid.getInstance();\n    dx = 0.0;\n    dy = 0.0;\n    dz = 0.0;\n    ds = 0.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static ETRF89Datum getInstance() {\n    if (ref == null) { \n      ref = new ETRF89Datum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/Ireland1965Datum.java",
    "content": "package uk.me.jstott.jcoord.datum;\n\nimport uk.me.jstott.jcoord.ellipsoid.ModifiedAiryEllipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 03-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class Ireland1965Datum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static Ireland1965Datum ref = null;\n  \n\n  /**\n   * Create a new Ireland 1965 Datum object.\n   * \n   * @since 1.1\n   */\n  private Ireland1965Datum() {\n    name = \"Ireland 1965\";\n    ellipsoid = ModifiedAiryEllipsoid.getInstance();\n    dx = 482.53;\n    dy = -130.596;\n    dz = 564.557;\n    ds = 8.15;\n    rx = -1.042;\n    ry = -0.214;\n    rz = -0.631;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static Ireland1965Datum getInstance() {\n    if (ref == null) { \n      ref = new Ireland1965Datum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/OSGB36Datum.java",
    "content": "package uk.me.jstott.jcoord.datum;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 05-Mar-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class OSGB36Datum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static OSGB36Datum ref = null;\n  \n\n  /**\n   * Create a new OSGB36 object.\n   * \n   * @since 1.1\n   */\n  private OSGB36Datum() {\n    name = \"Ordnance Survey of Great Britain 1936 (OSGB36)\";\n    ellipsoid = uk.me.jstott.jcoord.ellipsoid.Airy1830Ellipsoid.getInstance();\n    dx = 446.448;\n    dy = -125.157;\n    dz = 542.06;\n    ds = -20.4894;\n    rx = 0.1502;\n    ry = 0.2470;\n    rz = 0.8421;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static OSGB36Datum getInstance() {\n    if (ref == null) { \n      ref = new OSGB36Datum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/WGS84Datum.java",
    "content": "package uk.me.jstott.jcoord.datum;\n\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class WGS84Datum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static WGS84Datum ref = null;\n  \n\n  /**\n   * Create a new WGS84 object.\n   * \n   * @since 1.1\n   */\n  public WGS84Datum() {\n    name = \"World Geodetic System 1984 (WGS84)\";\n    ellipsoid = uk.me.jstott.jcoord.ellipsoid.WGS84Ellipsoid.getInstance();\n    dx = 0.0;\n    dy = 0.0;\n    dz = 0.0;\n    ds = 1.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static WGS84Datum getInstance() {\n    if (ref == null) { \n      ref = new WGS84Datum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/nad27/NAD27AlaskaDatum.java",
    "content": "package uk.me.jstott.jcoord.datum.nad27;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Clarke1866Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class representing the NAD27 (Alaska) datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NAD27AlaskaDatum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static NAD27AlaskaDatum ref = null;\n  \n\n  /**\n   * Create a new NAD27 (Alaska) datum object.\n   * \n   * @since 1.1\n   */\n  private NAD27AlaskaDatum() {\n    name = \"North American Datum 1927 (NAD27) - Alaska\";\n    ellipsoid = Clarke1866Ellipsoid.getInstance();\n    dx = -5.0;\n    dy = 135.0;\n    dz = 172.0;\n    ds = 0.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static NAD27AlaskaDatum getInstance() {\n    if (ref == null) { \n      ref = new NAD27AlaskaDatum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/nad27/NAD27AlbertaBritishColumbiaDatum.java",
    "content": "package uk.me.jstott.jcoord.datum.nad27;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Clarke1866Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class representing the NAD27 (Alberta and British Columbia) datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NAD27AlbertaBritishColumbiaDatum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static NAD27AlbertaBritishColumbiaDatum ref = null;\n  \n\n  /**\n   * Create a new NAD27 (Alberta and British Columbia) datum object.\n   * \n   * @since 1.1\n   */\n  private NAD27AlbertaBritishColumbiaDatum() {\n    name = \"North American Datum 1927 (NAD27) - Alberta and British Columbia\";\n    ellipsoid = Clarke1866Ellipsoid.getInstance();\n    dx = -7.0;\n    dy = 162.0;\n    dz = 188.0;\n    ds = 0.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static NAD27AlbertaBritishColumbiaDatum getInstance() {\n    if (ref == null) { \n      ref = new NAD27AlbertaBritishColumbiaDatum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/nad27/NAD27AleutianEastDatum.java",
    "content": "package uk.me.jstott.jcoord.datum.nad27;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Clarke1866Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class representing the NAD27 (Aleutian East) datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NAD27AleutianEastDatum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static NAD27AleutianEastDatum ref = null;\n  \n\n  /**\n   * Create a new NAD27 (Aleutian East) datum object.\n   * \n   * @since 1.1\n   */\n  private NAD27AleutianEastDatum() {\n    name = \"North American Datum 1927 (NAD27) - Aleutian East\";\n    ellipsoid = Clarke1866Ellipsoid.getInstance();\n    dx = -2.0;\n    dy = 152.0;\n    dz = 149.0;\n    ds = 0.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static NAD27AleutianEastDatum getInstance() {\n    if (ref == null) { \n      ref = new NAD27AleutianEastDatum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/nad27/NAD27AleutianWestDatum.java",
    "content": "package uk.me.jstott.jcoord.datum.nad27;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Clarke1866Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class representing the NAD27 (Aleutian West) datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NAD27AleutianWestDatum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static NAD27AleutianWestDatum ref = null;\n  \n\n  /**\n   * Create a new NAD27 (Aleutian West) datum object.\n   * \n   * @since 1.1\n   */\n  private NAD27AleutianWestDatum() {\n    name = \"North American Datum 1927 (NAD27) - Aleutian West\";\n    ellipsoid = Clarke1866Ellipsoid.getInstance();\n    dx = 2.0;\n    dy = 204.0;\n    dz = 105.0;\n    ds = 0.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static NAD27AleutianWestDatum getInstance() {\n    if (ref == null) { \n      ref = new NAD27AleutianWestDatum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/nad27/NAD27BahamasDatum.java",
    "content": "package uk.me.jstott.jcoord.datum.nad27;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Clarke1866Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class representing the NAD27 (Bahamas) datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NAD27BahamasDatum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static NAD27BahamasDatum ref = null;\n  \n\n  /**\n   * Create a new NAD27 (Bahamas) datum object.\n   * \n   * @since 1.1\n   */\n  private NAD27BahamasDatum() {\n    name = \"North American Datum 1927 (NAD27) - Bahamas\";\n    ellipsoid = Clarke1866Ellipsoid.getInstance();\n    dx = -4.0;\n    dy = 154.0;\n    dz = 178.0;\n    ds = 0.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static NAD27BahamasDatum getInstance() {\n    if (ref == null) { \n      ref = new NAD27BahamasDatum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/nad27/NAD27CanadaDatum.java",
    "content": "package uk.me.jstott.jcoord.datum.nad27;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Clarke1866Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class representing the NAD27 (Canada) datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NAD27CanadaDatum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static NAD27CanadaDatum ref = null;\n  \n\n  /**\n   * Create a new NAD27 (Canada) datum object.\n   * \n   * @since 1.1\n   */\n  private NAD27CanadaDatum() {\n    name = \"North American Datum 1927 (NAD27) - Canada\";\n    ellipsoid = Clarke1866Ellipsoid.getInstance();\n    dx = -10.0;\n    dy = 158.0;\n    dz = 187.0;\n    ds = 0.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static NAD27CanadaDatum getInstance() {\n    if (ref == null) { \n      ref = new NAD27CanadaDatum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/nad27/NAD27CanadaEastDatum.java",
    "content": "package uk.me.jstott.jcoord.datum.nad27;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Clarke1866Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class representing the NAD27 (Canada East) datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NAD27CanadaEastDatum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static NAD27CanadaEastDatum ref = null;\n  \n\n  /**\n   * Create a new NAD27 (Canada East) datum object.\n   * \n   * @since 1.1\n   */\n  private NAD27CanadaEastDatum() {\n    name = \"North American Datum 1927 (NAD27) - Canada East\";\n    ellipsoid = Clarke1866Ellipsoid.getInstance();\n    dx = -22.0;\n    dy = 160.0;\n    dz = 190.0;\n    ds = 0.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static NAD27CanadaEastDatum getInstance() {\n    if (ref == null) { \n      ref = new NAD27CanadaEastDatum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/nad27/NAD27CanadaManitobaOntarioDatum.java",
    "content": "package uk.me.jstott.jcoord.datum.nad27;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Clarke1866Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class representing the NAD27 (Canada Manitoba/Ontario) datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NAD27CanadaManitobaOntarioDatum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static NAD27CanadaManitobaOntarioDatum ref = null;\n  \n\n  /**\n   * Create a new NAD27 (Canada Manitoba/Ontario) datum object.\n   * \n   * @since 1.1\n   */\n  private NAD27CanadaManitobaOntarioDatum() {\n    name = \"North American Datum 1927 (NAD27) - Canada Manitoba/Ontario\";\n    ellipsoid = Clarke1866Ellipsoid.getInstance();\n    dx = -9.0;\n    dy = 157.0;\n    dz = 184.0;\n    ds = 0.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static NAD27CanadaManitobaOntarioDatum getInstance() {\n    if (ref == null) { \n      ref = new NAD27CanadaManitobaOntarioDatum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/nad27/NAD27CanadaNWTerritoryDatum.java",
    "content": "package uk.me.jstott.jcoord.datum.nad27;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Clarke1866Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class representing the NAD27 (Canada NW Territory) datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NAD27CanadaNWTerritoryDatum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static NAD27CanadaNWTerritoryDatum ref = null;\n  \n\n  /**\n   * Create a new NAD27 (Canada NW Territory) datum object.\n   * \n   * @since 1.1\n   */\n  private NAD27CanadaNWTerritoryDatum() {\n    name = \"North American Datum 1927 (NAD27) - Canada NW Territory\";\n    ellipsoid = Clarke1866Ellipsoid.getInstance();\n    dx = 4.0;\n    dy = 159.0;\n    dz = 188.0;\n    ds = 0.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static NAD27CanadaNWTerritoryDatum getInstance() {\n    if (ref == null) { \n      ref = new NAD27CanadaNWTerritoryDatum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/nad27/NAD27CanadaYukonDatum.java",
    "content": "package uk.me.jstott.jcoord.datum.nad27;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Clarke1866Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class representing the NAD27 (Canada Yukon) datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NAD27CanadaYukonDatum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static NAD27CanadaYukonDatum ref = null;\n  \n\n  /**\n   * Create a new NAD27 (Canada Yukon) datum object.\n   * \n   * @since 1.1\n   */\n  private NAD27CanadaYukonDatum() {\n    name = \"North American Datum 1927 (NAD27) - Canada Yukon\";\n    ellipsoid = Clarke1866Ellipsoid.getInstance();\n    dx = -7.0;\n    dy = 139.0;\n    dz = 181.0;\n    ds = 0.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static NAD27CanadaYukonDatum getInstance() {\n    if (ref == null) { \n      ref = new NAD27CanadaYukonDatum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/nad27/NAD27CanalZoneDatum.java",
    "content": "package uk.me.jstott.jcoord.datum.nad27;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Clarke1866Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class representing the NAD27 (Canal Zone) datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NAD27CanalZoneDatum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static NAD27CanalZoneDatum ref = null;\n  \n\n  /**\n   * Create a new NAD27 (Canal Zone) datum object.\n   * \n   * @since 1.1\n   */\n  private NAD27CanalZoneDatum() {\n    name = \"North American Datum 1927 (NAD27) - Canal Zone\";\n    ellipsoid = Clarke1866Ellipsoid.getInstance();\n    dx = 0.0;\n    dy = 125.0;\n    dz = 201.0;\n    ds = 0.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static NAD27CanalZoneDatum getInstance() {\n    if (ref == null) { \n      ref = new NAD27CanalZoneDatum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/nad27/NAD27CaribbeanDatum.java",
    "content": "package uk.me.jstott.jcoord.datum.nad27;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Clarke1866Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class representing the NAD27 (Caribbean) datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NAD27CaribbeanDatum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static NAD27CaribbeanDatum ref = null;\n  \n\n  /**\n   * Create a new NAD27 (Caribbean) datum object.\n   * \n   * @since 1.1\n   */\n  private NAD27CaribbeanDatum() {\n    name = \"North American Datum 1927 (NAD27) - Caribbean\";\n    ellipsoid = Clarke1866Ellipsoid.getInstance();\n    dx = -3.0;\n    dy = 142.0;\n    dz = 183.0;\n    ds = 0.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static NAD27CaribbeanDatum getInstance() {\n    if (ref == null) { \n      ref = new NAD27CaribbeanDatum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/nad27/NAD27CentralAmericaDatum.java",
    "content": "package uk.me.jstott.jcoord.datum.nad27;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Clarke1866Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class representing the NAD27 (Central America) datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NAD27CentralAmericaDatum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static NAD27CentralAmericaDatum ref = null;\n  \n\n  /**\n   * Create a new NAD27 (Alaska) datum object.\n   * \n   * @since 1.1\n   */\n  private NAD27CentralAmericaDatum() {\n    name = \"North American Datum 1927 (NAD27) - Central America\";\n    ellipsoid = Clarke1866Ellipsoid.getInstance();\n    dx = 0.0;\n    dy = 125.0;\n    dz = 194.0;\n    ds = 0.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static NAD27CentralAmericaDatum getInstance() {\n    if (ref == null) { \n      ref = new NAD27CentralAmericaDatum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/nad27/NAD27ContiguousUSDatum.java",
    "content": "package uk.me.jstott.jcoord.datum.nad27;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Clarke1866Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class representing the NAD27 (Contiguous United States) datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NAD27ContiguousUSDatum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static NAD27ContiguousUSDatum ref = null;\n  \n\n  /**\n   * Create a new NAD27 (Contiguous United States) datum object.\n   * \n   * @since 1.1\n   */\n  private NAD27ContiguousUSDatum() {\n    name = \"North American Datum 1927 (NAD27) - Contiguous United States\";\n    ellipsoid = Clarke1866Ellipsoid.getInstance();\n    dx = -8.0;\n    dy = 160.0;\n    dz = 176.0;\n    ds = 0.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static NAD27ContiguousUSDatum getInstance() {\n    if (ref == null) { \n      ref = new NAD27ContiguousUSDatum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/nad27/NAD27CubaDatum.java",
    "content": "package uk.me.jstott.jcoord.datum.nad27;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Clarke1866Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class representing the NAD27 (Cuba) datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NAD27CubaDatum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static NAD27CubaDatum ref = null;\n  \n\n  /**\n   * Create a new NAD27 (Cuba) datum object.\n   * \n   * @since 1.1\n   */\n  private NAD27CubaDatum() {\n    name = \"North American Datum 1927 (NAD27) - Cuba\";\n    ellipsoid = Clarke1866Ellipsoid.getInstance();\n    dx = -9.0;\n    dy = 152.0;\n    dz = 178.0;\n    ds = 0.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static NAD27CubaDatum getInstance() {\n    if (ref == null) { \n      ref = new NAD27CubaDatum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/nad27/NAD27EasternUSDatum.java",
    "content": "package uk.me.jstott.jcoord.datum.nad27;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Clarke1866Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class representing the NAD27 (Eastern US) datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NAD27EasternUSDatum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static NAD27EasternUSDatum ref = null;\n  \n\n  /**\n   * Create a new NAD27 (Eastern US) datum object.\n   * \n   * @since 1.1\n   */\n  private NAD27EasternUSDatum() {\n    name = \"North American Datum 1927 (NAD27) - Eastern US\";\n    ellipsoid = Clarke1866Ellipsoid.getInstance();\n    dx = -9.0;\n    dy = 161.0;\n    dz = 179.0;\n    ds = 0.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static NAD27EasternUSDatum getInstance() {\n    if (ref == null) { \n      ref = new NAD27EasternUSDatum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/nad27/NAD27GreenlandDatum.java",
    "content": "package uk.me.jstott.jcoord.datum.nad27;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Clarke1866Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class representing the NAD27 (Greenland) datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NAD27GreenlandDatum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static NAD27GreenlandDatum ref = null;\n  \n\n  /**\n   * Create a new NAD27 (Greenland) datum object.\n   * \n   * @since 1.1\n   */\n  private NAD27GreenlandDatum() {\n    name = \"North American Datum 1927 (NAD27) - Greenland\";\n    ellipsoid = Clarke1866Ellipsoid.getInstance();\n    dx = 11.0;\n    dy = 114.0;\n    dz = 195.0;\n    ds = 0.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static NAD27GreenlandDatum getInstance() {\n    if (ref == null) { \n      ref = new NAD27GreenlandDatum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/nad27/NAD27MexicoDatum.java",
    "content": "package uk.me.jstott.jcoord.datum.nad27;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Clarke1866Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class representing the NAD27 (Mexico) datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NAD27MexicoDatum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static NAD27MexicoDatum ref = null;\n  \n\n  /**\n   * Create a new NAD27 (Mexico) datum object.\n   * \n   * @since 1.1\n   */\n  private NAD27MexicoDatum() {\n    name = \"North American Datum 1927 (NAD27) - Mexico\";\n    ellipsoid = Clarke1866Ellipsoid.getInstance();\n    dx = -12.0;\n    dy = 130.0;\n    dz = 190.0;\n    ds = 0.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static NAD27MexicoDatum getInstance() {\n    if (ref == null) { \n      ref = new NAD27MexicoDatum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/nad27/NAD27SanSalvadorDatum.java",
    "content": "package uk.me.jstott.jcoord.datum.nad27;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Clarke1866Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class representing the NAD27 (San Salvador) datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NAD27SanSalvadorDatum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static NAD27SanSalvadorDatum ref = null;\n  \n\n  /**\n   * Create a new NAD27 (San Salvador) datum object.\n   * \n   * @since 1.1\n   */\n  private NAD27SanSalvadorDatum() {\n    name = \"North American Datum 1927 (NAD27) - San Salvador\";\n    ellipsoid = Clarke1866Ellipsoid.getInstance();\n    dx = 1.0;\n    dy = 140.0;\n    dz = 165.0;\n    ds = 0.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static NAD27SanSalvadorDatum getInstance() {\n    if (ref == null) { \n      ref = new NAD27SanSalvadorDatum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/datum/nad27/NAD27WesternUSDatum.java",
    "content": "package uk.me.jstott.jcoord.datum.nad27;\n\nimport uk.me.jstott.jcoord.datum.Datum;\nimport uk.me.jstott.jcoord.ellipsoid.Clarke1866Ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class representing the NAD27 (Western US) datum.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NAD27WesternUSDatum extends Datum {\n  \n  /**\n   * Static reference of this datum.\n   */\n  private static NAD27WesternUSDatum ref = null;\n  \n\n  /**\n   * Create a new NAD27 (Western US) datum object.\n   * \n   * @since 1.1\n   */\n  private NAD27WesternUSDatum() {\n    name = \"North American Datum 1927 (NAD27) - Western US\";\n    ellipsoid = Clarke1866Ellipsoid.getInstance();\n    dx = -8.0;\n    dy = 159.0;\n    dz = 175.0;\n    ds = 0.0;\n    rx = 0.0;\n    ry = 0.0;\n    rz = 0.0;\n  }\n  \n  \n  /**\n   * Get the static instance of this datum\n   * \n   * @return a reference to the static instance of this datum\n   * @since 1.1\n   */\n  public static NAD27WesternUSDatum getInstance() {\n    if (ref == null) { \n      ref = new NAD27WesternUSDatum();\n    }\n    return ref;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/Airy1830Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the Airy 1830 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class Airy1830Ellipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static Airy1830Ellipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the Airy 1830 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private Airy1830Ellipsoid() {\n    super(6377563.396, 6356256.909);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static Airy1830Ellipsoid getInstance() {\n    if (ref == null) {\n      ref = new Airy1830Ellipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/AustralianNational1966Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the Australian National 1966 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class AustralianNational1966Ellipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static AustralianNational1966Ellipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the Australian National 1966 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private AustralianNational1966Ellipsoid() {\n    super(6378160.0, 6356774.719);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static AustralianNational1966Ellipsoid getInstance() {\n    if (ref == null) {\n      ref = new AustralianNational1966Ellipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/Bessel1841Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the Bessel 1841 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class Bessel1841Ellipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static Bessel1841Ellipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the Bessel 1841 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private Bessel1841Ellipsoid() {\n    super(6377397.155, 6356078.9629);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static Bessel1841Ellipsoid getInstance() {\n    if (ref == null) {\n      ref = new Bessel1841Ellipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/Clarke1866Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the Clarke 1866 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class Clarke1866Ellipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static Clarke1866Ellipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the Clarke 1866 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private Clarke1866Ellipsoid() {\n    super(6378206.4, 6356583.8);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static Clarke1866Ellipsoid getInstance() {\n    if (ref == null) {\n      ref = new Clarke1866Ellipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/Clarke1880Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the Clarke 1880 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class Clarke1880Ellipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static Clarke1880Ellipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the Clarke 1880 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private Clarke1880Ellipsoid() {\n    super(6378249.145, 6356514.8696);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static Clarke1880Ellipsoid getInstance() {\n    if (ref == null) {\n      ref = new Clarke1880Ellipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class to represent a reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic abstract class Ellipsoid {\n\n  /**\n   * Semi major axis.\n   */\n  protected double semiMajorAxis;\n\n  /**\n   * Semi minor axis.\n   */\n  protected double semiMinorAxis;\n\n  /**\n   * Eccentricity squared.\n   */\n  protected double eccentricitySquared;\n\n  /**\n   * Flattening.\n   */\n  protected double flattening;\n\n\n  /**\n   * Create a new ellipsoid with the given parameters.\n   * \n   * @param semiMajorAxis\n   *          the semi major axis.\n   * @param semiMinorAxis\n   *          the semi minor axis.\n   * @since 1.1\n   */\n  public Ellipsoid(double semiMajorAxis, double semiMinorAxis) {\n    this.semiMajorAxis = semiMajorAxis;\n    this.semiMinorAxis = semiMinorAxis;\n    double semiMajorAxisSquared = semiMajorAxis * semiMajorAxis;\n    double semiMinorAxisSquared = semiMinorAxis * semiMinorAxis;\n    flattening = (semiMajorAxis - semiMinorAxis) / semiMajorAxis;\n    eccentricitySquared = (semiMajorAxisSquared - semiMinorAxisSquared)\n        / semiMajorAxisSquared;\n  }\n\n\n  /**\n   * Create a new ellipsoid with the given parameters. If either the\n   * semiMinorAxis or the eccentricitySquared are Double.NaN, then that value is\n   * calculated from the other two parameters. An IllegalArgumentException is\n   * thrown if both the semiMinorAxis and the eccentricitySquared are\n   * Double.NaN.\n   * \n   * @param semiMajorAxis\n   *          the semi major axis.\n   * @param semiMinorAxis\n   *          the semi minor axis.\n   * @param eccentricitySquared\n   *          the eccentricity squared.\n   * @throws IllegalArgumentException\n   *           is both the semiMinorAxis and eccentricitySquared parameters are\n   *           Double.NaN.\n   * @since 1.1\n   */\n  public Ellipsoid(double semiMajorAxis, double semiMinorAxis,\n      double eccentricitySquared) throws IllegalArgumentException {\n\n    if (Double.isNaN(semiMinorAxis) && Double.isNaN(eccentricitySquared)) {\n      throw new IllegalArgumentException(\n          \"At least one of semiMinorAxis and eccentricitySquared must be defined\");\n    }\n\n    this.semiMajorAxis = semiMajorAxis;\n    double semiMajorAxisSquared = semiMajorAxis * semiMajorAxis;\n\n    if (Double.isNaN(semiMinorAxis)) {\n      this.semiMinorAxis = Math.sqrt(semiMajorAxisSquared\n          * (1 - eccentricitySquared));\n    } else {\n      this.semiMinorAxis = semiMinorAxis;\n    }\n\n    double semiMinorAxisSquared = this.semiMinorAxis * this.semiMinorAxis;\n\n    flattening = (this.semiMajorAxis - this.semiMinorAxis) / this.semiMajorAxis;\n\n    if (Double.isNaN(eccentricitySquared)) {\n      this.eccentricitySquared = (semiMajorAxisSquared - semiMinorAxisSquared)\n          / semiMajorAxisSquared;\n    } else {\n      this.eccentricitySquared = eccentricitySquared;\n    }\n  }\n\n\n  /**\n   * Get a String representation of the Ellipsoid\n   * \n   * @return a String representation of the Ellipsoid\n   * @since 1.1\n   */\n  public String toString() {\n    return \"[semi-major axis = \" + getSemiMajorAxis() + \", semi-minor axis = \"\n        + getSemiMinorAxis() + \"]\";\n  }\n\n\n  /**\n   * Get the eccentricity squared.\n   * \n   * @return Returns the eccentricitySquared.\n   * @since 1.1\n   */\n  public double getEccentricitySquared() {\n    return eccentricitySquared;\n  }\n\n\n  /**\n   * Get the flattening.\n   * \n   * @return Returns the flattening.\n   * @since 1.1\n   */\n  public double getFlattening() {\n    return flattening;\n  }\n\n\n  /**\n   * Get the semi major axis.\n   * \n   * @return Returns the semiMajorAxis.\n   * @since 1.1\n   */\n  public double getSemiMajorAxis() {\n    return semiMajorAxis;\n  }\n\n\n  /**\n   * Get the semi minor axis.\n   * \n   * @return Returns the semiMinorAxis.\n   * @since 1.1\n   */\n  public double getSemiMinorAxis() {\n    return semiMinorAxis;\n  }\n}\n"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/EverestEllipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the Everest 1830 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class EverestEllipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static EverestEllipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the Everest 1830 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private EverestEllipsoid() {\n    super(6377276.34518, 6356075.41511);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static EverestEllipsoid getInstance() {\n    if (ref == null) {\n      ref = new EverestEllipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/Fischer1960Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the Fischer 1960 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class Fischer1960Ellipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static Fischer1960Ellipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the Fischer 1960 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private Fischer1960Ellipsoid() {\n    super(6378166.0, 6356784.284);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static Fischer1960Ellipsoid getInstance() {\n    if (ref == null) {\n      ref = new Fischer1960Ellipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/Fischer1968Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the Fischer 1968 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class Fischer1968Ellipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static Fischer1968Ellipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the Fischer 1968 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private Fischer1968Ellipsoid() {\n    super(6378150.0, 6356768.337);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static Fischer1968Ellipsoid getInstance() {\n    if (ref == null) {\n      ref = new Fischer1968Ellipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/GRS67Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the GRS67 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class GRS67Ellipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static GRS67Ellipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the GRS67 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private GRS67Ellipsoid() {\n    super(6378160.0, 6356774.51609);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static GRS67Ellipsoid getInstance() {\n    if (ref == null) {\n      ref = new GRS67Ellipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/GRS75Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the GRS75 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class GRS75Ellipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static GRS75Ellipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the GRS75 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private GRS75Ellipsoid() {\n    super(6378140.0, 6356755.288);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static GRS75Ellipsoid getInstance() {\n    if (ref == null) {\n      ref = new GRS75Ellipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/GRS80Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the GRS80 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class GRS80Ellipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static GRS80Ellipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the GRS80 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private GRS80Ellipsoid() {\n    super(6378137, 6356752.3141);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static GRS80Ellipsoid getInstance() {\n    if (ref == null) {\n      ref = new GRS80Ellipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/Hayford1910Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the Hayford 1910 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class Hayford1910Ellipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static Hayford1910Ellipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the Hayford 1910 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private Hayford1910Ellipsoid() {\n    super(6378388.0, 6356911.946);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static Hayford1910Ellipsoid getInstance() {\n    if (ref == null) {\n      ref = new Hayford1910Ellipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/Helmert1906Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the Helmert 1906 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class Helmert1906Ellipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static Helmert1906Ellipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the Helmert 1906 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private Helmert1906Ellipsoid() {\n    super(6378200.0, 6356818.17);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static Helmert1906Ellipsoid getInstance() {\n    if (ref == null) {\n      ref = new Helmert1906Ellipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/Hough1956Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the Hough 1956 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class Hough1956Ellipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static Hough1956Ellipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the Hough 1956 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private Hough1956Ellipsoid() {\n    super(6378270.0, 6356794.34);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static Hough1956Ellipsoid getInstance() {\n    if (ref == null) {\n      ref = new Hough1956Ellipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/IERS1989Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the IERS 1989 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class IERS1989Ellipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static IERS1989Ellipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the IERS 1989 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private IERS1989Ellipsoid() {\n    super(6378136.0, 6356751.302);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static IERS1989Ellipsoid getInstance() {\n    if (ref == null) {\n      ref = new IERS1989Ellipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/InternationalEllipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the International reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class InternationalEllipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static InternationalEllipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the International reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private InternationalEllipsoid() {\n    super(6378388, 6356911.9462);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static InternationalEllipsoid getInstance() {\n    if (ref == null) {\n      ref = new InternationalEllipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/Krassovsky1940Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the Krassovsky 1940 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class Krassovsky1940Ellipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static Krassovsky1940Ellipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the Krassovsky 1940 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private Krassovsky1940Ellipsoid() {\n    super(6378245.0, 6356863.019);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static Krassovsky1940Ellipsoid getInstance() {\n    if (ref == null) {\n      ref = new Krassovsky1940Ellipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/ModifiedAiryEllipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the Modified Airy reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class ModifiedAiryEllipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static ModifiedAiryEllipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the Modified Airy reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private ModifiedAiryEllipsoid() {\n    super(6377340.189, Double.NaN, 0.00667054015);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static ModifiedAiryEllipsoid getInstance() {\n    if (ref == null) {\n      ref = new ModifiedAiryEllipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/ModifiedEverestEllipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the Modified Everest reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class ModifiedEverestEllipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static ModifiedEverestEllipsoid ref = null;\n\n  /**\n   * Create an object defining the Modified Everest reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  public ModifiedEverestEllipsoid() {\n    super(6377304.063, 6356103.039);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static ModifiedEverestEllipsoid getInstance() {\n    if (ref == null) {\n      ref = new ModifiedEverestEllipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/NewInternational1967Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the New International 1967 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class NewInternational1967Ellipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static NewInternational1967Ellipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the Ne wInternational 1967 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private NewInternational1967Ellipsoid() {\n    super(6378157.5, 6356772.2);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static NewInternational1967Ellipsoid getInstance() {\n    if (ref == null) {\n      ref = new NewInternational1967Ellipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/SouthAmerican1969Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the South American 1969 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class SouthAmerican1969Ellipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static SouthAmerican1969Ellipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the South American 1969 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private SouthAmerican1969Ellipsoid() {\n    super(6378160.0, 6356774.7192);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static SouthAmerican1969Ellipsoid getInstance() {\n    if (ref == null) {\n      ref = new SouthAmerican1969Ellipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/WGS60Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the WGS60 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class WGS60Ellipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static WGS60Ellipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the WGS60 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private WGS60Ellipsoid() {\n    super(6378165.0, 6356783.287);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static WGS60Ellipsoid getInstance() {\n    if (ref == null) {\n      ref = new WGS60Ellipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/WGS66Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the WGS66 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class WGS66Ellipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static WGS66Ellipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the WGS66 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private WGS66Ellipsoid() {\n    super(6378145.0, 6356759.770);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static WGS66Ellipsoid getInstance() {\n    if (ref == null) {\n      ref = new WGS66Ellipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/WGS72Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the WGS72 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class WGS72Ellipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static WGS72Ellipsoid ref = null;\n  \n\n  /**\n   * Create an object defining the WGS72 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private WGS72Ellipsoid() {\n    super(6378135, 6356750.5);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static WGS72Ellipsoid getInstance() {\n    if (ref == null) {\n      ref = new WGS72Ellipsoid();\n    }\n    return ref;\n  }\n}"
  },
  {
    "path": "src/uk/me/jstott/jcoord/ellipsoid/WGS84Ellipsoid.java",
    "content": "package uk.me.jstott.jcoord.ellipsoid;\n\n/**\n * <p>\n * This class is part of the Jcoord package. Visit the <a\n * href=\"http://www.jstott.me.uk/jcoord/\">Jcoord</a> website for more\n * information.\n * </p>\n * \n * <p>\n * Class defining the WGS84 reference ellipsoid.\n * </p>\n * \n * <p>\n * (c) 2006 Jonathan Stott\n * </p>\n * \n * <p>\n * Created on 02-Apr-2006\n * </p>\n * \n * @author Jonathan Stott\n * @version 1.1\n * @since 1.1\n */\npublic class WGS84Ellipsoid extends Ellipsoid {\n  \n  /**\n   * Static reference of this ellipsoid.\n   */\n  private static WGS84Ellipsoid ref = null;\n\n  /**\n   * Create an object defining a WGS84 reference ellipsoid.\n   * \n   * @since 1.1\n   */\n  private WGS84Ellipsoid() {\n    super(6378137, 6356752.3142);\n  }\n  \n  \n  /**\n   * Get the static instance of this ellipsoid\n   * \n   * @return a reference to the static instance of this ellipsoid\n   * @since 1.1\n   */\n  public static WGS84Ellipsoid getInstance() {\n    if (ref == null) {\n      ref = new WGS84Ellipsoid();\n    }\n    return ref;\n  }\n}"
  }
]