├── .gitignore ├── LICENSE ├── README.md ├── manifest.xml ├── monkey.jungle ├── resources-rectangle └── strings.xml ├── resources-semiround ├── resources ├── drawables │ ├── drawables.xml │ ├── launcher_icon.png │ └── weather │ │ ├── clearsky_day.png │ │ ├── clearsky_night.png │ │ ├── clearsky_polartwilight.png │ │ ├── cloudy.png │ │ ├── fair_day.png │ │ ├── fair_night.png │ │ ├── fair_polartwilight.png │ │ ├── fog.png │ │ ├── heavyrain.png │ │ ├── heavyrainandthunder.png │ │ ├── heavyrainshowers_day.png │ │ ├── heavyrainshowers_night.png │ │ ├── heavyrainshowers_polartwilight.png │ │ ├── heavyrainshowersandthunder_day.png │ │ ├── heavyrainshowersandthunder_night.png │ │ ├── heavyrainshowersandthunder_polartwilight.png │ │ ├── heavysleet.png │ │ ├── heavysleetandthunder.png │ │ ├── heavysleetshowers_day.png │ │ ├── heavysleetshowers_night.png │ │ ├── heavysleetshowers_polartwilight.png │ │ ├── heavysleetshowersandthunder_day.png │ │ ├── heavysleetshowersandthunder_night.png │ │ ├── heavysleetshowersandthunder_polartwilight.png │ │ ├── heavysnow.png │ │ ├── heavysnowandthunder.png │ │ ├── heavysnowshowers_day.png │ │ ├── heavysnowshowers_night.png │ │ ├── heavysnowshowers_polartwilight.png │ │ ├── heavysnowshowersandthunder_day.png │ │ ├── heavysnowshowersandthunder_night.png │ │ ├── heavysnowshowersandthunder_polartwilight.png │ │ ├── lightrain.png │ │ ├── lightrainandthunder.png │ │ ├── lightrainshowers_day.png │ │ ├── lightrainshowers_night.png │ │ ├── lightrainshowers_polartwilight.png │ │ ├── lightrainshowersandthunder_day.png │ │ ├── lightrainshowersandthunder_night.png │ │ ├── lightrainshowersandthunder_polartwilight.png │ │ ├── lightsleet.png │ │ ├── lightsleetandthunder.png │ │ ├── lightsleetshowers_day.png │ │ ├── lightsleetshowers_night.png │ │ ├── lightsleetshowers_polartwilight.png │ │ ├── lightsnow.png │ │ ├── lightsnowandthunder.png │ │ ├── lightsnowshowers_day.png │ │ ├── lightsnowshowers_night.png │ │ ├── lightsnowshowers_polartwilight.png │ │ ├── lightssleetshowersandthunder_day.png │ │ ├── lightssleetshowersandthunder_night.png │ │ ├── lightssleetshowersandthunder_polartwilight.png │ │ ├── lightssnowshowersandthunder_day.png │ │ ├── lightssnowshowersandthunder_night.png │ │ ├── lightssnowshowersandthunder_polartwilight.png │ │ ├── partlycloudy_day.png │ │ ├── partlycloudy_night.png │ │ ├── partlycloudy_polartwilight.png │ │ ├── rain.png │ │ ├── rainandthunder.png │ │ ├── rainshowers_day.png │ │ ├── rainshowers_night.png │ │ ├── rainshowers_polartwilight.png │ │ ├── rainshowersandthunder_day.png │ │ ├── rainshowersandthunder_night.png │ │ ├── rainshowersandthunder_polartwilight.png │ │ ├── sleet.png │ │ ├── sleetandthunder.png │ │ ├── sleetshowers_day.png │ │ ├── sleetshowers_night.png │ │ ├── sleetshowers_polartwilight.png │ │ ├── sleetshowersandthunder_day.png │ │ ├── sleetshowersandthunder_night.png │ │ ├── sleetshowersandthunder_polartwilight.png │ │ ├── snow.png │ │ ├── snowandthunder.png │ │ ├── snowshowers_day.png │ │ ├── snowshowers_night.png │ │ ├── snowshowers_polartwilight.png │ │ ├── snowshowersandthunder_day.png │ │ ├── snowshowersandthunder_night.png │ │ └── snowshowersandthunder_polartwilight.png ├── properties.xml └── strings.xml └── source ├── App.mc ├── BaseData.mc ├── FullData.mc ├── Resources.mc └── view ├── AuroraView.mc ├── BaseDelegate.mc ├── BaseView.mc ├── CelestialView.mc ├── DailyView.mc ├── GraphView.mc ├── HourlyView.mc ├── SummaryView.mc ├── UvView.mc ├── WaterView.mc └── special ├── AppGlanceView.mc ├── HintDelegate.mc ├── HintView.mc ├── RegisterDelegate.mc ├── RegisterView.mc ├── SettingsDelegate.mc └── SpellSearchDelegate.mc /.gitignore: -------------------------------------------------------------------------------- 1 | /bin -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Rainy (Garmin App)

2 | 3 | A weather app i made because i wanted to make something "simple" for my watch (And Garmins weather app kinda sucks). 4 | The app has more features, is more accurate and is prettier than Garmins weather app! 5 | 6 | App supports most (CIQ 3.0+) watches and a few Edge devices. 7 | Download on the [Connect IQ store](https://apps.garmin.com/en-US/apps/e7c0c39c-cb76-475f-b5bc-7811b0fee104). 8 | 9 | ## 2.x Branch 10 | 11 | This is the 2.x (App Version) version which is a "Device App" and has more available memory. 12 | This branch has more features than 1.x but drops support for Instinct watches. 13 | 14 | ### Showcase of pages 15 | 16 | 17 | ## Contributing 18 | 19 | 1. Follow Garmins SDK and VSCode Setup. 20 | 2. Select **SDK 7.1.1** and install all the neccesary watches in Garmins SDK Manager. 21 | 3. Clone and Import the project into VSCode. 22 | 4. Maybe it works?? ¯\\\_(ツ)_/¯ 23 | 5. Create a Pull Request. -------------------------------------------------------------------------------- /manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 121 | 122 | 123 | 124 | 125 | 129 | 130 | eng 131 | 132 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /monkey.jungle: -------------------------------------------------------------------------------- 1 | project.manifest = manifest.xml 2 | -------------------------------------------------------------------------------- /resources-rectangle/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 1 3 | 4 | -------------------------------------------------------------------------------- /resources-semiround: -------------------------------------------------------------------------------- 1 | resources-rectangle -------------------------------------------------------------------------------- /resources/drawables/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /resources/drawables/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/launcher_icon.png -------------------------------------------------------------------------------- /resources/drawables/weather/clearsky_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/clearsky_day.png -------------------------------------------------------------------------------- /resources/drawables/weather/clearsky_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/clearsky_night.png -------------------------------------------------------------------------------- /resources/drawables/weather/clearsky_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/clearsky_polartwilight.png -------------------------------------------------------------------------------- /resources/drawables/weather/cloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/cloudy.png -------------------------------------------------------------------------------- /resources/drawables/weather/fair_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/fair_day.png -------------------------------------------------------------------------------- /resources/drawables/weather/fair_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/fair_night.png -------------------------------------------------------------------------------- /resources/drawables/weather/fair_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/fair_polartwilight.png -------------------------------------------------------------------------------- /resources/drawables/weather/fog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/fog.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavyrain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavyrain.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavyrainandthunder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavyrainandthunder.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavyrainshowers_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavyrainshowers_day.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavyrainshowers_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavyrainshowers_night.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavyrainshowers_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavyrainshowers_polartwilight.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavyrainshowersandthunder_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavyrainshowersandthunder_day.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavyrainshowersandthunder_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavyrainshowersandthunder_night.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavyrainshowersandthunder_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavyrainshowersandthunder_polartwilight.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavysleet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavysleet.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavysleetandthunder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavysleetandthunder.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavysleetshowers_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavysleetshowers_day.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavysleetshowers_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavysleetshowers_night.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavysleetshowers_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavysleetshowers_polartwilight.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavysleetshowersandthunder_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavysleetshowersandthunder_day.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavysleetshowersandthunder_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavysleetshowersandthunder_night.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavysleetshowersandthunder_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavysleetshowersandthunder_polartwilight.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavysnow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavysnow.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavysnowandthunder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavysnowandthunder.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavysnowshowers_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavysnowshowers_day.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavysnowshowers_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavysnowshowers_night.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavysnowshowers_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavysnowshowers_polartwilight.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavysnowshowersandthunder_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavysnowshowersandthunder_day.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavysnowshowersandthunder_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavysnowshowersandthunder_night.png -------------------------------------------------------------------------------- /resources/drawables/weather/heavysnowshowersandthunder_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/heavysnowshowersandthunder_polartwilight.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightrain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightrain.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightrainandthunder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightrainandthunder.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightrainshowers_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightrainshowers_day.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightrainshowers_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightrainshowers_night.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightrainshowers_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightrainshowers_polartwilight.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightrainshowersandthunder_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightrainshowersandthunder_day.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightrainshowersandthunder_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightrainshowersandthunder_night.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightrainshowersandthunder_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightrainshowersandthunder_polartwilight.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightsleet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightsleet.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightsleetandthunder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightsleetandthunder.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightsleetshowers_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightsleetshowers_day.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightsleetshowers_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightsleetshowers_night.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightsleetshowers_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightsleetshowers_polartwilight.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightsnow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightsnow.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightsnowandthunder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightsnowandthunder.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightsnowshowers_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightsnowshowers_day.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightsnowshowers_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightsnowshowers_night.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightsnowshowers_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightsnowshowers_polartwilight.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightssleetshowersandthunder_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightssleetshowersandthunder_day.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightssleetshowersandthunder_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightssleetshowersandthunder_night.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightssleetshowersandthunder_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightssleetshowersandthunder_polartwilight.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightssnowshowersandthunder_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightssnowshowersandthunder_day.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightssnowshowersandthunder_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightssnowshowersandthunder_night.png -------------------------------------------------------------------------------- /resources/drawables/weather/lightssnowshowersandthunder_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/lightssnowshowersandthunder_polartwilight.png -------------------------------------------------------------------------------- /resources/drawables/weather/partlycloudy_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/partlycloudy_day.png -------------------------------------------------------------------------------- /resources/drawables/weather/partlycloudy_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/partlycloudy_night.png -------------------------------------------------------------------------------- /resources/drawables/weather/partlycloudy_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/partlycloudy_polartwilight.png -------------------------------------------------------------------------------- /resources/drawables/weather/rain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/rain.png -------------------------------------------------------------------------------- /resources/drawables/weather/rainandthunder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/rainandthunder.png -------------------------------------------------------------------------------- /resources/drawables/weather/rainshowers_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/rainshowers_day.png -------------------------------------------------------------------------------- /resources/drawables/weather/rainshowers_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/rainshowers_night.png -------------------------------------------------------------------------------- /resources/drawables/weather/rainshowers_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/rainshowers_polartwilight.png -------------------------------------------------------------------------------- /resources/drawables/weather/rainshowersandthunder_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/rainshowersandthunder_day.png -------------------------------------------------------------------------------- /resources/drawables/weather/rainshowersandthunder_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/rainshowersandthunder_night.png -------------------------------------------------------------------------------- /resources/drawables/weather/rainshowersandthunder_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/rainshowersandthunder_polartwilight.png -------------------------------------------------------------------------------- /resources/drawables/weather/sleet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/sleet.png -------------------------------------------------------------------------------- /resources/drawables/weather/sleetandthunder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/sleetandthunder.png -------------------------------------------------------------------------------- /resources/drawables/weather/sleetshowers_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/sleetshowers_day.png -------------------------------------------------------------------------------- /resources/drawables/weather/sleetshowers_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/sleetshowers_night.png -------------------------------------------------------------------------------- /resources/drawables/weather/sleetshowers_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/sleetshowers_polartwilight.png -------------------------------------------------------------------------------- /resources/drawables/weather/sleetshowersandthunder_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/sleetshowersandthunder_day.png -------------------------------------------------------------------------------- /resources/drawables/weather/sleetshowersandthunder_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/sleetshowersandthunder_night.png -------------------------------------------------------------------------------- /resources/drawables/weather/sleetshowersandthunder_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/sleetshowersandthunder_polartwilight.png -------------------------------------------------------------------------------- /resources/drawables/weather/snow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/snow.png -------------------------------------------------------------------------------- /resources/drawables/weather/snowandthunder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/snowandthunder.png -------------------------------------------------------------------------------- /resources/drawables/weather/snowshowers_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/snowshowers_day.png -------------------------------------------------------------------------------- /resources/drawables/weather/snowshowers_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/snowshowers_night.png -------------------------------------------------------------------------------- /resources/drawables/weather/snowshowers_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/snowshowers_polartwilight.png -------------------------------------------------------------------------------- /resources/drawables/weather/snowshowersandthunder_day.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/snowshowersandthunder_day.png -------------------------------------------------------------------------------- /resources/drawables/weather/snowshowersandthunder_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/snowshowersandthunder_night.png -------------------------------------------------------------------------------- /resources/drawables/weather/snowshowersandthunder_polartwilight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BleachDev/Rainy/de79034a7854b03838d53cb649a7971571190533/resources/drawables/weather/snowshowersandthunder_polartwilight.png -------------------------------------------------------------------------------- /resources/properties.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | true 5 | 6 | false 7 | 0 8 | 0 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Celsius 24 | Fahrenheit 25 | 26 | 27 | 28 | 29 | m/s 30 | km/h 31 | mph 32 | Bft. 33 | Knots 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /resources/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Rainy Pro 3 | 0 4 | 5 | -------------------------------------------------------------------------------- /source/App.mc: -------------------------------------------------------------------------------- 1 | import Toybox.Application; 2 | import Toybox.Lang; 3 | import Toybox.Math; 4 | import Toybox.System; 5 | import Toybox.WatchUi; 6 | 7 | public var data as FullData?; 8 | public var res as Resources?; 9 | (:glance) public var VERSION = "2.2.3"; 10 | (:glance) public var IS_GLANCE as Boolean = false; 11 | public var SQUARE_MODE as Boolean = false; // Whether the watch is rectangle/semiround 12 | 13 | (:glance) 14 | class App extends Application.AppBase { 15 | 16 | function initialize() { 17 | AppBase.initialize(); 18 | } 19 | 20 | // onStart() is called on application start up 21 | function onStart(state as Dictionary?) as Void { 22 | } 23 | 24 | // onStop() is called when your application is exiting 25 | function onStop(state as Dictionary?) as Void { 26 | Position.enableLocationEvents(Position.LOCATION_DISABLE, null); 27 | } 28 | 29 | // Return the initial view of your application here 30 | function getInitialView() as [ WatchUi.Views ] or [ WatchUi.Views, WatchUi.InputDelegates ] { 31 | SQUARE_MODE = "1".equals(WatchUi.loadResource(Rez.Strings.SQUARE_MODE)); 32 | 33 | res = new Resources(); 34 | data = new FullData(); 35 | data.load(); 36 | 37 | return [ new SummaryView(), new BaseDelegate() ]; 38 | } 39 | 40 | function getGlanceView() as [ WatchUi.GlanceView ] or [ WatchUi.GlanceView, WatchUi.GlanceViewDelegate ] or Null { 41 | IS_GLANCE = true; 42 | 43 | var data = new BaseData(); 44 | data.load(); 45 | 46 | return [ new AppGlanceView(data) ]; 47 | } 48 | } 49 | 50 | // This definetely shouldn't be here but thats a later problem 51 | (:glance) 52 | function generateArrow(centerPoint as [Float, Float], angle as Float, length as Number) as Array { 53 | // Map out the coordinates of the arrow 54 | var coords = [[0, length / 2] as Array, 55 | [(length * 0.07).toNumber(), (-length / 2 * 0.5).toNumber()] as Array, 56 | [(length * 0.3).toNumber(), (-length / 2 * 0.3).toNumber()] as Array, 57 | [0, -length / 2] as Array, 58 | [-(length * 0.3).toNumber(), (-length / 2 * 0.3).toNumber()] as Array, 59 | [-(length * 0.07).toNumber(), (-length / 2 * 0.5).toNumber()] as Array] as Array>; 60 | var result = new Array[coords.size()]; 61 | var rad = Toybox.Math.toRadians(angle); 62 | var cos = Toybox.Math.cos(rad); 63 | var sin = Toybox.Math.sin(rad); 64 | 65 | // Transform the coordinates 66 | for (var i = 0; i < coords.size(); i++) { 67 | var x = (coords[i][0] * cos) - (coords[i][1] * sin); 68 | var y = (coords[i][0] * sin) + (coords[i][1] * cos); 69 | 70 | result[i] = [centerPoint[0] + x + length / 2, centerPoint[1] + y + length / 2]; 71 | } 72 | 73 | return result; 74 | } 75 | 76 | (:glance) 77 | function degrees(c as Float, unit as Number) { 78 | return (unit == 0 ? c : c * (9.0/5.0) + 32).toNumber(); 79 | } 80 | 81 | (:glance) 82 | function wind(ms as Float, unit as Number) { 83 | return (unit == 0 ? ms : unit == 1 ? ms * 3.6 : unit == 2 ? ms * 2.237 : unit == 3 ? Math.round(Math.pow(ms / 0.836, 2.0 / 3)) : ms * 1.9438).toNumber(); 84 | } -------------------------------------------------------------------------------- /source/BaseData.mc: -------------------------------------------------------------------------------- 1 | import Toybox.Communications; 2 | import Toybox.Position; 3 | import Toybox.Lang; 4 | import Toybox.Application; 5 | import Toybox.Time; 6 | 7 | (:glance) 8 | class BaseData { 9 | 10 | public var autoLocation as Boolean = true; 11 | public var tempUnits as Number = System.getDeviceSettings().temperatureUnits; // 0 = C, 1 = F 12 | public var windUnits as Number = 0; // 0 = m/s, 1 = km/h, 2 = mph, 3 = bft., 4 = knots 13 | 14 | public var position as Array?; 15 | public var location as String = ".."; 16 | public var time as Moment = Time.now(); 17 | // Forecast 18 | public var nowRainfall as Array?; // Null (If unavailable) or empty (If outdated) or list of next 90 mins of rain 19 | public var temperatures as Array = []; 20 | public var windSpeeds as Array = []; 21 | public var windDirections as Array = []; 22 | public var rainfall as Array = []; 23 | public var humidity as Array = []; 24 | public var symbols as Array = []; 25 | public var hours as Number = 0; 26 | 27 | function update(coords as Array) as Void { 28 | System.println("Refreshing, " + coords); 29 | if (coords == null) { 30 | return; 31 | } 32 | 33 | position = coords; 34 | Storage.setValue("geo", position); 35 | 36 | var hours = IS_GLANCE ? 6 : 48; 37 | var days = IS_GLANCE ? 0 : 22; 38 | request("https://api.bleach.dev/weather/forecast?hourly=" + hours + "&daily=" + days + "&lat=" + position[0] + "&lon=" + position[1], method(:fetchForecastData)); 39 | request("https://api.bleach.dev/weather/search?limit=1&lat=" + position[0] + "&lon=" + position[1], method(:fetchGeoData)); 40 | } 41 | 42 | // Request order 43 | // -> Forecast 44 | // -> Geo ----> (F) Celestial ---> (F) Aurora 45 | // |---> (F) License 46 | // \---> (F) Water 47 | function request(url, callback) { 48 | Communications.makeWebRequest(url, null, { 49 | :method => Communications.HTTP_REQUEST_METHOD_GET, 50 | :headers => { "User-Agent" => "GarminYr/" + VERSION + " me@bleach.dev" }, 51 | :responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON 52 | }, callback); 53 | } 54 | 55 | function load() { 56 | if (Properties.getValue("autoLocation") != null) { autoLocation = Properties.getValue("autoLocation"); } 57 | if (Properties.getValue("tempUnits") != null) { tempUnits = Properties.getValue("tempUnits"); } 58 | if (Properties.getValue("windUnits") != null) { windUnits = Properties.getValue("windUnits"); } 59 | 60 | if (Storage.getValue("geo") != null) { position = Storage.getValue("geo"); } 61 | if (Storage.getValue("location") != null) { location = Storage.getValue("location"); } 62 | if (Storage.getValue("time") != null) { time = new Moment(Storage.getValue("time")); } 63 | 64 | if (Storage.getValue("nowRainfall") != null) { nowRainfall = Storage.getValue("nowRainfall"); } 65 | if (Storage.getValue("temperatures") != null) { temperatures = Storage.getValue("temperatures"); } 66 | if (Storage.getValue("windSpeeds") != null) { windSpeeds = Storage.getValue("windSpeeds"); } 67 | if (Storage.getValue("windDirections") != null) { windDirections = Storage.getValue("windDirections"); } 68 | if (Storage.getValue("rainfall") != null) { rainfall = Storage.getValue("rainfall"); } 69 | if (Storage.getValue("humidity") != null) { humidity = Storage.getValue("humidity"); } 70 | if (Storage.getValue("symbols") != null) { symbols = Storage.getValue("symbols"); } 71 | if (Storage.getValue("hours") != null) { hours = Storage.getValue("hours"); } 72 | 73 | // Sync Data 74 | var now = Time.now().value(); 75 | for (var i = time.value(); i + 3600 < now; i += 3600) { 76 | removeHour(); 77 | } 78 | 79 | WatchUi.requestUpdate(); 80 | 81 | // Update if we have a valid position 82 | if (!autoLocation && Properties.getValue("manLocation").length() > 0) { 83 | return; 84 | } 85 | 86 | var pos = Position.getInfo().position.toDegrees(); 87 | if (autoLocation && pos[0] > -90 && pos[0] < 90 && pos[1] > -180 && pos[1] < 180) { 88 | update(pos); 89 | } else if (autoLocation && Activity.getActivityInfo() != null && Activity.getActivityInfo().currentLocation != null) { 90 | update(Activity.getActivityInfo().currentLocation.toDegrees()); 91 | } else if (autoLocation && Toybox has :Weather 92 | && Weather.getCurrentConditions() != null && Weather.getCurrentConditions().observationLocationPosition != null) { 93 | update(Weather.getCurrentConditions().observationLocationPosition.toDegrees()); 94 | } else if (position != null) { 95 | update(position); 96 | } 97 | } 98 | 99 | function removeHour() { 100 | if (nowRainfall != null) { 101 | nowRainfall = []; // No more 90 minute rain 102 | } 103 | if (temperatures.size() > 0) { temperatures.remove(temperatures[0]); } 104 | if (windSpeeds.size() > 0) { windSpeeds.remove(windSpeeds[0]); } 105 | if (windDirections.size() > 0) { windDirections.remove(windDirections[0]); } 106 | if (rainfall.size() > 0) { rainfall.remove(rainfall[0]); } 107 | if (humidity.size() > 0) { humidity.remove(humidity[0]); } 108 | if (symbols.size() > 0) { symbols.remove(symbols[0]); } 109 | hours--; 110 | } 111 | 112 | // Fetching Methods 113 | 114 | function fetchForecastData(responseCode as Number, data as Dictionary?) as Boolean { 115 | System.println("FC " + responseCode); 116 | if (responseCode != 200 || data == null || data["forecast"] == null) { 117 | return false; 118 | } 119 | 120 | time = Time.now(); 121 | hours = data["hourly"]; 122 | 123 | var forecastData = data["forecast"] as Dictionary; 124 | 125 | var size = forecastData.size(); 126 | temperatures = new [size]; 127 | windSpeeds = new [size]; 128 | windDirections = new [size]; 129 | rainfall = new [size]; 130 | humidity = new [size]; 131 | symbols = new [size]; 132 | for (var i = 0; i < size; i++) { 133 | var hour = forecastData[i]; 134 | temperatures[i] = hour["air_temperature"]; 135 | windSpeeds[i] = hour["wind_speed"]; 136 | windDirections[i] = hour["wind_from_direction"]; 137 | rainfall[i] = hour["precipitation_amount"]; 138 | humidity[i] = hour["relative_humidity"]; 139 | symbols[i] = hour["symbol_code"].hashCode(); 140 | } 141 | 142 | if (data["nowcast"] != null) { 143 | var nowData = data["nowcast"] as Dictionary; 144 | nowRainfall = new [nowData.size() < 19 ? nowData.size() : 19]; 145 | for (var i = 0; i < nowRainfall.size(); i++) { 146 | nowRainfall[i] = nowData[i]; 147 | } 148 | } 149 | 150 | WatchUi.requestUpdate(); 151 | if (!IS_GLANCE) { 152 | Storage.setValue("time", time.value()); 153 | Storage.setValue("hours", hours); 154 | 155 | Storage.setValue("nowRainfall", nowRainfall); 156 | Storage.setValue("temperatures", temperatures); 157 | Storage.setValue("windSpeeds", windSpeeds); 158 | Storage.setValue("windDirections", windDirections); 159 | Storage.setValue("rainfall", rainfall); 160 | Storage.setValue("humidity", humidity); 161 | Storage.setValue("symbols", symbols); 162 | } 163 | return true; 164 | } 165 | 166 | function fetchGeoData(responseCode as Number, data as Dictionary?) as Boolean { 167 | System.println("GEO " + responseCode); 168 | if (responseCode != 200 || data == null || data.size() == 0) { 169 | System.println("GEO EXIT " + data); 170 | return false; 171 | } 172 | 173 | location = data[0]["name"]; 174 | Storage.setValue("location", location); 175 | 176 | if (IS_GLANCE) { 177 | WatchUi.requestUpdate(); 178 | } 179 | return true; 180 | } 181 | } -------------------------------------------------------------------------------- /source/FullData.mc: -------------------------------------------------------------------------------- 1 | import Toybox.Communications; 2 | import Toybox.Position; 3 | import Toybox.Lang; 4 | import Toybox.Application; 5 | import Toybox.Time; 6 | import Toybox.WatchUi; 7 | 8 | class FullData extends BaseData { 9 | 10 | public var DEVICE_ID = uniqueId(); 11 | public var blocked = false; 12 | 13 | public var hints as Number = 0; 14 | public var pageOrder as Boolean = false; 15 | // Forecast 16 | public var maxRainfall as Array = []; 17 | // Aurora 18 | public var hourlyAurora as Array = []; 19 | public var hourlyClouds as Array = []; 20 | // Uv 21 | public var uv as Array = []; 22 | // Celestial 23 | public var sunLength as Number = 0; 24 | public var sunDifference as Number = 0; 25 | public var sunRise as Number = 0; 26 | public var sunMax as Number = 0; 27 | public var sunSet as Number = 0; 28 | public var sunElevation as Array = []; 29 | public var sunNextEclipse as Number = 0; 30 | public var sunEclipseObsc as Float = 0.0; 31 | public var moonNextNew as Number = 0; 32 | public var moonNextFull as Number = 0; 33 | public var moonIllumination as Number = 0; 34 | public var moonPhase as String = ""; 35 | // Water 36 | public var waterNames as Array = []; 37 | public var waterTemperatures as Array = []; 38 | public var waterDistances as Array = []; 39 | public var waterTimestamps as Array = []; 40 | 41 | function initialize() { 42 | BaseData.initialize(); 43 | } 44 | 45 | // Callback function for Position.enableLocationEvents 46 | function posCB(loc as Position.Info) as Void { 47 | if (loc.position != null) { 48 | update(loc.position.toDegrees()); 49 | } 50 | } 51 | 52 | function load() { 53 | if (Storage.getValue("hints") != null) { hints = Storage.getValue("hints"); } 54 | 55 | if (Properties.getValue("pageOrder") != null) { pageOrder = Properties.getValue("pageOrder"); } 56 | 57 | if (Storage.getValue("maxRainfall") != null) { maxRainfall = Storage.getValue("maxRainfall"); } 58 | 59 | if (Storage.getValue("hourlyAurora") != null) { hourlyAurora = Storage.getValue("hourlyAurora"); } 60 | if (Storage.getValue("hourlyClouds") != null) { hourlyClouds = Storage.getValue("hourlyClouds"); } 61 | 62 | if (Storage.getValue("uv") != null) { uv = Storage.getValue("uv"); } 63 | 64 | if (Storage.getValue("sunLength") != null) { sunLength = Storage.getValue("sunLength"); } 65 | if (Storage.getValue("sunDifference") != null) { sunDifference = Storage.getValue("sunDifference"); } 66 | if (Storage.getValue("sunRise") != null) { sunRise = Storage.getValue("sunRise"); } 67 | if (Storage.getValue("sunMax") != null) { sunMax = Storage.getValue("sunMax"); } 68 | if (Storage.getValue("sunSet") != null) { sunSet = Storage.getValue("sunSet"); } 69 | if (Storage.getValue("sunElevation") != null) { sunElevation = Storage.getValue("sunElevation"); } 70 | if (Storage.getValue("sunNextEclipse") != null) { sunNextEclipse = Storage.getValue("sunNextEclipse"); } 71 | if (Storage.getValue("sunEclipseObsc") != null) { sunEclipseObsc = Storage.getValue("sunEclipseObsc"); } 72 | if (Storage.getValue("moonNextNew") != null) { moonNextNew = Storage.getValue("moonNextNew"); } 73 | if (Storage.getValue("moonNextFull") != null) { moonNextFull = Storage.getValue("moonNextFull"); } 74 | if (Storage.getValue("moonIllumination") != null) { moonIllumination = Storage.getValue("moonIllumination"); } 75 | if (Storage.getValue("moonPhase") != null) { moonPhase = Storage.getValue("moonPhase"); } 76 | 77 | if (Storage.getValue("waterNames") != null) { waterNames = Storage.getValue("waterNames"); } 78 | if (Storage.getValue("waterTemperatures") != null) { waterTemperatures = Storage.getValue("waterTemperatures"); } 79 | if (Storage.getValue("waterDistances") != null) { waterDistances = Storage.getValue("waterDistances"); } 80 | if (Storage.getValue("waterTimestamps") != null) { waterTimestamps = Storage.getValue("waterTimestamps"); } 81 | 82 | BaseData.load(); 83 | 84 | Position.enableLocationEvents(Position.LOCATION_ONE_SHOT, method(:posCB)); 85 | } 86 | 87 | function removeHour() { 88 | BaseData.removeHour(); 89 | 90 | if (maxRainfall.size() > 0) { maxRainfall.remove(maxRainfall[0]); } 91 | if (hourlyAurora.size() > 0) { hourlyAurora.remove(hourlyAurora[0]); } 92 | if (hourlyClouds.size() > 0) { hourlyClouds.remove(hourlyClouds[0]); } 93 | if (uv.size() > 0) { uv.remove(uv[0]); } 94 | } 95 | 96 | // Fetching Methods 97 | 98 | function fetchForecastData(responseCode as Number, data as Dictionary?) as Boolean { 99 | if (!BaseData.fetchForecastData(responseCode, data)) { 100 | return false; 101 | } 102 | 103 | var forecastData = data["forecast"] as Dictionary; 104 | 105 | var hours = forecastData.size(); 106 | maxRainfall = new [hours]; 107 | hourlyClouds = new [hours]; 108 | uv = new [hours]; 109 | for (var i = 0; i < hours; i++) { 110 | var hour = forecastData[i]; 111 | maxRainfall[i] = hour["precipitation_max"] != null ? hour["precipitation_max"] : rainfall[i]; 112 | hourlyClouds[i] = hour["cloud_area_fraction"]; 113 | uv[i] = hour["uv"]; 114 | } 115 | 116 | Storage.setValue("maxRainfall", maxRainfall); 117 | Storage.setValue("hourlyClouds", hourlyClouds); 118 | Storage.setValue("uv", uv); 119 | return true; 120 | } 121 | 122 | function fetchGeoData(responseCode as Number, data as Dictionary?) as Boolean { 123 | if (!BaseData.fetchGeoData(responseCode, data)) { 124 | return false; 125 | } 126 | 127 | request("https://api.bleach.dev/rainy/license?id=" + DEVICE_ID, method(:fetchLicense)); 128 | 129 | var showWater = "NO".equals(data[0]["code"]); 130 | BaseDelegate.pageCount = showWater ? 8 : 7; 131 | 132 | request("https://api.bleach.dev/weather/celestial?lat=" + position[0] + "&lon=" + position[1], method(:fetchCelestialData)); 133 | if (showWater) { 134 | request("https://api.bleach.dev/weather/water?lat=" + position[0] + "&lon=" + position[1], method(:fetchWaterData)); 135 | } 136 | return true; 137 | } 138 | 139 | function fetchAuroraData(responseCode as Number, data as Dictionary?) as Void { 140 | System.println("AUR " + responseCode); 141 | if (responseCode != 200 || data == null) { 142 | System.println("AUR EXIT " + data); 143 | return; 144 | } 145 | 146 | hourlyAurora = new [data["aurora"].size()]; 147 | for (var i = 0; i < data["aurora"].size(); i++) { 148 | hourlyAurora[i] = data["aurora"][i]["activity"]; 149 | } 150 | 151 | Storage.setValue("hourlyAurora", hourlyAurora); 152 | WatchUi.requestUpdate(); 153 | } 154 | 155 | function fetchCelestialData(responseCode as Number, data as Dictionary?) as Void { 156 | System.println("CST " + responseCode); 157 | if (responseCode != 200 || data == null) { 158 | System.println("CST EXIT " + data); 159 | return; 160 | } 161 | 162 | sunLength = data["sun"]["day_length"]; 163 | sunDifference = data["sun"]["difference"]; 164 | sunRise = data["sun"]["rise"]; 165 | sunMax = data["sun"]["max"]; 166 | sunSet = data["sun"]["set"]; 167 | sunElevation = data["sun"]["elevation"]; 168 | sunNextEclipse = data["sun"]["next_eclipse_time"]; 169 | sunEclipseObsc = data["sun"]["next_eclipse_obscuration"]; 170 | moonNextNew = data["moon"]["next_new"]; 171 | moonNextFull = data["moon"]["next_full"]; 172 | moonIllumination = data["moon"]["illumination_percent"]; 173 | moonPhase = data["moon"]["phase"]; 174 | 175 | Storage.setValue("sunLength", sunLength); 176 | Storage.setValue("sunDifference", sunDifference); 177 | Storage.setValue("sunRise", sunRise); 178 | Storage.setValue("sunMax", sunMax); 179 | Storage.setValue("sunSet", sunSet); 180 | Storage.setValue("sunElevation", sunElevation); 181 | Storage.setValue("sunNextEclipse", sunNextEclipse); 182 | Storage.setValue("sunEclipseObsc", sunEclipseObsc); 183 | Storage.setValue("moonNextNew", moonNextNew); 184 | Storage.setValue("moonNextFull", moonNextFull); 185 | Storage.setValue("moonIllumination", moonIllumination); 186 | Storage.setValue("moonPhase", moonPhase); 187 | WatchUi.requestUpdate(); 188 | 189 | request("https://api.bleach.dev/weather/aurora?noclouds&limit=32&lat=" + position[0] + "&lon=" + position[1], method(:fetchAuroraData)); 190 | } 191 | 192 | function fetchWaterData(responseCode as Number, data as Dictionary?) as Void { 193 | System.println("WTR " + responseCode); 194 | if (responseCode != 200 || data == null) { 195 | System.println("WTR EXIT " + data); 196 | return; 197 | } 198 | 199 | var len = data["water"].size(); 200 | waterNames = new [len]; 201 | waterTemperatures = new [len]; 202 | waterDistances = new [len]; 203 | waterTimestamps = new [len]; 204 | for (var i = 0; i < len; i++) { 205 | var waterData = data["water"][i]; 206 | waterNames[i] = waterData["name"]; 207 | waterTemperatures[i] = waterData["temperature"]; 208 | waterDistances[i] = waterData["distance"]; 209 | waterTimestamps[i] = waterData["time"]; 210 | } 211 | 212 | Storage.setValue("waterNames", waterNames); 213 | Storage.setValue("waterTemperatures", waterTemperatures); 214 | Storage.setValue("waterDistances", waterDistances); 215 | Storage.setValue("waterTimestamps", waterTimestamps); 216 | WatchUi.requestUpdate(); 217 | } 218 | 219 | function fetchLicense(responseCode as Number, data as Dictionary?) as Void { 220 | System.println("LIC " + responseCode); 221 | if (responseCode == 200 && data != null) { 222 | if (data["demo"]) { 223 | blocked = data["demo"] == 0; 224 | WatchUi.pushView(new RegisterView(data["demo"]), new RegisterDelegate(), WatchUi.SLIDE_BLINK); 225 | } 226 | } 227 | } 228 | 229 | // Helper Methods 230 | 231 | function hourlyEntries() { 232 | return hours < symbols.size() ? hours : symbols.size(); 233 | } 234 | 235 | function uniqueId() { 236 | var alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); 237 | var deviceId = System.getDeviceSettings().uniqueIdentifier.toUpper().toCharArray(); 238 | var rainyId = ""; 239 | for (var i = 0; i < 5; i++) { 240 | rainyId = rainyId + alphabet[alphabet.indexOf(deviceId[i * 2]) + alphabet.indexOf(deviceId[i * 2 + 1])]; 241 | } 242 | return rainyId; 243 | } 244 | 245 | function enterText(text) { 246 | var menu = new Menu2({:title => "Location"}); 247 | menu.addItem(new MenuItem("Loading Locations..", "Please Wait :)", 0, {})); 248 | 249 | WatchUi.pushView(menu, new Menu2InputDelegate(), WatchUi.SLIDE_IMMEDIATE); 250 | data.request("https://api.bleach.dev/weather/search?q=" + text, method(:fetchSearch)); 251 | } 252 | 253 | function fetchSearch(responseCode as Number, data as Dictionary?) as Boolean { 254 | if (responseCode != 200 || data == null) { 255 | var menu = new Menu2({:title => "Location"}); 256 | menu.addItem(new MenuItem("Error", "Connection Error", 0, {})); 257 | WatchUi.switchToView(menu, new Menu2InputDelegate(), WatchUi.SLIDE_BLINK); 258 | return false; 259 | } 260 | 261 | var menu = new Menu2({:title => "Location" }); 262 | for (var i = 0; i < data.size(); i++) { 263 | menu.addItem(new MenuItem(data[i]["name"], data[i]["region"] + " (" + data[i]["code"] + "), El. " + data[i]["elevation"] + "m", i, {})); 264 | } 265 | 266 | WatchUi.switchToView(menu, new SpellListDelegate(data), WatchUi.SLIDE_BLINK); 267 | return true; 268 | } 269 | } -------------------------------------------------------------------------------- /source/Resources.mc: -------------------------------------------------------------------------------- 1 | import Toybox.Application; 2 | import Toybox.Lang; 3 | import Toybox.WatchUi; 4 | 5 | class Resources { 6 | 7 | // The micro-optimizations are real 8 | private var resources as Dictionary = {}; 9 | private var resourceNames as Dictionary = { 10 | -1331507130 => :clearsky_day, 11 | -1535495851 => :clearsky_night, 12 | -1219673323 => :clearsky_polartwilight, 13 | 2118327279 => :cloudy, 14 | 2018941991 => :fair_day, 15 | 1971704425 => :fair_night, 16 | 223183045 => :fair_polartwilight, 17 | 104671553 => :fog, 18 | 44038243 => :heavyrain, 19 | 340693381 => :heavyrainandthunder, 20 | -838448405 => :heavyrainshowers_day, 21 | 530501279 => :heavyrainshowers_night, 22 | -679099218 => :heavyrainshowers_polartwilight, 23 | 275421535 => :heavyrainshowersandthunder_day, 24 | 608654722 => :heavyrainshowersandthunder_night, 25 | 411129877 => :heavyrainshowersandthunder_polartwilight, 26 | -2083185603 => :heavysleet, 27 | 737898598 => :heavysleetandthunder, 28 | 843467800 => :heavysleetshowers_day, 29 | -1951529380 => :heavysleetshowers_night, 30 | 693091304 => :heavysleetshowers_polartwilight, 31 | 1781829784 => :heavysleetshowersandthunder_day, 32 | 527239930 => :heavysleetshowersandthunder_night, 33 | 319532770 => :heavysleetshowersandthunder_polartwilight, 34 | 1079366920 => :heavysnow, 35 | 317439734 => :heavysnowandthunder, 36 | -1876409313 => :heavysnowshowers_day, 37 | 1264317029 => :heavysnowshowers_night, 38 | -2055361185 => :heavysnowshowers_polartwilight, 39 | -966622704 => :heavysnowshowersandthunder_day, 40 | -235523705 => :heavysnowshowersandthunder_night, 41 | 1868186092 => :heavysnowshowersandthunder_polartwilight, 42 | 116707690 => :lightrain, 43 | 726025730 => :lightrainandthunder, 44 | -2001193696 => :lightrainshowers_day, 45 | -503752469 => :lightrainshowers_night, 46 | 2020764452 => :lightrainshowers_polartwilight, 47 | -1185464364 => :lightrainshowersandthunder_day, 48 | -250495803 => :lightrainshowersandthunder_night, 49 | -2108073736 => :lightrainshowersandthunder_polartwilight, 50 | -1438230832 => :lightsleet, 51 | -290628965 => :lightsleetandthunder, 52 | 2032889179 => :lightsleetshowers_day, 53 | -2041929173 => :lightsleetshowers_night, 54 | 452199269 => :lightsleetshowers_polartwilight, 55 | 1017818640 => :lightsnow, 56 | 702772083 => :lightsnowandthunder, 57 | 1255812693 => :lightsnowshowers_day, 58 | 95845553 => :lightsnowshowers_night, 59 | 644502485 => :lightsnowshowers_polartwilight, 60 | -150750682 => :lightssleetshowersandthunder_day, 61 | 119919348 => :lightssleetshowersandthunder_night, 62 | -763981242 => :lightssleetshowersandthunder_polartwilight, 63 | 962854891 => :lightssnowshowersandthunder_day, 64 | -1262287275 => :lightssnowshowersandthunder_night, 65 | 10936797 => :lightssnowshowersandthunder_polartwilight, 66 | 822101396 => :partlycloudy_day, 67 | 1887971799 => :partlycloudy_night, 68 | 413412525 => :partlycloudy_polartwilight, 69 | 1448337921 => :rain, 70 | 1451666232 => :rainandthunder, 71 | -494771989 => :rainshowers_day, 72 | -496560659 => :rainshowers_night, 73 | 1779947666 => :rainshowers_polartwilight, 74 | -1426281150 => :rainshowersandthunder_day, 75 | 866900721 => :rainshowersandthunder_night, 76 | 1206449457 => :rainshowersandthunder_polartwilight, 77 | -1618873504 => :sleet, 78 | 1081575014 => :sleetandthunder, 79 | -1853359748 => :sleetshowers_day, 80 | 564902271 => :sleetshowers_night, 81 | -331927723 => :sleetshowers_polartwilight, 82 | 622593029 => :sleetshowersandthunder_day, 83 | 1105769884 => :sleetshowersandthunder_night, 84 | 429035076 => :sleetshowersandthunder_polartwilight, 85 | -1811300697 => :snow, 86 | 1428412585 => :snowandthunder, 87 | -1532732897 => :snowshowers_day, 88 | 103037363 => :snowshowers_night, 89 | 403685698 => :snowshowers_polartwilight, 90 | 1492424179 => :snowshowersandthunder_day, 91 | 22722294 => :snowshowersandthunder_night, 92 | -1631461623 => :snowshowersandthunder_polartwilight, 93 | 0 => :LauncherIcon 94 | }; 95 | 96 | function getSymbol(code as Number) { 97 | if (resources[code] != null) { 98 | return resources[code]; 99 | } 100 | 101 | if (resourceNames[code] == null) { 102 | System.println("! " + code); 103 | if (resources[/*"clearsky_day"*/ -1331507130] != null) { 104 | return resources[/*"clearsky_day"*/ -1331507130]; 105 | } 106 | code = -1331507130; 107 | } 108 | 109 | System.println("Loading Res: " + code); 110 | resources[code] = WatchUi.loadResource(Rez.Drawables[resourceNames[code]]) as BitmapResource; 111 | resourceNames.remove(code); 112 | return resources[code]; 113 | } 114 | } -------------------------------------------------------------------------------- /source/view/AuroraView.mc: -------------------------------------------------------------------------------- 1 | import Toybox.Graphics; 2 | import Toybox.WatchUi; 3 | import Toybox.Lang; 4 | 5 | class AuroraView extends BaseView { 6 | 7 | function initialize() { 8 | BaseView.initialize(1); 9 | } 10 | 11 | // Update the view 12 | function onDraw(dc as Dc, W as Number, H as Number, FONT_HEIGHT as Number) as Void { 13 | drawHeader(dc, W, H, "Aurora"); 14 | if (data.hourlyClouds.size() == 0) { 15 | dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT); 16 | dc.drawText(W / 2, H / 2.5, Graphics.FONT_TINY, "Aurora & Cloud Data\nUnavailable.", Graphics.TEXT_JUSTIFY_CENTER); 17 | } else { 18 | var mw = W * 0.15; // Margin Width 19 | var mh = H * 0.23; // Margin Height 20 | var cw = W - mw * 2.0; // Chart Width 21 | var ch = H - mh * 2.0; // Chart Height 22 | 23 | // Aurora guidelines 24 | dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT); 25 | dc.drawLine(mw, mh + ch - ch * 0.4 * 0.15, mw + cw, mh + ch - ch * 0.4 * 0.15); 26 | dc.drawLine(mw, mh + ch - ch * 0.4 * 0.5, mw + cw, mh + ch - ch * 0.4 * 0.5); 27 | dc.drawLine(mw, mh + ch - ch * 0.4, mw + cw, mh + ch - ch * 0.4); 28 | dc.drawText(mw + cw + 3, mh + ch - ch * 0.4 * 0.15 - H / 26, Graphics.FONT_XTINY, "Lo", Graphics.TEXT_JUSTIFY_LEFT); 29 | dc.drawText(mw + cw + 3, mh + ch - ch * 0.4 * 0.5 - H / 26, Graphics.FONT_XTINY, "Hi", Graphics.TEXT_JUSTIFY_LEFT); 30 | 31 | var len = data.hourlyEntries() > 32 ? 32 : data.hourlyEntries(); 32 | var cloudPoints = new [len * 2]; 33 | var auroraPoints = new [len + 2]; 34 | 35 | var hour = Time.Gregorian.info(Time.now(), Time.FORMAT_SHORT).hour; 36 | var maxAurora = 0; 37 | var cloudY = mh + FONT_HEIGHT + ch * 0.12; 38 | for (var i = 0; i < len; i++) { 39 | var x = mw + (cw / (len - 1.0) * i); 40 | cloudPoints[i] = [ x, cloudY + ch * data.hourlyClouds[i] / 1000 ]; 41 | cloudPoints[len * 2 - 1 - i] = [ x, cloudY - ch * data.hourlyClouds[i] / 1000 ]; 42 | 43 | var aurStrength = data.hourlyAurora.size() > i ? data.hourlyAurora[i] : 0; 44 | auroraPoints[i] = [ x, mh + ch - (ch * aurStrength * 0.4)]; 45 | maxAurora = aurStrength > maxAurora ? aurStrength : maxAurora; 46 | 47 | if (i % 6 == 0) { 48 | dc.drawText(x, mh + ch, Graphics.FONT_XTINY, ((hour + i) % 24).format("%02d"), Graphics.TEXT_JUSTIFY_CENTER); 49 | } 50 | if ((hour + i) % 24 == 0) { 51 | dc.drawLine(x, mh + ch * 0.6, x, mh + ch - 1); 52 | } 53 | } 54 | 55 | auroraPoints[len] = [ mw + cw, mh + ch ]; 56 | auroraPoints[len + 1] = [ mw, mh + ch ]; 57 | 58 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 59 | dc.fillPolygon(cloudPoints); 60 | 61 | if (len > 0) { 62 | dc.drawText(mw, mh, Graphics.FONT_XTINY, data.hourlyClouds[0].format("%d") + "% Cloudy", Graphics.TEXT_JUSTIFY_LEFT); 63 | } 64 | 65 | dc.setColor(Graphics.COLOR_GREEN, Graphics.COLOR_TRANSPARENT); 66 | dc.fillPolygon(auroraPoints); 67 | 68 | dc.drawText(mw + cw, mh, Graphics.FONT_XTINY, 69 | (maxAurora == 0 ? "No" : maxAurora < 0.5 ? "Low" : "High") + " Kp", Graphics.TEXT_JUSTIFY_RIGHT); 70 | } 71 | 72 | // Page Indicator 73 | drawIndicator(dc, 4); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /source/view/BaseDelegate.mc: -------------------------------------------------------------------------------- 1 | import Toybox.Lang; 2 | import Toybox.WatchUi; 3 | 4 | class BaseDelegate extends BehaviorDelegate { 5 | 6 | public static var pageCount = 7; 7 | private static var page as Number = 0; 8 | private var startX as Number = 0; 9 | 10 | function initialize() { 11 | BehaviorDelegate.initialize(); 12 | } 13 | 14 | function onMenu() { 15 | var menu = new Menu2({:title=> "Rainy " + VERSION }); 16 | menu.addItem(new MenuItem("Location", data.autoLocation ? "Automatic (GPS)" : data.location, 0, {})); 17 | menu.addItem(new MenuItem("Show Graph First", data.pageOrder ? "Yes" : "No", 1, {})); 18 | menu.addItem(new MenuItem("Temp Units", data.tempUnits == 0 ? "Celsius" : "Fahrenheit", 2, {})); 19 | menu.addItem(new MenuItem("Wind Units", SettingsDelegate.WIND_UNITS[data.windUnits], 3, {})); 20 | menu.addItem(new MenuItem("Refresh Weather", "Updated " + ((Time.now().value() - data.time.value()) / 60) + " minutes ago", 4, {})); 21 | WatchUi.pushView(menu, new SettingsDelegate(), WatchUi.SLIDE_BLINK); 22 | return true; 23 | } 24 | 25 | function onNextPage() as Boolean { 26 | if (data.temperatures.size() < 1) { 27 | return false; 28 | } 29 | 30 | page = page == (pageCount - 1) ? 0 : page + 1; 31 | 32 | var view = getView(page); 33 | WatchUi.switchToView(view[0], view[1], WatchUi.SLIDE_UP); 34 | return true; 35 | } 36 | 37 | function onPreviousPage() as Boolean { 38 | if (data.temperatures.size() < 1) { 39 | return false; 40 | } 41 | 42 | page = page == 0 ? pageCount - 1 : page - 1; 43 | 44 | var view = getView(page); 45 | WatchUi.switchToView(view[0], view[1], WatchUi.SLIDE_DOWN); 46 | return true; 47 | } 48 | 49 | // Swipe left to go to next page in multiplage views 50 | function onDrag(event as DragEvent) as Boolean { 51 | if (event.getType() == 0 /* START */) { 52 | startX = event.getCoordinates()[0]; 53 | } else if (event.getType() == 2 /* STOP */) { 54 | if (startX - event.getCoordinates()[0] > (System.getDeviceSettings().screenWidth / 4)) { 55 | onSelectOrSwipe(false); 56 | } 57 | } 58 | 59 | return BehaviorDelegate.onDrag(event); 60 | } 61 | 62 | function onSelect() as Boolean { 63 | return onSelectOrSwipe(false); 64 | } 65 | 66 | // If softAction then only iterate through information and don't open any menus 67 | // Returns whether the next page should be displayed in softAction mode 68 | function onSelectOrSwipe(softAction as Boolean) as Boolean { 69 | BaseView.page = (BaseView.page + 1) % BaseView.pages; 70 | WatchUi.requestUpdate(); 71 | return BaseView.page != 0 || !softAction; 72 | } 73 | 74 | function getView(page as Number) as Array { 75 | switch (data.pageOrder ? (page == 1 ? 3 : page == 3 ? 1 : page) : page) { 76 | case 0: return [ new SummaryView(), new BaseDelegate() ]; 77 | case 1: return [ new DailyView(), new BaseDelegate() ]; 78 | case 2: return [ new HourlyView(), new BaseDelegate() ]; 79 | case 3: return [ new GraphView(), new BaseDelegate() ]; 80 | case 4: return [ new AuroraView(), new BaseDelegate() ]; 81 | case 5: return [ new UvView(), new BaseDelegate() ]; 82 | case 6: return [ new CelestialView(), new BaseDelegate() ]; 83 | default: return [ new WaterView(), new BaseDelegate() ]; 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /source/view/BaseView.mc: -------------------------------------------------------------------------------- 1 | import Toybox.Application; 2 | import Toybox.Graphics; 3 | import Toybox.WatchUi; 4 | import Toybox.Lang; 5 | 6 | class BaseView extends WatchUi.View { 7 | public static var page as Number = 0; 8 | public static var pages as Number = 1; // Super not correct use of static but we do what the voices in my head tell us to 9 | 10 | function initialize(pages) { 11 | View.initialize(); 12 | self.pages = pages; 13 | self.page = 0; 14 | } 15 | 16 | // Update the view 17 | function onUpdate(dc as Dc) as Void { 18 | // Call the parent onUpdate function to redraw the layout 19 | View.onUpdate(dc); 20 | 21 | // Bad place but yeah 22 | if (Properties.getValue("manLocation").length() > 0) { 23 | data.enterText(Properties.getValue("manLocation")); 24 | Properties.setValue("manLocation", ""); 25 | return; 26 | } 27 | 28 | var W = dc.getWidth(); 29 | var H = dc.getHeight(); 30 | if (data.blocked) { 31 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 32 | dc.drawText(W / 2, H / 4, Graphics.FONT_MEDIUM, "Relaunch App and\nfollow activation\ninstructions.", Graphics.TEXT_JUSTIFY_CENTER); 33 | return; 34 | } 35 | 36 | onDraw(dc, W, H, dc.getFontHeight(Graphics.FONT_XTINY)); 37 | } 38 | 39 | function onDraw(dc as Dc, W as Number, H as Number, FONT_HEIGHT as Number) as Void { 40 | } 41 | 42 | function drawHeader(dc as Dc, W as Number, H as Number, text as String) { 43 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 44 | var font = text.length() > 16 ? Graphics.FONT_XTINY : text.length() > 13 ? Graphics.FONT_TINY : text.length() > 11 ? Graphics.FONT_SMALL : Graphics.FONT_MEDIUM; 45 | dc.drawText(W / 2, H / 5.1 - dc.getFontHeight(font), font, text, Graphics.TEXT_JUSTIFY_CENTER); 46 | } 47 | 48 | public static function drawIndicator(dc as Dc, selectedIndex as Number) as Void { 49 | drawDots(dc, BaseDelegate.pageCount, selectedIndex, 3.14, 0.1); 50 | } 51 | 52 | public static function drawDots(dc as Dc, count as Number, selected as Number, angle as Float, space as Float) as Void { 53 | var height = dc.getWidth() / 30; 54 | for (var i = 0; i < count; i++) { 55 | var b = dc.getWidth() / 2 - height + 2; 56 | 57 | //round page indicator 58 | var x_i = b * Toybox.Math.cos(angle + ((count - 1f) / 2) * space - i * space) + dc.getWidth() / 2; 59 | var y_i = b * Toybox.Math.sin(angle + ((count - 1f) / 2) * space - i * space) + dc.getHeight() / 2; 60 | 61 | if (SQUARE_MODE) { 62 | x_i = x_i < dc.getWidth() / 2 ? height - 2 : dc.getWidth() - height + 2; 63 | } 64 | 65 | if (i == selected || selected == -1) { 66 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 67 | dc.fillCircle(x_i, y_i, height / 2); 68 | } else { 69 | dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT); 70 | dc.drawCircle(x_i, y_i, height / 2); 71 | } 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /source/view/CelestialView.mc: -------------------------------------------------------------------------------- 1 | import Toybox.Graphics; 2 | import Toybox.WatchUi; 3 | import Toybox.Lang; 4 | import Toybox.Time; 5 | 6 | class CelestialView extends BaseView { 7 | 8 | function initialize() { 9 | BaseView.initialize(2); 10 | } 11 | 12 | // Update the view 13 | function onDraw(dc as Dc, W as Number, H as Number, FONT_HEIGHT as Number) as Void { 14 | drawHeader(dc, W, H, page == 0 ? "Sun" : "Moon"); 15 | 16 | var mw = W * ((dc.getFontHeight(Graphics.FONT_XTINY).toFloat() / H > 0.09) ? 0.1 : 0.13); // Margin Width (Less for devices with bigger fonts) 17 | var mh = H * 0.23; // Margin Height 18 | var cw = W - mw * 2.0; // Chart Width 19 | var ch = (H - mh * 2.0) * 0.4; // Chart Height 20 | 21 | // Sun line 22 | if (page == 0) { 23 | dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT); 24 | dc.drawLine(mw, H - mh, mw + cw, H - mh); 25 | dc.drawLine(mw, H - mh - ch * 0.5, mw + cw, H - mh - ch * 0.5); 26 | dc.drawLine(mw, H - mh - ch, mw + cw, H - mh - ch); 27 | 28 | var len = data.sunElevation.size(); 29 | var sunPoints = new [len * 2] as Array<[Float, Float]>; 30 | var lightPoints = []; 31 | for (var i = 0; i < len; i++) { 32 | var x = mw + (cw / (len - 1.0) * i); 33 | 34 | sunPoints[i] = [ x, H - mh - ch * 0.5 - (data.sunElevation[i] / 150.0) * ch]; 35 | sunPoints[len * 2 - 1 - i] = [ x, H - mh - ch * 0.5 - H / 150 - (data.sunElevation[i] / 150.0) * ch]; 36 | if (data.sunElevation[i] >= 0) { 37 | lightPoints.add(sunPoints[i]); 38 | } 39 | } 40 | 41 | dc.setColor(Graphics.COLOR_YELLOW, Graphics.COLOR_TRANSPARENT); 42 | dc.fillPolygon(lightPoints); 43 | 44 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 45 | dc.fillPolygon(sunPoints); 46 | 47 | // Sun ball 48 | var time = Gregorian.info(Time.now(), Time.FORMAT_SHORT); 49 | var minute = time.hour * 60 + time.min; 50 | var sunY = sunPoints[(sunPoints.size() / 2 * (minute / 1441.0)).toNumber()][1]; 51 | 52 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 53 | dc.fillCircle(mw + cw * (minute / 1440.0), sunY, W / 28); 54 | dc.setColor(sunY > H - mh - ch * 0.5 ? Graphics.COLOR_BLACK : Graphics.COLOR_YELLOW, Graphics.COLOR_TRANSPARENT); 55 | dc.fillCircle(mw + cw * (minute / 1440.0), sunY, W / 32); 56 | 57 | // Timestamps 58 | var riseTime = Gregorian.info(new Moment(data.sunRise), Time.FORMAT_SHORT); 59 | var maxTime = Gregorian.info(new Moment(data.sunMax), Time.FORMAT_SHORT); 60 | var setTime = Gregorian.info(new Moment(data.sunSet), Time.FORMAT_SHORT); 61 | 62 | var riseX = mw + cw * ((riseTime.hour * 60 + riseTime.min) / 1440.0); 63 | var maxX = mw + cw * ((maxTime.hour * 60 + maxTime.min) / 1440.0); 64 | var setX = mw + cw * ((setTime.hour * 60 + setTime.min) / 1440.0); 65 | 66 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 67 | dc.drawText(riseX, H - mh - ch - FONT_HEIGHT, Graphics.FONT_XTINY, "RISE", Graphics.TEXT_JUSTIFY_CENTER); 68 | dc.drawText(riseX, H - mh, Graphics.FONT_XTINY, 69 | riseTime.hour.format("%02d") + ":" + riseTime.min.format("%02d"), Graphics.TEXT_JUSTIFY_CENTER); 70 | 71 | dc.drawText(maxX, H - mh - ch - FONT_HEIGHT, Graphics.FONT_XTINY, "HIGH", Graphics.TEXT_JUSTIFY_CENTER); 72 | dc.drawText(maxX, H - mh, Graphics.FONT_XTINY, 73 | maxTime.hour.format("%02d") + ":" + maxTime.min.format("%02d"), Graphics.TEXT_JUSTIFY_CENTER); 74 | 75 | dc.drawText(setX, H - mh - ch - FONT_HEIGHT, Graphics.FONT_XTINY, "SET", Graphics.TEXT_JUSTIFY_CENTER); 76 | dc.drawText(setX, H - mh, Graphics.FONT_XTINY, 77 | setTime.hour.format("%02d") + ":" + setTime.min.format("%02d"), Graphics.TEXT_JUSTIFY_CENTER); 78 | 79 | dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT); 80 | dc.drawLine(riseX, H - mh - ch, riseX, H - mh); 81 | dc.drawLine(maxX, H - mh - ch, maxX, H - mh); 82 | dc.drawLine(setX, H - mh - ch, setX, H - mh); 83 | 84 | // Top Text 85 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 86 | dc.drawText(mw, mh, Graphics.FONT_XTINY, 87 | "Daylight " + (data.sunLength / 3600).format("%d") + 88 | "h " + ((data.sunLength / 60) % 60).format("%d") + "m", Graphics.TEXT_JUSTIFY_LEFT); 89 | dc.drawText(mw, mh + FONT_HEIGHT, Graphics.FONT_XTINY, 90 | "Today " + (data.sunDifference >= 0 ? "+" : "-") + (data.sunDifference.abs() / 60).format("%d") + 91 | "m " + (data.sunDifference.abs() % 60).format("%d") + "s", Graphics.TEXT_JUSTIFY_LEFT); 92 | 93 | // Sun Icon 94 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 95 | dc.fillCircle(W - mw - W / 10, mh + H / 10, W / 13); 96 | dc.setColor(Graphics.COLOR_ORANGE, Graphics.COLOR_TRANSPARENT); 97 | dc.fillCircle(W - mw - W / 10, mh + H / 10, W / 15); 98 | dc.setColor(Graphics.COLOR_YELLOW, Graphics.COLOR_TRANSPARENT); 99 | dc.fillCircle(W - mw - W / 10, mh + H / 10, W / 17); 100 | } else { 101 | // Top Text 102 | var eclipseTime = Gregorian.info(new Moment(data.sunNextEclipse), Time.FORMAT_MEDIUM); 103 | dc.drawText(mw, mh, Graphics.FONT_XTINY, 104 | "Next Solar", Graphics.TEXT_JUSTIFY_LEFT); 105 | dc.drawText(mw, mh + FONT_HEIGHT, Graphics.FONT_XTINY, 106 | "Eclipse (" + (data.sunEclipseObsc > 0.99 ? "total" : "partial") + ")", Graphics.TEXT_JUSTIFY_LEFT); 107 | dc.drawText(mw, mh + FONT_HEIGHT * 2, Graphics.FONT_XTINY, 108 | eclipseTime.day + " " + eclipseTime.month + " " + eclipseTime.year, Graphics.TEXT_JUSTIFY_LEFT); 109 | 110 | // Moon Icon 111 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 112 | dc.fillCircle(W - mw - W / 10, mh + H / 10, W / 13); 113 | dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_TRANSPARENT); 114 | dc.fillCircle(W - mw - W / 10 + (W / 13) * data.moonIllumination / 100, mh + H / 10 - (W / 13) * data.moonIllumination / 100, W / 13); 115 | 116 | // Bottom Text 117 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 118 | dc.drawText(mw, H - mh - FONT_HEIGHT * 2, Graphics.FONT_XTINY, "Now: " + data.moonIllumination.toNumber() + "%", Graphics.TEXT_JUSTIFY_LEFT); 119 | dc.drawText(mw, H - mh - FONT_HEIGHT, Graphics.FONT_XTINY, 120 | data.moonPhase.substring(0, data.moonPhase.find(" ")), Graphics.TEXT_JUSTIFY_LEFT); 121 | dc.drawText(mw, H - mh, Graphics.FONT_XTINY, 122 | data.moonPhase.substring(data.moonPhase.find(" ") + 1, data.moonPhase.length()), Graphics.TEXT_JUSTIFY_LEFT); 123 | 124 | var fullTime = Gregorian.info(new Moment(data.moonNextFull), Time.FORMAT_MEDIUM); 125 | var newTime = Gregorian.info(new Moment(data.moonNextNew), Time.FORMAT_MEDIUM); 126 | dc.drawText(W - mw, H - mh - FONT_HEIGHT * 3, Graphics.FONT_XTINY, "Full Moon", Graphics.TEXT_JUSTIFY_RIGHT); 127 | dc.drawText(W - mw, H - mh - FONT_HEIGHT * 2, Graphics.FONT_XTINY, 128 | fullTime.month + " " + fullTime.day + " " + fullTime.hour.format("%02d") + ":" + fullTime.min.format("%02d"), Graphics.TEXT_JUSTIFY_RIGHT); 129 | dc.drawText(W - mw, H - mh - FONT_HEIGHT, Graphics.FONT_XTINY, "New Moon", Graphics.TEXT_JUSTIFY_RIGHT); 130 | dc.drawText(W - mw, H - mh, Graphics.FONT_XTINY, 131 | newTime.month + " " + newTime.day + " " + newTime.hour.format("%02d") + ":" + newTime.min.format("%02d"), Graphics.TEXT_JUSTIFY_RIGHT); 132 | } 133 | 134 | // Local Page Indicator 135 | dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT); 136 | dc.drawText(W / 2, H * 0.8 + FONT_HEIGHT, Graphics.FONT_TINY, (page + 1) + "/2", Graphics.TEXT_JUSTIFY_CENTER); 137 | 138 | // Page Indicator 139 | drawIndicator(dc, 6); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /source/view/DailyView.mc: -------------------------------------------------------------------------------- 1 | import Toybox.Application; 2 | import Toybox.Graphics; 3 | import Toybox.WatchUi; 4 | import Toybox.Lang; 5 | 6 | class DailyView extends BaseView { 7 | 8 | function initialize() { 9 | BaseView.initialize(Math.floor(dailyIndices().size() / 4)); 10 | } 11 | 12 | // Update the view 13 | function onDraw(dc as Dc, W as Number, H as Number, FONT_HEIGHT as Number) as Void { 14 | if (data.hints & 2 == 0) { 15 | data.hints |= 2; 16 | Storage.setValue("hints", data.hints); 17 | WatchUi.pushView(new HintView("Press Select to scroll\non pages with\nmultiple slides.", [ 0 ]), new HintDelegate([ 0 ]), WatchUi.SLIDE_BLINK); 18 | return; 19 | } 20 | 21 | var indices = dailyIndices(); 22 | 23 | var realHour = Time.Gregorian.info(data.time, Time.FORMAT_SHORT).hour; 24 | var hour = realHour % 6 == 0 ? realHour : realHour + (6 - (realHour % 6)); 25 | var date = Time.Gregorian.info(new Time.Moment(data.time.value() + 86400 * (page + (realHour > 18 ? 1 : 0))), Time.FORMAT_MEDIUM); 26 | drawHeader(dc, W, H, page ? date.day_of_week + " " + date.day + "." : "Daily"); 27 | 28 | var filler = (hour % 24) / 6; 29 | for (var i = 0; i < 4; i++) { 30 | var entry = page * 4 + i - filler; 31 | if (indices.size() <= entry) { 32 | break; 33 | } 34 | 35 | if (entry == -1 && realHour % 6 != 0) { 36 | HourlyView.drawTableEntry(dc, W, H, realHour, indices[0], i); 37 | } else if (entry <= -1) { 38 | HourlyView.drawTableEntry(dc, W, H, "--", null, i); 39 | } else { 40 | HourlyView.drawTableEntry(dc, W, H, (hour + entry * 6) % 24, indices[entry], i); 41 | } 42 | } 43 | 44 | // Local Page Indicator 45 | dc.drawText(W / 2, H * 0.75 + FONT_HEIGHT, Graphics.FONT_TINY, (page + 1) + "/" + pages, Graphics.TEXT_JUSTIFY_CENTER); 46 | 47 | // Page Indicator 48 | drawIndicator(dc, data.pageOrder ? 3 : 1); 49 | } 50 | 51 | function dailyIndices() as Array { 52 | var hour = Time.Gregorian.info(data.time, Time.FORMAT_SHORT).hour; 53 | var entries = []; 54 | for (var i = 0; i < data.symbols.size(); i++) { 55 | if ((hour + i) % 6 == 0 || i >= data.hours) { 56 | entries.add(i); 57 | } 58 | } 59 | 60 | return entries; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /source/view/GraphView.mc: -------------------------------------------------------------------------------- 1 | import Toybox.Graphics; 2 | import Toybox.WatchUi; 3 | import Toybox.Lang; 4 | 5 | class GraphView extends BaseView { 6 | 7 | function initialize() { 8 | BaseView.initialize((data.hourlyEntries() - 2) / 12 + 1); 9 | } 10 | 11 | function onDraw(dc as Dc, W as Number, H as Number, FONT_HEIGHT as Number) as Void { 12 | drawHeader(dc, W, H, page ? "In " + (page * 12) + " Hours" : "Now"); 13 | dc.setColor(Graphics.COLOR_DK_GRAY, Graphics.COLOR_TRANSPARENT); 14 | 15 | var offset = page * 12; 16 | var mw = W * 0.16; // Margin Width 17 | var mh = H * 0.25; // Margin Height 18 | var lw = (W - mw * 2) / 12.0; // Line Width 19 | var lh = (H - mh * 2) / 9.0; // Line Height 20 | var points = data.hourlyEntries() - offset > 12 ? 12 : data.hourlyEntries() - offset - 1; 21 | var startHour = Time.Gregorian.info(data.time, Time.FORMAT_SHORT).hour; 22 | 23 | var minTemp = data.temperatures[0]; 24 | var maxTemp = data.temperatures[0]; 25 | for (var i = 0; i < data.temperatures.size(); i++) { 26 | if (data.temperatures[i] - 2 < minTemp) { 27 | minTemp = data.temperatures[i] - 2; 28 | } 29 | if (data.temperatures[i] + 2 > maxTemp) { 30 | maxTemp = data.temperatures[i] + 2; 31 | } 32 | } 33 | var diffTemp = maxTemp - minTemp; 34 | 35 | // Horizontal Graph Background 36 | dc.setColor(Graphics.COLOR_DK_GRAY, Graphics.COLOR_TRANSPARENT); 37 | for (var i = 0; i < 10; i++) { 38 | dc.drawLine(mw, mh + lh * i, W - mw - lw * (12 - points), mh + lh * i); 39 | } 40 | 41 | // 0 Degree line 42 | if (minTemp < 0 && maxTemp > 0) { 43 | var freezingY = H - mh - (-minTemp / diffTemp) * (H - mh * 2); 44 | dc.setColor(Graphics.COLOR_BLUE, Graphics.COLOR_TRANSPARENT); 45 | dc.drawLine(mw, freezingY, W - mw - lw * (12 - points), freezingY); 46 | } 47 | 48 | // Vertical Graph Background + Timestamps 49 | for (var i = 0; i < points + 1; i++) { 50 | dc.setColor((startHour + offset + i) % 24 == 0 ? Graphics.COLOR_LT_GRAY : Graphics.COLOR_DK_GRAY, Graphics.COLOR_TRANSPARENT); 51 | dc.drawLine(mw + lw * i, mh, mw + lw * i, H - mh); 52 | 53 | // Bottom time text 54 | if (i % 3 == 0) { 55 | dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT); 56 | dc.drawText(mw + lw * i, H - mh, Graphics.FONT_XTINY, ((startHour + offset + i) % 24).format("%02d"), Graphics.TEXT_JUSTIFY_CENTER); 57 | } 58 | } 59 | 60 | var tempPoints = new [points * 2 + 2]; 61 | for (var i = 0; i < points; i++) { 62 | // Rain (fillRectancle uses wh instead of xy :troll:) 63 | dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT); 64 | var maxRain = data.maxRainfall[offset + i]; 65 | dc.fillRectangle(mw + lw * i, H - mh - lh * maxRain, lw, lh * maxRain); 66 | 67 | dc.setColor(Graphics.COLOR_BLUE, Graphics.COLOR_TRANSPARENT); 68 | var rain = data.rainfall[offset + i]; 69 | dc.fillRectangle(mw + lw * i, H - mh - lh * rain, lw, lh * rain); 70 | 71 | // Temperature 72 | var t1 = H - mh - ((data.temperatures[offset + i].toFloat() - minTemp) / diffTemp) * (H - mh * 2); 73 | tempPoints[i] = [ mw + lw * i, t1 ]; 74 | tempPoints[tempPoints.size() - 1 - i] = [ mw + lw * i, t1 - lh / 4 ]; 75 | if (i + 1 == points) { 76 | var t2 = H - mh - ((data.temperatures[offset + i + 1].toFloat() - minTemp) / diffTemp) * (H - mh * 2); 77 | tempPoints[i + 1] = [ mw + lw * (i + 1), t2 ]; 78 | tempPoints[i + 2] = [ mw + lw * (i + 1), t2 - lh / 4 ]; 79 | } 80 | } 81 | 82 | dc.setColor(Graphics.COLOR_RED, Graphics.COLOR_TRANSPARENT); 83 | dc.fillPolygon(tempPoints); 84 | 85 | // Left side temperature text 86 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 87 | dc.drawText(mw - 3, mh, Graphics.FONT_XTINY, degrees(maxTemp, data.tempUnits) + "°", Graphics.TEXT_JUSTIFY_RIGHT); 88 | dc.drawText(mw - 3, mh + (H - mh * 2) * 0.27, Graphics.FONT_XTINY, degrees(maxTemp - diffTemp * 0.33, data.tempUnits) + "°", Graphics.TEXT_JUSTIFY_RIGHT); 89 | dc.drawText(mw - 3, mh + (H - mh * 2) * 0.55, Graphics.FONT_XTINY, degrees(maxTemp - diffTemp * 0.66, data.tempUnits) + "°", Graphics.TEXT_JUSTIFY_RIGHT); 90 | dc.drawText(mw - 3, H - mh - FONT_HEIGHT, Graphics.FONT_XTINY, degrees(minTemp, data.tempUnits) + "°", Graphics.TEXT_JUSTIFY_RIGHT); 91 | 92 | // Right side rainfall text 93 | dc.drawText(W - mw + 3, mh, Graphics.FONT_XTINY, "9", Graphics.TEXT_JUSTIFY_LEFT); 94 | dc.drawText(W - mw + 3, mh + (H - mh * 2) * 0.27, Graphics.FONT_XTINY, "6", Graphics.TEXT_JUSTIFY_LEFT); 95 | dc.drawText(W - mw + 3, mh + (H - mh * 2) * 0.55, Graphics.FONT_XTINY, "3", Graphics.TEXT_JUSTIFY_LEFT); 96 | dc.drawText(W - mw + 3, H - mh - FONT_HEIGHT, Graphics.FONT_XTINY, "0", Graphics.TEXT_JUSTIFY_LEFT); 97 | 98 | // Local Page Indicator 99 | dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT); 100 | dc.drawText(W / 2, H - mh + FONT_HEIGHT, Graphics.FONT_TINY, (page + 1) + "/" + pages, Graphics.TEXT_JUSTIFY_CENTER); 101 | 102 | // Page Indicator 103 | drawIndicator(dc, data.pageOrder ? 1 : 3); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /source/view/HourlyView.mc: -------------------------------------------------------------------------------- 1 | import Toybox.Graphics; 2 | import Toybox.WatchUi; 3 | import Toybox.Lang; 4 | import Toybox.Math; 5 | 6 | class HourlyView extends BaseView { 7 | 8 | function initialize() { 9 | BaseView.initialize(6); 10 | } 11 | 12 | // Update the view 13 | function onDraw(dc as Dc, W as Number, H as Number, FONT_HEIGHT as Number) as Void { 14 | drawHeader(dc, W, H, page ? "In " + (page * 4) + " Hours" : "Hourly"); 15 | 16 | var hour = Time.Gregorian.info(data.time, Time.FORMAT_SHORT).hour; 17 | for (var i = 0; i < 4; i++) { 18 | var entry = page * 4 + i; 19 | if (data.hourlyEntries() <= entry) { 20 | break; 21 | } 22 | 23 | drawTableEntry(dc, W, H, (hour + entry) % 24, entry, i); 24 | } 25 | 26 | // Local Page Indicator 27 | dc.drawText(W / 2, H * 0.75 + FONT_HEIGHT, Graphics.FONT_TINY, (page + 1) + "/" + pages, Graphics.TEXT_JUSTIFY_CENTER); 28 | 29 | // Page Indicator 30 | drawIndicator(dc, 2); 31 | } 32 | 33 | static function drawTableEntry(dc as Dc, W as Number, H as Number, hour as Number or String, dataIndex as Number?, entry as Number) { 34 | var mh = H * 0.2; 35 | var lh = H / 6.75; 36 | var offset = entry * lh; 37 | 38 | // Time 39 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 40 | dc.drawText(W / 15, mh + lh / 4 + offset, Graphics.FONT_XTINY, hour instanceof String ? hour : hour.format("%02d"), Graphics.TEXT_JUSTIFY_LEFT); 41 | if (dataIndex == null) { 42 | return; 43 | } 44 | 45 | // Temperature 46 | dc.drawBitmap(W / 7, mh + offset, res.getSymbol(data.symbols[dataIndex])); 47 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 48 | dc.drawText(W / 7 + 47, mh + offset, Graphics.FONT_MEDIUM, degrees(data.temperatures[dataIndex], data.tempUnits) + "°", Graphics.TEXT_JUSTIFY_LEFT); 49 | 50 | // Rainfall 51 | var rain = data.rainfall[dataIndex]; 52 | dc.setColor(Graphics.COLOR_BLUE, Graphics.COLOR_TRANSPARENT); 53 | dc.drawText(W / 1.62, 54 | mh + offset, Graphics.FONT_MEDIUM, 55 | rain.format(Math.round(rain) == rain ? "%d" : "%.1f"), Graphics.TEXT_JUSTIFY_CENTER); 56 | 57 | // Wind 58 | dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT); 59 | dc.drawText(W - lh * 0.9, 60 | mh + offset, 61 | Graphics.FONT_MEDIUM, wind(data.windSpeeds[dataIndex], data.windUnits), Graphics.TEXT_JUSTIFY_RIGHT); 62 | dc.fillPolygon(generateArrow([ W - lh * 0.84, mh + lh / 8 + offset ], 63 | data.windDirections[dataIndex] + 180, (lh * 0.73).toNumber())); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /source/view/SummaryView.mc: -------------------------------------------------------------------------------- 1 | import Toybox.Graphics; 2 | import Toybox.WatchUi; 3 | import Toybox.Lang; 4 | 5 | class SummaryView extends BaseView { 6 | 7 | function initialize() { 8 | BaseView.initialize(1); 9 | } 10 | 11 | // Update the view 12 | function onDraw(dc as Dc, W as Number, H as Number, FONT_HEIGHT as Number) as Void { 13 | if (data.position == null) { 14 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 15 | dc.drawText(W / 2, H / 8.2, Graphics.FONT_MEDIUM, "Updating GPS\nLocation..", Graphics.TEXT_JUSTIFY_CENTER); 16 | dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT); 17 | dc.drawText(W / 2, H / 2.1, Graphics.FONT_TINY, "Or press MENU\nto manually select\na location.", Graphics.TEXT_JUSTIFY_CENTER); 18 | return; 19 | } 20 | 21 | if (data.symbols.size() < 1) { 22 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 23 | dc.drawText(W / 2, H / 6.5, Graphics.FONT_LARGE, "Loading..", Graphics.TEXT_JUSTIFY_CENTER); 24 | dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT); 25 | dc.drawText(W / 2, H / 2.6, Graphics.FONT_TINY, "Phone Bluetooth\nconnection required\nto load Weather.", Graphics.TEXT_JUSTIFY_CENTER); 26 | return; 27 | } 28 | 29 | // Location 30 | drawHeader(dc, W, H, data.location); 31 | 32 | // Temperature 33 | var sumM = H / 13; // Summary margin 34 | dc.drawBitmap(sumM, sumM * 2.7, res.getSymbol(data.symbols.size() == 0 ? 2018941991 : data.symbols[0])); 35 | 36 | dc.drawText(sumM + 50, sumM * 2.5, Graphics.FONT_NUMBER_MILD, degrees(data.temperatures[0], data.tempUnits) + "°", Graphics.TEXT_JUSTIFY_LEFT); 37 | dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT); 38 | var apTemp = calcApparentTemperature(data.temperatures[0], data.humidity[0], data.windSpeeds[0]); 39 | dc.drawText(sumM, sumM * 5, Graphics.FONT_XTINY, "Feels Like " + degrees(apTemp, data.tempUnits) + "°" + (data.tempUnits == 1 ? "F" : "C"), Graphics.TEXT_JUSTIFY_LEFT); 40 | 41 | // Wind 42 | dc.drawText(W - sumM * 3.1, sumM * 2.5, Graphics.FONT_NUMBER_MILD, wind(data.windSpeeds[0], data.windUnits), Graphics.TEXT_JUSTIFY_RIGHT); 43 | dc.drawText(W - sumM * 1.1, sumM * 5, Graphics.FONT_XTINY, 44 | SettingsDelegate.WIND_UNITS[data.windUnits], Graphics.TEXT_JUSTIFY_RIGHT); 45 | dc.fillPolygon(generateArrow([ W - sumM * 2.9, sumM * 2.9 ], data.windDirections[0] + 180, (sumM * 1.8).toNumber())); 46 | dc.setColor(Graphics.COLOR_BLUE, Graphics.COLOR_TRANSPARENT); 47 | 48 | // Rain chart 49 | var mw = W / 8.5; // Rain chart width margin 50 | var mh = H / 6.7; // Rain chart height margin 51 | var lh = H / 14; // Rain chart line height 52 | var rainPrimary = data.nowRainfall != null && data.nowRainfall.size() > 0; 53 | var rainBackup = data.rainfall.size() >= 6; 54 | 55 | var rainCount = rainPrimary ? data.nowRainfall.size() : rainBackup ? 6 : 0; 56 | var rainPolygon = new [rainCount + 2]; 57 | rainPolygon[0] = [ W - mw, H - mh ]; 58 | rainPolygon[1] = [ mw, H - mh ]; 59 | for (var i = 0; i < rainCount; i++) { 60 | var r = rainPrimary ? data.nowRainfall[i] : data.rainfall[i]; 61 | rainPolygon[i + 2] = [ mw + ((W - (mw * 2)) / (rainCount - 1)) * i, H - mh - (r <= 0 ? 0 : r > 5 ? lh * 3.25 : ((r + 0.3) * lh * 0.6)) ]; 62 | } 63 | 64 | dc.fillPolygon(rainPolygon); 65 | 66 | dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT); 67 | dc.drawText(W * 0.27, H - mh + 2, Graphics.FONT_XTINY, "Now", Graphics.TEXT_JUSTIFY_CENTER); 68 | dc.drawText(W / 2, H - mh + 2, Graphics.FONT_XTINY, !rainPrimary && rainBackup ? "3hr" : "45", Graphics.TEXT_JUSTIFY_CENTER); 69 | dc.drawText(W * 0.73, H - mh + 2, Graphics.FONT_XTINY, !rainPrimary && rainBackup ? "6hr" : "90", Graphics.TEXT_JUSTIFY_CENTER); 70 | 71 | dc.drawLine(mw, H - mh, W - mw, H - mh); 72 | dc.setColor(Graphics.COLOR_DK_GRAY, Graphics.COLOR_TRANSPARENT); 73 | dc.drawLine(mw, H - mh - lh, W - mw, H - mh - lh); 74 | dc.drawLine(mw, H - mh - lh * 2, W - mw, H - mh - lh * 2); 75 | dc.drawLine(mw, H - mh - lh * 3, W - mw, H - mh - lh * 3); 76 | 77 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 78 | dc.drawText(W / 2, H - mh - lh * 3 - FONT_HEIGHT, Graphics.FONT_XTINY, 79 | rainPrimary ? "Rainfall next 90 min." : rainBackup ? "Rainfall next 6 hr." : "90 Min. Rainfall Unavailable.", Graphics.TEXT_JUSTIFY_CENTER); 80 | 81 | // Page Indicator 82 | drawIndicator(dc, 0); 83 | } 84 | 85 | // thanks Mr. Gpt (may or may not actually calculate the right values) 86 | function calcApparentTemperature(temp as Float, humidity as Float, wind as Float) as Float { 87 | if (temp > 27.0) { 88 | // Calculate Heat Index 89 | var tempFahrenheit = (temp * 9 / 5) + 32; 90 | var heatIndex = 0.5 * (tempFahrenheit + 61.0 + ((tempFahrenheit - 68.0) * 1.2) + (humidity * 0.094)); 91 | 92 | // Ensure the result is in Celsius 93 | return (heatIndex - 32) * 5 / 9; 94 | } else if (temp < 10.0) { 95 | var windMph = wind * 2.237; // meters/s to miles/h 96 | var windChill = 13.12 + 0.6215 * temp - 11.37 * Math.pow(windMph, 0.16) + 0.3965 * temp * Math.pow(windMph, 0.16); 97 | 98 | return windChill; 99 | } 100 | 101 | return temp + (humidity / 100.0) * 5; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /source/view/UvView.mc: -------------------------------------------------------------------------------- 1 | import Toybox.Graphics; 2 | import Toybox.WatchUi; 3 | import Toybox.Lang; 4 | 5 | class UvView extends BaseView { 6 | 7 | function initialize() { 8 | BaseView.initialize(1); 9 | } 10 | 11 | // Update the view 12 | function onDraw(dc as Dc, W as Number, H as Number, FONT_HEIGHT as Number) as Void { 13 | drawHeader(dc, W, H, "UV / Air"); 14 | 15 | var mw = W * 0.15; // Margin Width 16 | var mh = H * 0.23; // Margin Height 17 | var cw = W - mw * 2.0; // Chart Width 18 | var ch = (H - mh * 2.0) * 0.4; // Chart Height 19 | 20 | // UV guidelines 21 | dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT); 22 | dc.drawLine(mw, H - mh - ch, mw + cw, H - mh - ch); 23 | dc.drawLine(mw, H - mh - ch * 0.65, mw + cw, H - mh - ch * 0.65); 24 | dc.drawLine(mw, H - mh - ch * 0.3, mw + cw, H - mh - ch * 0.3); 25 | dc.drawText(mw + cw + 3, H - mh - ch - H / 26, Graphics.FONT_XTINY, "10", Graphics.TEXT_JUSTIFY_LEFT); 26 | dc.drawText(mw + cw + 3, H - mh - ch * 0.65 - H / 26, Graphics.FONT_XTINY, "6", Graphics.TEXT_JUSTIFY_LEFT); 27 | dc.drawText(mw + cw + 3, H - mh - ch * 0.3 - H / 26, Graphics.FONT_XTINY, "3", Graphics.TEXT_JUSTIFY_LEFT); 28 | 29 | // UV chart 30 | var len = data.hourlyEntries() > 25 ? 25 : data.hourlyEntries(); 31 | var hour = Time.Gregorian.info(Time.now(), Time.FORMAT_SHORT).hour; 32 | for (var i = 0; i < len; i++) { 33 | var x = mw + (cw / len * i); 34 | var h = Math.floor(data.uv[i] < 0.3 ? ch / 20 : data.uv[i] * ch / 10); 35 | 36 | colorUv(dc, data.uv[i]); 37 | dc.drawRectangle(x, H - mh - h, cw / len * 0.9, h); 38 | 39 | if (i % 4 == 0) { 40 | dc.drawText(x, H - mh, Graphics.FONT_XTINY, ((hour + i) % 24).format("%02d"), Graphics.TEXT_JUSTIFY_CENTER); 41 | } 42 | } 43 | 44 | // UV Text 45 | colorUv(dc, data.uv[0]); 46 | dc.drawText(mw, mh * 0.8, Graphics.FONT_NUMBER_MEDIUM, data.uv[0].format("%d"), Graphics.TEXT_JUSTIFY_LEFT); 47 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 48 | dc.drawText(mw, mh * 1.85, Graphics.FONT_XTINY, 49 | data.uv[0] >= 11 ? "Extreme" : data.uv[0] >= 8 ? "Strong" : data.uv[0] >= 6 ? "High" : 50 | data.uv[0] >= 3 ? "Medium" : data.uv[0] >= 0.3 ? "Low" : "No UV", Graphics.TEXT_JUSTIFY_LEFT); 51 | 52 | // yep 53 | var temp = data.temperatures[0]; 54 | var hum = data.humidity[0]; 55 | var dew = 243.04 * (Math.ln(hum / 100) + ((17.625 * temp) / (243.04 + temp))) / (17.625 - Math.ln(hum / 100) - ((17.625 * temp) / (243.04 + temp))); 56 | 57 | // Humidity Text 58 | dc.drawText(mw + cw, mh, Graphics.FONT_XTINY, "Humidity " + hum.format("%d") + "%", Graphics.TEXT_JUSTIFY_RIGHT); 59 | dc.drawText(mw + cw, mh + FONT_HEIGHT, Graphics.FONT_XTINY, "Dew " + degrees(dew, data.tempUnits) + "°", Graphics.TEXT_JUSTIFY_RIGHT); 60 | dc.setColor(dew < 15 ? Graphics.COLOR_GREEN : 61 | dew < 20 ? Graphics.COLOR_YELLOW : 62 | dew < 24 ? Graphics.COLOR_ORANGE : Graphics.COLOR_RED, Graphics.COLOR_TRANSPARENT); 63 | dc.drawText(mw + cw, mh + FONT_HEIGHT * 2, Graphics.FONT_XTINY, 64 | dew < 15 ? "Pleasant" : dew < 20 ? "Sticky" : dew < 24 ? "Uncomfortable" : "Unbearable", Graphics.TEXT_JUSTIFY_RIGHT); 65 | 66 | // Page Indicator 67 | drawIndicator(dc, 5); 68 | } 69 | 70 | function colorUv(dc, uv) { 71 | dc.setColor(uv >= 11 ? Graphics.COLOR_PURPLE : 72 | uv >= 8 ? Graphics.COLOR_RED : 73 | uv >= 6 ? Graphics.COLOR_ORANGE : 74 | uv >= 3 ? Graphics.COLOR_YELLOW : 75 | uv >= 0.3 ? Graphics.COLOR_GREEN : Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /source/view/WaterView.mc: -------------------------------------------------------------------------------- 1 | import Toybox.Graphics; 2 | import Toybox.WatchUi; 3 | import Toybox.Lang; 4 | import Toybox.Time; 5 | 6 | class WaterView extends BaseView { 7 | 8 | function initialize() { 9 | BaseView.initialize(1); 10 | } 11 | 12 | // Update the view 13 | function onDraw(dc as Dc, W as Number, H as Number, FONT_HEIGHT as Number) as Void { 14 | var mw = W * 0.085; // Margin Width 15 | var mh = H * 0.22; // Margin Height 16 | 17 | drawHeader(dc, W, H, "Water"); 18 | if (data.waterNames.size() == 0) { 19 | dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT); 20 | dc.drawText(W / 2, H / 2.5, Graphics.FONT_TINY, "No Nearby Water\nTemperatures Found.", Graphics.TEXT_JUSTIFY_CENTER); 21 | } else { 22 | for (var i = 0; i < data.waterNames.size() && i < 3; i++) { 23 | var offset = mh + i * (H / 4.5); 24 | var small = data.waterNames[i].length() > 13; 25 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 26 | dc.drawText(mw, offset, small ? Graphics.FONT_XTINY : Graphics.FONT_TINY, data.waterNames[i], Graphics.TEXT_JUSTIFY_LEFT); 27 | 28 | dc.setColor(Graphics.COLOR_DK_GRAY, Graphics.COLOR_TRANSPARENT); 29 | dc.drawText(W - mw - H / 4.25, offset + H / (small ? 11 : 10), Graphics.FONT_XTINY, 30 | (data.waterDistances[i] / 1000.0).format(data.waterDistances[i] > 10000 ? "%d" : "%.1f") + "km", Graphics.TEXT_JUSTIFY_RIGHT); 31 | 32 | var time = Time.Gregorian.info(new Moment(data.waterTimestamps[i]), Time.FORMAT_MEDIUM); 33 | dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT); 34 | dc.drawText(mw, offset + H / (small ? 11 : 10), Graphics.FONT_XTINY, 35 | time.month + " " + time.day.format("%02d") + " " + time.hour.format("%02d") + ":" + time.min.format("%02d"), Graphics.TEXT_JUSTIFY_LEFT); 36 | 37 | 38 | dc.setColor(Graphics.COLOR_RED, Graphics.COLOR_TRANSPARENT); 39 | dc.drawText(W - mw * 0.8, offset + H / 40, Graphics.FONT_SMALL, 40 | (data.tempUnits == 1 ? degrees(data.waterTemperatures[i], data.tempUnits) : data.waterTemperatures[i].format("%.1f")) + "°", Graphics.TEXT_JUSTIFY_RIGHT); 41 | } 42 | } 43 | 44 | // Page Indicator 45 | drawIndicator(dc, 7); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /source/view/special/AppGlanceView.mc: -------------------------------------------------------------------------------- 1 | import Toybox.Graphics; 2 | import Toybox.WatchUi; 3 | import Toybox.Lang; 4 | 5 | (:glance) 6 | class AppGlanceView extends WatchUi.GlanceView { 7 | 8 | private var data as BaseData; 9 | 10 | function initialize(data as BaseData) { 11 | GlanceView.initialize(); 12 | 13 | System.println("Init Glance"); 14 | self.data = data; 15 | } 16 | 17 | // Update the view 18 | function onUpdate(dc as Dc) as Void { 19 | var W = dc.getWidth(); 20 | var H = dc.getHeight(); 21 | var loaded = data.temperatures.size() > 0; 22 | 23 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 24 | dc.drawText(0, H * 0.15, Graphics.FONT_GLANCE, data.location, Graphics.TEXT_JUSTIFY_LEFT); 25 | dc.drawText(0, H * 0.4, Graphics.FONT_GLANCE_NUMBER, (loaded ? degrees(data.temperatures[0], data.tempUnits) : "--") + "°", Graphics.TEXT_JUSTIFY_LEFT); 26 | 27 | dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT); 28 | dc.fillPolygon(generateArrow([ W * 0.22, H * 0.52 ], loaded ? data.windDirections[0] + 180 : 0, W / 8)); 29 | dc.drawText(W * 0.37, H * 0.4, Graphics.FONT_GLANCE_NUMBER, loaded ? wind(data.windSpeeds[0], data.windUnits) : "--", Graphics.TEXT_JUSTIFY_LEFT); 30 | 31 | var mw = W * 0.55; // Rain chart width margin (left) 32 | var mh = H / 5.5; // Rain chart height margin (bottom) 33 | var lh = (H - mh * 3) / 3; // Rain chart line height 34 | var chartWidth = W - mw; 35 | 36 | var rainPrimary = data.nowRainfall != null && data.nowRainfall.size() > 0; 37 | var rainBackup = data.rainfall.size() >= 6; 38 | 39 | if (rainPrimary || rainBackup) { 40 | var rainCount = rainPrimary ? data.nowRainfall.size() : rainBackup ? 6 : 0; 41 | var rainPolygon = new [rainCount + 2]; 42 | rainPolygon[0] = [ mw + chartWidth, H - mh ]; 43 | rainPolygon[1] = [ mw, H - mh ]; 44 | for (var i = 0; i < rainCount; i++) { 45 | var r = rainPrimary ? data.nowRainfall[i] : data.rainfall[i]; 46 | rainPolygon[i + 2] = [ mw + (chartWidth / (rainCount - 1)) * i, H - mh - (r <= 0 ? 0 : r > 5 ? lh * 3 : ((r + 0.3) * lh * 0.6)) ]; 47 | } 48 | 49 | dc.setColor(Graphics.COLOR_BLUE, Graphics.COLOR_TRANSPARENT); 50 | dc.fillPolygon(rainPolygon); 51 | 52 | if (!rainPrimary && data.nowRainfall != null) { 53 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 54 | dc.drawText(W, H * 0.2, Graphics.FONT_GLANCE, "6hr", Graphics.TEXT_JUSTIFY_RIGHT); 55 | } 56 | } else { 57 | dc.drawText(W * 0.8, H * 0.3, Graphics.FONT_GLANCE, "Rain\nUnavailable", Graphics.TEXT_JUSTIFY_CENTER); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /source/view/special/HintDelegate.mc: -------------------------------------------------------------------------------- 1 | import Toybox.Lang; 2 | import Toybox.WatchUi; 3 | 4 | class HintDelegate extends WatchUi.BehaviorDelegate { 5 | 6 | private var buttons; 7 | 8 | function initialize(buttons) { 9 | BehaviorDelegate.initialize(); 10 | self.buttons = buttons; 11 | } 12 | 13 | public function onSelect() as Boolean { 14 | if (buttons.indexOf(0) != -1) { 15 | WatchUi.popView(WatchUi.SLIDE_BLINK); 16 | return true; 17 | } 18 | 19 | return false; 20 | } 21 | 22 | //! Handle going to the previous view 23 | //! @return true if handled, false otherwise 24 | public function onPreviousPage() as Boolean { 25 | if (buttons.indexOf(1) != -1) { 26 | WatchUi.popView(WatchUi.SLIDE_BLINK); 27 | return true; 28 | } 29 | return false; 30 | } 31 | } -------------------------------------------------------------------------------- /source/view/special/HintView.mc: -------------------------------------------------------------------------------- 1 | import Toybox.Graphics; 2 | import Toybox.WatchUi; 3 | import Toybox.Lang; 4 | 5 | class HintView extends WatchUi.View { 6 | 7 | private var text; 8 | private var buttons; 9 | 10 | function initialize(text, buttons) { 11 | View.initialize(); 12 | self.text = text; 13 | self.buttons = buttons; 14 | } 15 | 16 | // Update the view 17 | function onUpdate(dc as Dc) as Void { 18 | // Call the parent onUpdate function to redraw the layout 19 | View.onUpdate(dc); 20 | 21 | var W = dc.getWidth(); 22 | var H = dc.getHeight(); 23 | 24 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 25 | dc.drawText(W / 2, 26 | H / 8, Graphics.FONT_LARGE, "HINT", Graphics.TEXT_JUSTIFY_CENTER); 27 | dc.setColor(Graphics.COLOR_LT_GRAY, Graphics.COLOR_TRANSPARENT); 28 | dc.drawText(W / 2, H / 2.5, Graphics.FONT_TINY, text, Graphics.TEXT_JUSTIFY_CENTER); 29 | 30 | if (buttons.indexOf(0) != -1) { 31 | BaseView.drawDots(dc, 5, -1, 5.73, 0.06); 32 | } 33 | /*if (buttons.indexOf(1) != -1) { 34 | BaseView.drawDots(dc, 5, -1, 3.14, 0.06); 35 | }*/ 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /source/view/special/RegisterDelegate.mc: -------------------------------------------------------------------------------- 1 | import Toybox.Lang; 2 | import Toybox.WatchUi; 3 | 4 | class RegisterDelegate extends WatchUi.BehaviorDelegate { 5 | 6 | function initialize() { 7 | BehaviorDelegate.initialize(); 8 | } 9 | 10 | public function onSelect() as Boolean { 11 | WatchUi.popView(WatchUi.SLIDE_BLINK); 12 | return true; 13 | } 14 | } -------------------------------------------------------------------------------- /source/view/special/RegisterView.mc: -------------------------------------------------------------------------------- 1 | import Toybox.Graphics; 2 | import Toybox.WatchUi; 3 | import Toybox.Lang; 4 | 5 | class RegisterView extends WatchUi.View { 6 | 7 | private var days; 8 | 9 | function initialize(days) { 10 | View.initialize(); 11 | self.days = days; 12 | } 13 | 14 | // Update the view 15 | function onUpdate(dc as Dc) as Void { 16 | // Call the parent onUpdate function to redraw the layout 17 | View.onUpdate(dc); 18 | 19 | var W = dc.getWidth(); 20 | var H = dc.getHeight(); 21 | 22 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 23 | dc.drawText(W / 2, H / 12, Graphics.FONT_LARGE, "TRIAL", Graphics.TEXT_JUSTIFY_CENTER); 24 | dc.setColor(data.blocked ? Graphics.COLOR_RED : Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 25 | dc.drawText(W / 2, H / 4.8, Graphics.FONT_TINY, days + " Days Left", Graphics.TEXT_JUSTIFY_CENTER); 26 | 27 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 28 | dc.drawText(W / 2, H / 2.55, Graphics.FONT_TINY, "To Activate: Go to", Graphics.TEXT_JUSTIFY_CENTER); 29 | dc.setColor(Graphics.COLOR_BLUE, Graphics.COLOR_TRANSPARENT); 30 | dc.drawText(W / 2, H / 2, Graphics.FONT_MEDIUM, "rainy.bleach.dev", Graphics.TEXT_JUSTIFY_CENTER); 31 | dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT); 32 | dc.drawText(W / 2, H / 1.55, Graphics.FONT_TINY, "and enter code", Graphics.TEXT_JUSTIFY_CENTER); 33 | dc.setColor(Graphics.COLOR_BLUE, Graphics.COLOR_TRANSPARENT); 34 | dc.drawText(W / 2, H / 1.35, Graphics.FONT_MEDIUM, data.DEVICE_ID, Graphics.TEXT_JUSTIFY_CENTER); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /source/view/special/SettingsDelegate.mc: -------------------------------------------------------------------------------- 1 | import Toybox.Application; 2 | import Toybox.Lang; 3 | import Toybox.WatchUi; 4 | 5 | class SettingsDelegate extends Menu2InputDelegate { 6 | 7 | public static var WIND_UNITS = [ "m/s", "km/h", "mph", "Bft.", "Knots" ]; 8 | 9 | function initialize() { 10 | Menu2InputDelegate.initialize(); 11 | } 12 | 13 | function onSelect(item) { 14 | System.println(item.getId()); 15 | if (item.getId() == 0) { 16 | if (!(WatchUi has :TextPicker)) { 17 | item.setSubLabel("See Mobile Settings!"); 18 | return; 19 | } 20 | 21 | if (data.autoLocation) { 22 | WatchUi.pushView( 23 | new TextPicker(""), 24 | new SpellSearchDelegate(), 25 | WatchUi.SLIDE_LEFT 26 | ); 27 | } else { 28 | item.setSubLabel("Automatic (GPS)"); 29 | data.autoLocation = true; 30 | Properties.setValue("autoLocation", data.autoLocation); 31 | } 32 | } else if (item.getId() == 1) { 33 | data.pageOrder = !data.pageOrder; 34 | Properties.setValue("pageOrder", data.pageOrder); 35 | item.setSubLabel(data.pageOrder ? "Yes" : "No"); 36 | } else if (item.getId() == 2) { 37 | data.tempUnits = (data.tempUnits + 1) % 2; 38 | Properties.setValue("tempUnits", data.tempUnits); 39 | item.setSubLabel(data.tempUnits == 0 ? "Celsius" : "Fahrenheit"); 40 | } else if (item.getId() == 3) { 41 | data.windUnits = (data.windUnits + 1) % WIND_UNITS.size(); 42 | Properties.setValue("windUnits", data.windUnits); 43 | item.setSubLabel(WIND_UNITS[data.windUnits]); 44 | } else if (item.getId() == 4) { 45 | data.update(data.position); 46 | WatchUi.popView(WatchUi.SLIDE_BLINK); 47 | } 48 | 49 | } 50 | } -------------------------------------------------------------------------------- /source/view/special/SpellSearchDelegate.mc: -------------------------------------------------------------------------------- 1 | import Toybox.Application; 2 | import Toybox.Lang; 3 | import Toybox.WatchUi; 4 | 5 | class SpellSearchDelegate extends TextPickerDelegate { 6 | 7 | function initialize() { 8 | TextPickerDelegate.initialize(); 9 | } 10 | 11 | function onTextEntered(text, changed) { 12 | if (changed) { 13 | WatchUi.popView(WatchUi.SLIDE_IMMEDIATE); 14 | WatchUi.popView(WatchUi.SLIDE_IMMEDIATE); 15 | data.enterText(text); 16 | WatchUi.pushView(new View(), null, WatchUi.SLIDE_IMMEDIATE); // Not good but onTextEntered pops the last view after returning 17 | return true; 18 | } 19 | 20 | return false; 21 | } 22 | } 23 | 24 | class SpellListDelegate extends Menu2InputDelegate { 25 | 26 | private var dt as Dictionary?; 27 | 28 | function initialize(dt as Dictionary?) { 29 | Menu2InputDelegate.initialize(); 30 | self.dt = dt; 31 | } 32 | 33 | function onSelect(item) { 34 | data.autoLocation = false; 35 | Properties.setValue("autoLocation", data.autoLocation); 36 | data.update(dt[item.getId()]["position"]); 37 | WatchUi.popView(WatchUi.SLIDE_BLINK); 38 | } 39 | } --------------------------------------------------------------------------------