├── .classpath ├── .gitignore ├── .project ├── AndroidManifest.xml ├── LICENSE_LGPL_V2 ├── README.md ├── extras └── release.py ├── libs └── melautils.jar ├── lint.xml ├── project.properties ├── res ├── values-it │ └── strings.xml ├── values-pt │ └── strings.xml ├── values │ └── strings.xml └── xml │ └── preferences.xml └── src └── edu └── mit └── mobile └── android └── appupdater ├── AppUpdateChecker.java ├── OnAppUpdateListener.java └── OnUpdateDialog.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | gen/ 3 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | AppUpdateChecker 4 | 5 | 6 | 7 | 8 | 9 | com.android.ide.eclipse.adt.ResourceManagerBuilder 10 | 11 | 12 | 13 | 14 | com.android.ide.eclipse.adt.PreCompilerBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.jdt.core.javabuilder 20 | 21 | 22 | 23 | 24 | com.android.ide.eclipse.adt.ApkBuilder 25 | 26 | 27 | 28 | 29 | 30 | com.android.ide.eclipse.adt.AndroidNature 31 | org.eclipse.jdt.core.javanature 32 | 33 | 34 | -------------------------------------------------------------------------------- /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LICENSE_LGPL_V2: -------------------------------------------------------------------------------- 1 | GNU LIBRARY GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1991 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor 6 | Boston, MA 02110-1301, USA. 7 | Everyone is permitted to copy and distribute verbatim copies 8 | of this license document, but changing it is not allowed. 9 | 10 | [This is the first released version of the library GPL. It is 11 | numbered 2 because it goes with version 2 of the ordinary GPL.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Library General Public License, applies to some 21 | specially designated Free Software Foundation software, and to any 22 | other libraries whose authors decide to use it. You can use it for 23 | your libraries, too. 24 | 25 | When we speak of free software, we are referring to freedom, not 26 | price. Our General Public Licenses are designed to make sure that you 27 | have the freedom to distribute copies of free software (and charge for 28 | this service if you wish), that you receive source code or can get it 29 | if you want it, that you can change the software or use pieces of it 30 | in new free programs; and that you know you can do these things. 31 | 32 | To protect your rights, we need to make restrictions that forbid 33 | anyone to deny you these rights or to ask you to surrender the rights. 34 | These restrictions translate to certain responsibilities for you if 35 | you distribute copies of the library, or if you modify it. 36 | 37 | For example, if you distribute copies of the library, whether gratis 38 | or for a fee, you must give the recipients all the rights that we gave 39 | you. You must make sure that they, too, receive or can get the source 40 | code. If you link a program with the library, you must provide 41 | complete object files to the recipients so that they can relink them 42 | with the library, after making changes to the library and recompiling 43 | it. And you must show them these terms so they know their rights. 44 | 45 | Our method of protecting your rights has two steps: (1) copyright 46 | the library, and (2) offer you this license which gives you legal 47 | permission to copy, distribute and/or modify the library. 48 | 49 | Also, for each distributor's protection, we want to make certain 50 | that everyone understands that there is no warranty for this free 51 | library. If the library is modified by someone else and passed on, we 52 | want its recipients to know that what they have is not the original 53 | version, so that any problems introduced by others will not reflect on 54 | the original authors' reputations. 55 | 56 | Finally, any free program is threatened constantly by software 57 | patents. We wish to avoid the danger that companies distributing free 58 | software will individually obtain patent licenses, thus in effect 59 | transforming the program into proprietary software. To prevent this, 60 | we have made it clear that any patent must be licensed for everyone's 61 | free use or not licensed at all. 62 | 63 | Most GNU software, including some libraries, is covered by the ordinary 64 | GNU General Public License, which was designed for utility programs. This 65 | license, the GNU Library General Public License, applies to certain 66 | designated libraries. This license is quite different from the ordinary 67 | one; be sure to read it in full, and don't assume that anything in it is 68 | the same as in the ordinary license. 69 | 70 | The reason we have a separate public license for some libraries is that 71 | they blur the distinction we usually make between modifying or adding to a 72 | program and simply using it. Linking a program with a library, without 73 | changing the library, is in some sense simply using the library, and is 74 | analogous to running a utility program or application program. However, in 75 | a textual and legal sense, the linked executable is a combined work, a 76 | derivative of the original library, and the ordinary General Public License 77 | treats it as such. 78 | 79 | Because of this blurred distinction, using the ordinary General 80 | Public License for libraries did not effectively promote software 81 | sharing, because most developers did not use the libraries. We 82 | concluded that weaker conditions might promote sharing better. 83 | 84 | However, unrestricted linking of non-free programs would deprive the 85 | users of those programs of all benefit from the free status of the 86 | libraries themselves. This Library General Public License is intended to 87 | permit developers of non-free programs to use free libraries, while 88 | preserving your freedom as a user of such programs to change the free 89 | libraries that are incorporated in them. (We have not seen how to achieve 90 | this as regards changes in header files, but we have achieved it as regards 91 | changes in the actual functions of the Library.) The hope is that this 92 | will lead to faster development of free libraries. 93 | 94 | The precise terms and conditions for copying, distribution and 95 | modification follow. Pay close attention to the difference between a 96 | "work based on the library" and a "work that uses the library". The 97 | former contains code derived from the library, while the latter only 98 | works together with the library. 99 | 100 | Note that it is possible for a library to be covered by the ordinary 101 | General Public License rather than by this special one. 102 | 103 | GNU LIBRARY GENERAL PUBLIC LICENSE 104 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 105 | 106 | 0. This License Agreement applies to any software library which 107 | contains a notice placed by the copyright holder or other authorized 108 | party saying it may be distributed under the terms of this Library 109 | General Public License (also called "this License"). Each licensee is 110 | addressed as "you". 111 | 112 | A "library" means a collection of software functions and/or data 113 | prepared so as to be conveniently linked with application programs 114 | (which use some of those functions and data) to form executables. 115 | 116 | The "Library", below, refers to any such software library or work 117 | which has been distributed under these terms. A "work based on the 118 | Library" means either the Library or any derivative work under 119 | copyright law: that is to say, a work containing the Library or a 120 | portion of it, either verbatim or with modifications and/or translated 121 | straightforwardly into another language. (Hereinafter, translation is 122 | included without limitation in the term "modification".) 123 | 124 | "Source code" for a work means the preferred form of the work for 125 | making modifications to it. For a library, complete source code means 126 | all the source code for all modules it contains, plus any associated 127 | interface definition files, plus the scripts used to control compilation 128 | and installation of the library. 129 | 130 | Activities other than copying, distribution and modification are not 131 | covered by this License; they are outside its scope. The act of 132 | running a program using the Library is not restricted, and output from 133 | such a program is covered only if its contents constitute a work based 134 | on the Library (independent of the use of the Library in a tool for 135 | writing it). Whether that is true depends on what the Library does 136 | and what the program that uses the Library does. 137 | 138 | 1. You may copy and distribute verbatim copies of the Library's 139 | complete source code as you receive it, in any medium, provided that 140 | you conspicuously and appropriately publish on each copy an 141 | appropriate copyright notice and disclaimer of warranty; keep intact 142 | all the notices that refer to this License and to the absence of any 143 | warranty; and distribute a copy of this License along with the 144 | Library. 145 | 146 | You may charge a fee for the physical act of transferring a copy, 147 | and you may at your option offer warranty protection in exchange for a 148 | fee. 149 | 150 | 2. You may modify your copy or copies of the Library or any portion 151 | of it, thus forming a work based on the Library, and copy and 152 | distribute such modifications or work under the terms of Section 1 153 | above, provided that you also meet all of these conditions: 154 | 155 | a) The modified work must itself be a software library. 156 | 157 | b) You must cause the files modified to carry prominent notices 158 | stating that you changed the files and the date of any change. 159 | 160 | c) You must cause the whole of the work to be licensed at no 161 | charge to all third parties under the terms of this License. 162 | 163 | d) If a facility in the modified Library refers to a function or a 164 | table of data to be supplied by an application program that uses 165 | the facility, other than as an argument passed when the facility 166 | is invoked, then you must make a good faith effort to ensure that, 167 | in the event an application does not supply such function or 168 | table, the facility still operates, and performs whatever part of 169 | its purpose remains meaningful. 170 | 171 | (For example, a function in a library to compute square roots has 172 | a purpose that is entirely well-defined independent of the 173 | application. Therefore, Subsection 2d requires that any 174 | application-supplied function or table used by this function must 175 | be optional: if the application does not supply it, the square 176 | root function must still compute square roots.) 177 | 178 | These requirements apply to the modified work as a whole. If 179 | identifiable sections of that work are not derived from the Library, 180 | and can be reasonably considered independent and separate works in 181 | themselves, then this License, and its terms, do not apply to those 182 | sections when you distribute them as separate works. But when you 183 | distribute the same sections as part of a whole which is a work based 184 | on the Library, the distribution of the whole must be on the terms of 185 | this License, whose permissions for other licensees extend to the 186 | entire whole, and thus to each and every part regardless of who wrote 187 | it. 188 | 189 | Thus, it is not the intent of this section to claim rights or contest 190 | your rights to work written entirely by you; rather, the intent is to 191 | exercise the right to control the distribution of derivative or 192 | collective works based on the Library. 193 | 194 | In addition, mere aggregation of another work not based on the Library 195 | with the Library (or with a work based on the Library) on a volume of 196 | a storage or distribution medium does not bring the other work under 197 | the scope of this License. 198 | 199 | 3. You may opt to apply the terms of the ordinary GNU General Public 200 | License instead of this License to a given copy of the Library. To do 201 | this, you must alter all the notices that refer to this License, so 202 | that they refer to the ordinary GNU General Public License, version 2, 203 | instead of to this License. (If a newer version than version 2 of the 204 | ordinary GNU General Public License has appeared, then you can specify 205 | that version instead if you wish.) Do not make any other change in 206 | these notices. 207 | 208 | Once this change is made in a given copy, it is irreversible for 209 | that copy, so the ordinary GNU General Public License applies to all 210 | subsequent copies and derivative works made from that copy. 211 | 212 | This option is useful when you wish to copy part of the code of 213 | the Library into a program that is not a library. 214 | 215 | 4. You may copy and distribute the Library (or a portion or 216 | derivative of it, under Section 2) in object code or executable form 217 | under the terms of Sections 1 and 2 above provided that you accompany 218 | it with the complete corresponding machine-readable source code, which 219 | must be distributed under the terms of Sections 1 and 2 above on a 220 | medium customarily used for software interchange. 221 | 222 | If distribution of object code is made by offering access to copy 223 | from a designated place, then offering equivalent access to copy the 224 | source code from the same place satisfies the requirement to 225 | distribute the source code, even though third parties are not 226 | compelled to copy the source along with the object code. 227 | 228 | 5. A program that contains no derivative of any portion of the 229 | Library, but is designed to work with the Library by being compiled or 230 | linked with it, is called a "work that uses the Library". Such a 231 | work, in isolation, is not a derivative work of the Library, and 232 | therefore falls outside the scope of this License. 233 | 234 | However, linking a "work that uses the Library" with the Library 235 | creates an executable that is a derivative of the Library (because it 236 | contains portions of the Library), rather than a "work that uses the 237 | library". The executable is therefore covered by this License. 238 | Section 6 states terms for distribution of such executables. 239 | 240 | When a "work that uses the Library" uses material from a header file 241 | that is part of the Library, the object code for the work may be a 242 | derivative work of the Library even though the source code is not. 243 | Whether this is true is especially significant if the work can be 244 | linked without the Library, or if the work is itself a library. The 245 | threshold for this to be true is not precisely defined by law. 246 | 247 | If such an object file uses only numerical parameters, data 248 | structure layouts and accessors, and small macros and small inline 249 | functions (ten lines or less in length), then the use of the object 250 | file is unrestricted, regardless of whether it is legally a derivative 251 | work. (Executables containing this object code plus portions of the 252 | Library will still fall under Section 6.) 253 | 254 | Otherwise, if the work is a derivative of the Library, you may 255 | distribute the object code for the work under the terms of Section 6. 256 | Any executables containing that work also fall under Section 6, 257 | whether or not they are linked directly with the Library itself. 258 | 259 | 6. As an exception to the Sections above, you may also compile or 260 | link a "work that uses the Library" with the Library to produce a 261 | work containing portions of the Library, and distribute that work 262 | under terms of your choice, provided that the terms permit 263 | modification of the work for the customer's own use and reverse 264 | engineering for debugging such modifications. 265 | 266 | You must give prominent notice with each copy of the work that the 267 | Library is used in it and that the Library and its use are covered by 268 | this License. You must supply a copy of this License. If the work 269 | during execution displays copyright notices, you must include the 270 | copyright notice for the Library among them, as well as a reference 271 | directing the user to the copy of this License. Also, you must do one 272 | of these things: 273 | 274 | a) Accompany the work with the complete corresponding 275 | machine-readable source code for the Library including whatever 276 | changes were used in the work (which must be distributed under 277 | Sections 1 and 2 above); and, if the work is an executable linked 278 | with the Library, with the complete machine-readable "work that 279 | uses the Library", as object code and/or source code, so that the 280 | user can modify the Library and then relink to produce a modified 281 | executable containing the modified Library. (It is understood 282 | that the user who changes the contents of definitions files in the 283 | Library will not necessarily be able to recompile the application 284 | to use the modified definitions.) 285 | 286 | b) Accompany the work with a written offer, valid for at 287 | least three years, to give the same user the materials 288 | specified in Subsection 6a, above, for a charge no more 289 | than the cost of performing this distribution. 290 | 291 | c) If distribution of the work is made by offering access to copy 292 | from a designated place, offer equivalent access to copy the above 293 | specified materials from the same place. 294 | 295 | d) Verify that the user has already received a copy of these 296 | materials or that you have already sent this user a copy. 297 | 298 | For an executable, the required form of the "work that uses the 299 | Library" must include any data and utility programs needed for 300 | reproducing the executable from it. However, as a special exception, 301 | the source code distributed need not include anything that is normally 302 | distributed (in either source or binary form) with the major 303 | components (compiler, kernel, and so on) of the operating system on 304 | which the executable runs, unless that component itself accompanies 305 | the executable. 306 | 307 | It may happen that this requirement contradicts the license 308 | restrictions of other proprietary libraries that do not normally 309 | accompany the operating system. Such a contradiction means you cannot 310 | use both them and the Library together in an executable that you 311 | distribute. 312 | 313 | 7. You may place library facilities that are a work based on the 314 | Library side-by-side in a single library together with other library 315 | facilities not covered by this License, and distribute such a combined 316 | library, provided that the separate distribution of the work based on 317 | the Library and of the other library facilities is otherwise 318 | permitted, and provided that you do these two things: 319 | 320 | a) Accompany the combined library with a copy of the same work 321 | based on the Library, uncombined with any other library 322 | facilities. This must be distributed under the terms of the 323 | Sections above. 324 | 325 | b) Give prominent notice with the combined library of the fact 326 | that part of it is a work based on the Library, and explaining 327 | where to find the accompanying uncombined form of the same work. 328 | 329 | 8. You may not copy, modify, sublicense, link with, or distribute 330 | the Library except as expressly provided under this License. Any 331 | attempt otherwise to copy, modify, sublicense, link with, or 332 | distribute the Library is void, and will automatically terminate your 333 | rights under this License. However, parties who have received copies, 334 | or rights, from you under this License will not have their licenses 335 | terminated so long as such parties remain in full compliance. 336 | 337 | 9. You are not required to accept this License, since you have not 338 | signed it. However, nothing else grants you permission to modify or 339 | distribute the Library or its derivative works. These actions are 340 | prohibited by law if you do not accept this License. Therefore, by 341 | modifying or distributing the Library (or any work based on the 342 | Library), you indicate your acceptance of this License to do so, and 343 | all its terms and conditions for copying, distributing or modifying 344 | the Library or works based on it. 345 | 346 | 10. Each time you redistribute the Library (or any work based on the 347 | Library), the recipient automatically receives a license from the 348 | original licensor to copy, distribute, link with or modify the Library 349 | subject to these terms and conditions. You may not impose any further 350 | restrictions on the recipients' exercise of the rights granted herein. 351 | You are not responsible for enforcing compliance by third parties to 352 | this License. 353 | 354 | 11. If, as a consequence of a court judgment or allegation of patent 355 | infringement or for any other reason (not limited to patent issues), 356 | conditions are imposed on you (whether by court order, agreement or 357 | otherwise) that contradict the conditions of this License, they do not 358 | excuse you from the conditions of this License. If you cannot 359 | distribute so as to satisfy simultaneously your obligations under this 360 | License and any other pertinent obligations, then as a consequence you 361 | may not distribute the Library at all. For example, if a patent 362 | license would not permit royalty-free redistribution of the Library by 363 | all those who receive copies directly or indirectly through you, then 364 | the only way you could satisfy both it and this License would be to 365 | refrain entirely from distribution of the Library. 366 | 367 | If any portion of this section is held invalid or unenforceable under any 368 | particular circumstance, the balance of the section is intended to apply, 369 | and the section as a whole is intended to apply in other circumstances. 370 | 371 | It is not the purpose of this section to induce you to infringe any 372 | patents or other property right claims or to contest validity of any 373 | such claims; this section has the sole purpose of protecting the 374 | integrity of the free software distribution system which is 375 | implemented by public license practices. Many people have made 376 | generous contributions to the wide range of software distributed 377 | through that system in reliance on consistent application of that 378 | system; it is up to the author/donor to decide if he or she is willing 379 | to distribute software through any other system and a licensee cannot 380 | impose that choice. 381 | 382 | This section is intended to make thoroughly clear what is believed to 383 | be a consequence of the rest of this License. 384 | 385 | 12. If the distribution and/or use of the Library is restricted in 386 | certain countries either by patents or by copyrighted interfaces, the 387 | original copyright holder who places the Library under this License may add 388 | an explicit geographical distribution limitation excluding those countries, 389 | so that distribution is permitted only in or among countries not thus 390 | excluded. In such case, this License incorporates the limitation as if 391 | written in the body of this License. 392 | 393 | 13. The Free Software Foundation may publish revised and/or new 394 | versions of the Library General Public License from time to time. 395 | Such new versions will be similar in spirit to the present version, 396 | but may differ in detail to address new problems or concerns. 397 | 398 | Each version is given a distinguishing version number. If the Library 399 | specifies a version number of this License which applies to it and 400 | "any later version", you have the option of following the terms and 401 | conditions either of that version or of any later version published by 402 | the Free Software Foundation. If the Library does not specify a 403 | license version number, you may choose any version ever published by 404 | the Free Software Foundation. 405 | 406 | 14. If you wish to incorporate parts of the Library into other free 407 | programs whose distribution conditions are incompatible with these, 408 | write to the author to ask for permission. For software which is 409 | copyrighted by the Free Software Foundation, write to the Free 410 | Software Foundation; we sometimes make exceptions for this. Our 411 | decision will be guided by the two goals of preserving the free status 412 | of all derivatives of our free software and of promoting the sharing 413 | and reuse of software generally. 414 | 415 | NO WARRANTY 416 | 417 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 418 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 419 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 420 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 421 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 422 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 423 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 424 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 425 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 426 | 427 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 428 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 429 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 430 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 431 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 432 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 433 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 434 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 435 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 436 | DAMAGES. 437 | 438 | END OF TERMS AND CONDITIONS 439 | 440 | How to Apply These Terms to Your New Libraries 441 | 442 | If you develop a new library, and you want it to be of the greatest 443 | possible use to the public, we recommend making it free software that 444 | everyone can redistribute and change. You can do so by permitting 445 | redistribution under these terms (or, alternatively, under the terms of the 446 | ordinary General Public License). 447 | 448 | To apply these terms, attach the following notices to the library. It is 449 | safest to attach them to the start of each source file to most effectively 450 | convey the exclusion of warranty; and each file should have at least the 451 | "copyright" line and a pointer to where the full notice is found. 452 | 453 | 454 | Copyright (C) 455 | 456 | This library is free software; you can redistribute it and/or 457 | modify it under the terms of the GNU Lesser General Public 458 | License as published by the Free Software Foundation; either 459 | version 2 of the License, or (at your option) any later version. 460 | 461 | This library is distributed in the hope that it will be useful, 462 | but WITHOUT ANY WARRANTY; without even the implied warranty of 463 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 464 | Lesser General Public License for more details. 465 | 466 | You should have received a copy of the GNU Lesser General Public 467 | License along with this library; if not, write to the Free Software 468 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 469 | 470 | Also add information on how to contact you by electronic and paper mail. 471 | 472 | You should also get your employer (if you work as a programmer) or your 473 | school, if any, to sign a "copyright disclaimer" for the library, if 474 | necessary. Here is a sample; alter the names: 475 | 476 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 477 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 478 | 479 | , 1 April 1990 480 | Ty Coon, President of Vice 481 | 482 | That's all there is to it! 483 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | App Update Checker 2 | ================== 3 | 4 | A simple non-Market way to keep your app updated. 5 | 6 | All it requires to set up is a URL pointing to a JSON document describing your 7 | app's changes. 8 | 9 | It will compare its version code (from the manifest file) to the versions 10 | listed in the JSON. If there are newer version(s), it will provide the 11 | changelog between the installed version and the latest version. The updater 12 | checks against the versionCode, but displays the versionName. 13 | 14 | While you can create your own OnAppUpdateListener to listen for new updates, 15 | OnUpdateDialog is a handy implementation that displays a Dialog with a bulleted 16 | list and a button to do the upgrade. 17 | 18 | The JSON format looks like this: 19 | 20 | { 21 | "package": { 22 | "downloadUrl": "http://coolapp.example.com/myapp.apk" 23 | }, 24 | 25 | "0.2": { 26 | "versionCode": 2, 27 | "changelog": ["New automatic update checker", "Improved template interactions"] 28 | }, 29 | "0.1": { 30 | "versionCode": 1, 31 | "changelog": ["fixed crash"] 32 | } 33 | } 34 | 35 | Publishing steps 36 | ---------------- 37 | 38 | 1. point the app updater to a URL you control (in the strings.xml file) 39 | 2. upload apk to server 40 | 3. generate the json document that the app updater looks at which contains information about the release. There's a python script that can generate such documents in the app updater's source. 41 | 4. publish the json document at the URL in step 1 42 | 5. every time the app starts, it'll check to see if there's an update to that json file. It has a backoff threshhold that's set compile time for the app updater so it won't check it if it already checked it within N minutes. 43 | 44 | 45 | License 46 | ======= 47 | 48 | AppUpdateChecker 49 | Copyright (C) 2011 [MIT Mobile Experience Lab][mel] 50 | 51 | This library is free software; you can redistribute it and/or 52 | modify it under the terms of the GNU Lesser General Public 53 | License as published by the Free Software Foundation; either 54 | version 2.1 of the License, or (at your option) any later version. 55 | 56 | This library is distributed in the hope that it will be useful, 57 | but WITHOUT ANY WARRANTY; without even the implied warranty of 58 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 59 | Lesser General Public License for more details. 60 | 61 | You should have received a copy of the GNU Lesser General Public 62 | License along with this library; if not, write to the Free Software 63 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 64 | 65 | [mel]: http://mobile.mit.edu/ 66 | -------------------------------------------------------------------------------- /extras/release.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2.7 2 | 3 | import json, sys 4 | import urllib2 5 | import rfc822, datetime, time 6 | import os 7 | 8 | import argparse 9 | 10 | DOWNLOAD_URL = 'downloadUrl' 11 | PACKAGE = 'package' 12 | VERSION_CODE = 'versionCode' 13 | CHANGELOG = 'changelog' 14 | 15 | APK_CONTENT_TYPE = 'application/vnd.android.package-archive' 16 | 17 | class HeadRequest(urllib2.Request): 18 | def get_method(self): 19 | return "HEAD" 20 | 21 | class VersionListException(Exception): 22 | pass 23 | 24 | class VersionList: 25 | """AppUpdateChecker JSON file generator / updater 26 | 27 | This creates and updates the JSON file read by AppUpdateChecker, as well as allows 28 | a file to be verified to ensure that it's constructed properly. 29 | """ 30 | versions = None 31 | package = None 32 | 33 | def __init__(self, json_file=None): 34 | self.json_file = json_file 35 | if json_file: 36 | self.json = json.load(json_file) 37 | self.parse() 38 | 39 | def parse(self): 40 | if not self.json_file: 41 | return 42 | 43 | if type(self.json) != dict: 44 | return 45 | self.versions = dict(self.json) 46 | if PACKAGE in self.versions: 47 | self.package = self.versions[PACKAGE] 48 | del self.versions[PACKAGE] 49 | 50 | def verify(self, online=False): 51 | """Returns a tuple of verification, error message 52 | 53 | Raises VersionListException if the list hasn't been loaded""" 54 | 55 | if not self.json_file: 56 | raise VersionListException("must load a version list file first") 57 | 58 | ver = self.versions 59 | if type(ver) != dict: 60 | return (False,"Document is not a JSON object") 61 | 62 | if not self.package: 63 | return (False,"missing %s key" % PACKAGE) 64 | if DOWNLOAD_URL not in self.package: 65 | return (False,"missing %s key in %s object" % (DOWNLOAD_URL, PACKAGE)) 66 | 67 | for code, info in ver.iteritems(): 68 | if type(info) != dict: 69 | return (False,"value for version '%s' is not a JSON object" % code) 70 | if type(ver[code][VERSION_CODE]) != int: 71 | return (False,"version code in key %s of version '%s' is not an int" % (VERSION_CODE, code)) 72 | if type(ver[code][CHANGELOG]) != list: 73 | return (False, "key %s in version '%s' is not a list" % (CHANGELOG, code)) 74 | 75 | if online: 76 | return self.verify_online() 77 | 78 | return (True, None) 79 | 80 | def download_url(self, url=None): 81 | if url: 82 | if not self.package: 83 | self.package = {} 84 | self.package[DOWNLOAD_URL] = url 85 | 86 | return self.package[DOWNLOAD_URL] 87 | 88 | def version_latest(self): 89 | ver = self.versions_sorted()[-1] 90 | return (ver, self.versions[ver]) 91 | 92 | def versions_sorted(self): 93 | """Retrieves a sorted list of all the version names, sorted by version code 94 | 95 | Raises VersionListException if the list hasn't been loaded""" 96 | if not self.json_file: 97 | raise VersionListException("must load a version list file first") 98 | 99 | return sorted(self.versions, key=lambda ver: self.versions[ver][VERSION_CODE]) 100 | 101 | def verify_online(self): 102 | url = self.download_url() 103 | 104 | try: 105 | res = urllib2.urlopen(HeadRequest(url)) 106 | except urllib2.HTTPError as e: 107 | return (False, e) 108 | 109 | if res.code != 200: 110 | return (False, "%d %s" % (res.code, res.msg)) 111 | 112 | sys.stderr.writelines("HEAD %s returned %d %s\n" % (url, res.code, res.msg)) 113 | 114 | content_type = res.headers['content-type'] 115 | if APK_CONTENT_TYPE != content_type: 116 | sys.stderr.writelines("warning: content type returned by %s should be %s, not %s\n" % (url, APK_CONTENT_TYPE, content_type)) 117 | 118 | last_modified = res.headers.get('last-modified', None) 119 | if last_modified: 120 | last_modified = datetime.datetime.fromtimestamp(time.mktime(rfc822.parsedate(last_modified))) 121 | sys.stderr.writelines("last modified %s\n" % last_modified) 122 | 123 | size = res.headers.get('content-length', None) 124 | if size and size < 4000: 125 | return (False, "content length of %s was less than 4k." % url) 126 | 127 | res.close() 128 | 129 | return (True, None) 130 | 131 | def write_json(self, out): 132 | j = dict(self.versions) 133 | j[PACKAGE] = self.package 134 | json.dump(j, out, indent=2) 135 | 136 | def add_release(self, ver_code, ver_name, changelog=[]): 137 | if not self.versions: 138 | self.versions = {} 139 | if ver_name in self.versions.keys(): 140 | raise VersionListException("version '%s' already exists" % ver_name) 141 | ver_code = int(ver_code) 142 | if ver_code in map(lambda v: v[VERSION_CODE], self.versions.values()): 143 | raise VersionListException("version code '%d' already exists" % ver_code) 144 | self.versions[ver_name] = {VERSION_CODE: ver_code, CHANGELOG: changelog} 145 | 146 | def release_cmd(args): 147 | if args.filename: 148 | try: 149 | inpt = open(args.filename) 150 | except IOError as e: 151 | # this is not ideal according to http://docs.python.org/howto/doanddont.html 152 | # but there's no good way to determine if it's a "file not found" or other error 153 | # based on the exception alone 154 | if not os.path.exists(args.filename): 155 | inpt = None 156 | else: 157 | raise e 158 | 159 | ver = VersionList(inpt) 160 | if inpt: 161 | ver.verify() 162 | else: 163 | ver = VersionList(sys.stdin) 164 | ver.verify() 165 | 166 | try: 167 | ver.add_release(args.code, args.name, args.changelog) 168 | if args.url: 169 | ver.download_url(args.url) 170 | if args.filename: 171 | out = open(args.filename, 'w') 172 | else: 173 | out = sys.stdout 174 | ver.write_json(out) 175 | except VersionListException as e: 176 | sys.stderr.writelines("%s\n" % e) 177 | 178 | def verify_cmd(args): 179 | if args.filename: 180 | inpt = open(args.filename) 181 | else: 182 | inpt = sys.stdin 183 | ver = VersionList(inpt) 184 | (res, err) = ver.verify(online=args.online) 185 | if res: 186 | print "verification succeeded: no errors found" 187 | latest, latest_info = ver.version_latest() 188 | print "Latest version is %s (%d)" % (latest, latest_info[VERSION_CODE]) 189 | else: 190 | print "verification failed: %s" % err 191 | 192 | if __name__=='__main__': 193 | parser = argparse.ArgumentParser( 194 | description="Generate or update a static json AppUpdateChecker file.") 195 | 196 | parser.add_argument("-f", "--file", dest="filename", 197 | help="read/write version information to FILE", metavar="FILE") 198 | 199 | subparsers = parser.add_subparsers(help='sub-command help') 200 | 201 | verify = subparsers.add_parser('verify', help="verify the document") 202 | release = subparsers.add_parser('release', help="add a new release") 203 | 204 | verify.add_argument("-d", "--verify-download", dest="online", action="store_true", 205 | help="when verifying, perform a HEAD request on the download URL to ensure it is valid") 206 | 207 | verify.set_defaults(func=verify_cmd) 208 | 209 | release.add_argument("-u", "--url", dest="url", help="set/update download URL") 210 | 211 | release.add_argument('code', help='integer version code') 212 | release.add_argument('name', help='a unique name for the release') 213 | release.add_argument('changelog', help='changelog entries', nargs='+') 214 | release.set_defaults(func=release_cmd) 215 | 216 | args = parser.parse_args() 217 | args.func(args) 218 | -------------------------------------------------------------------------------- /libs/melautils.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitmel/AppUpdateChecker/0163f36c5ed25168ab59395170973d32c3d17baf/libs/melautils.jar -------------------------------------------------------------------------------- /lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system use, 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | 10 | # Project target. 11 | target=Google Inc.:Google APIs:8 12 | android.library=true 13 | -------------------------------------------------------------------------------- /res/values-it/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | C\'è una nuova versione (%1$s) di %2$s disponibile! 4 | Aggiorna 5 | 6 | -------------------------------------------------------------------------------- /res/values-pt/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Nova versão (%1$s) de %2$s disponível! 4 | Nova versão 5 | 6 | -------------------------------------------------------------------------------- /res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | There is a new version (%1$s) of %2$s available! 6 | 7 | Upgrade 8 | 9 | Check for updates 10 | 11 | Minimum update frequency 12 | 13 | Checks for updates at most this many minutes 14 | 15 | -------------------------------------------------------------------------------- /res/xml/preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/edu/mit/mobile/android/appupdater/AppUpdateChecker.java: -------------------------------------------------------------------------------- 1 | package edu.mit.mobile.android.appupdater; 2 | 3 | /* 4 | * Copyright (C) 2010-2012 MIT Mobile Experience Lab 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | import java.util.ArrayList; 21 | import java.util.Comparator; 22 | import java.util.Iterator; 23 | import java.util.Map.Entry; 24 | import java.util.TreeMap; 25 | 26 | import org.apache.http.HttpEntity; 27 | import org.apache.http.HttpResponse; 28 | import org.apache.http.HttpStatus; 29 | import org.apache.http.StatusLine; 30 | import org.apache.http.client.methods.HttpGet; 31 | import org.apache.http.impl.client.DefaultHttpClient; 32 | import org.json.JSONArray; 33 | import org.json.JSONException; 34 | import org.json.JSONObject; 35 | 36 | import android.content.Context; 37 | import android.content.Intent; 38 | import android.content.SharedPreferences; 39 | import android.content.pm.PackageManager.NameNotFoundException; 40 | import android.net.Uri; 41 | import android.os.AsyncTask; 42 | import android.preference.PreferenceManager; 43 | import android.util.Log; 44 | import edu.mit.mobile.android.utils.StreamUtils; 45 | 46 | /** 47 | * A fairly simple non-Market app update checker. Give it a URL pointing to a JSON file 48 | * and it will compare its version (from the manifest file) to the versions listed in the JSON. 49 | * If there are newer version(s), it will provide the changelog between the installed version 50 | * and the latest version. The updater checks against the versionCode, but displays the versionName. 51 | * 52 | * While you can create your own OnAppUpdateListener to listen for new updates, OnUpdateDialog is 53 | * a handy implementation that displays a Dialog with a bulleted list and a button to do the upgrade. 54 | * 55 | * The JSON format looks like this: 56 | *
 57 |   {
 58 |     "package": {
 59 |         "downloadUrl": "http://locast.mit.edu/connects/lcc.apk"
 60 |     },
 61 | 
 62 |     "1.4.3": {
 63 |     "versionCode": 6,
 64 |     "changelog": ["New automatic update checker", "Improved template interactions"]
 65 |     },
 66 |     "1.4.2": {
 67 |     "versionCode": 5,
 68 |     "changelog": ["fixed crash when saving cast"]
 69 |     }
 70 | }
 71 |  * 
72 | * 73 | * @author Steve Pomeroy 74 | * 75 | */ 76 | public class AppUpdateChecker { 77 | private final static String TAG = AppUpdateChecker.class.getSimpleName(); 78 | 79 | public static final String SHARED_PREFERENCES_NAME = "edu.mit.mobile.android.appupdater.preferences"; 80 | public static final String 81 | PREF_ENABLED = "enabled", 82 | PREF_MIN_INTERVAL = "min_interval", 83 | PREF_LAST_UPDATED = "last_checked"; 84 | 85 | private final String mVersionListUrl; 86 | private int currentAppVersion; 87 | 88 | private JSONObject pkgInfo; 89 | private final Context mContext; 90 | 91 | private final OnAppUpdateListener mUpdateListener; 92 | private SharedPreferences mPrefs; 93 | 94 | private static final int MILLISECONDS_IN_MINUTE = 60000; 95 | 96 | /** 97 | * @param context 98 | * @param versionListUrl URL pointing to a JSON file with the update list. 99 | * @param updateListener 100 | */ 101 | public AppUpdateChecker(Context context, String versionListUrl, OnAppUpdateListener updateListener) { 102 | mContext = context; 103 | mVersionListUrl = versionListUrl; 104 | mUpdateListener = updateListener; 105 | 106 | try { 107 | currentAppVersion = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode; 108 | } catch (final NameNotFoundException e) { 109 | Log.e(TAG, "Cannot get version for self! Who am I?! What's going on!? I'm so confused :-("); 110 | return; 111 | } 112 | 113 | mPrefs = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); 114 | // defaults are kept in the preference file for ease of tweaking 115 | // TODO put this on a thread somehow 116 | PreferenceManager.setDefaultValues(mContext, SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE, R.xml.preferences, false); 117 | } 118 | 119 | // min interval is stored as a string so a preference editor could potentially edit it using a text edit widget 120 | 121 | public int getMinInterval(){ 122 | return Integer.valueOf(mPrefs.getString(PREF_MIN_INTERVAL, "60")); 123 | } 124 | 125 | public void setMinInterval(int minutes){ 126 | mPrefs.edit().putString(PREF_MIN_INTERVAL, String.valueOf(minutes)).commit(); 127 | } 128 | 129 | public boolean getEnabled(){ 130 | return mPrefs.getBoolean(PREF_ENABLED, true); 131 | } 132 | 133 | public void setEnabled(boolean enabled){ 134 | mPrefs.edit().putBoolean(PREF_ENABLED, enabled).commit(); 135 | } 136 | 137 | /** 138 | * You normally shouldn't need to call this, as {@link #checkForUpdates()} checks it before doing any updates. 139 | * 140 | * @return true if the updater should check for updates 141 | */ 142 | public boolean isStale(){ 143 | return System.currentTimeMillis() - mPrefs.getLong(PREF_LAST_UPDATED, 0) > getMinInterval() * MILLISECONDS_IN_MINUTE; 144 | } 145 | 146 | /** 147 | * Checks for updates if updates haven't been checked for recently and if checking is enabled. 148 | */ 149 | public void checkForUpdates(){ 150 | if (mPrefs.getBoolean(PREF_ENABLED, true) && isStale()){ 151 | forceCheckForUpdates(); 152 | } 153 | } 154 | 155 | /** 156 | * Checks for updates regardless of when the last check happened or if checking for updates is enabled. 157 | */ 158 | public void forceCheckForUpdates(){ 159 | Log.d(TAG, "checking for updates..."); 160 | if (versionTask == null){ 161 | versionTask = new GetVersionJsonTask(); 162 | versionTask.execute(mVersionListUrl); 163 | }else{ 164 | Log.w(TAG, "checkForUpdates() called while already checking for updates. Ignoring..."); 165 | } 166 | } 167 | 168 | // why oh why is the JSON API so poorly integrated into java? 169 | @SuppressWarnings("unchecked") 170 | private void triggerFromJson(JSONObject jo) throws JSONException { 171 | 172 | final ArrayList changelog = new ArrayList(); 173 | 174 | // keep a sorted map of versionCode to the version information objects. 175 | // Most recent is at the top. 176 | final TreeMap versionMap = 177 | new TreeMap(new Comparator() { 178 | public int compare(Integer object1, Integer object2) { 179 | return object2.compareTo(object1); 180 | }; 181 | }); 182 | 183 | for (final Iterator i = jo.keys(); i.hasNext(); ){ 184 | final String versionName = i.next(); 185 | if (versionName.equals("package")){ 186 | pkgInfo = jo.getJSONObject(versionName); 187 | continue; 188 | } 189 | final JSONObject versionInfo = jo.getJSONObject(versionName); 190 | versionInfo.put("versionName", versionName); 191 | 192 | final int versionCode = versionInfo.getInt("versionCode"); 193 | versionMap.put(versionCode, versionInfo); 194 | } 195 | final int latestVersionNumber = versionMap.firstKey(); 196 | final String latestVersionName = versionMap.get(latestVersionNumber).getString("versionName"); 197 | final Uri downloadUri = Uri.parse(pkgInfo.getString("downloadUrl")); 198 | 199 | if (currentAppVersion > latestVersionNumber){ 200 | Log.d(TAG, "We're newer than the latest published version ("+latestVersionName+"). Living in the future..."); 201 | mUpdateListener.appUpdateStatus(true, latestVersionName, null, downloadUri); 202 | return; 203 | } 204 | 205 | if (currentAppVersion == latestVersionNumber){ 206 | Log.d(TAG, "We're at the latest version ("+currentAppVersion+")"); 207 | mUpdateListener.appUpdateStatus(true, latestVersionName, null, downloadUri); 208 | return; 209 | } 210 | 211 | // construct the changelog. Newest entries are at the top. 212 | for (final Entry version: versionMap.headMap(currentAppVersion).entrySet()){ 213 | final JSONObject versionInfo = version.getValue(); 214 | final JSONArray versionChangelog = versionInfo.optJSONArray("changelog"); 215 | if (versionChangelog != null){ 216 | final int len = versionChangelog.length(); 217 | for (int i = 0; i < len; i++){ 218 | changelog.add(versionChangelog.getString(i)); 219 | } 220 | } 221 | } 222 | 223 | mUpdateListener.appUpdateStatus(false, latestVersionName, changelog, downloadUri); 224 | } 225 | 226 | private class VersionCheckException extends Exception { 227 | /** 228 | * 229 | */ 230 | private static final long serialVersionUID = 397593559982487816L; 231 | 232 | public VersionCheckException(String msg) { 233 | super(msg); 234 | } 235 | } 236 | 237 | /** 238 | * Send off an intent to start the download of the app. 239 | */ 240 | public void startUpgrade(){ 241 | try { 242 | final Uri downloadUri = Uri.parse(pkgInfo.getString("downloadUrl")); 243 | mContext.startActivity(new Intent(Intent.ACTION_VIEW, downloadUri)); 244 | } catch (final JSONException e) { 245 | e.printStackTrace(); 246 | } 247 | } 248 | 249 | private GetVersionJsonTask versionTask; 250 | private class GetVersionJsonTask extends AsyncTask{ 251 | private String errorMsg = null; 252 | 253 | @Override 254 | protected void onProgressUpdate(Integer... values) { 255 | Log.d(TAG, "update check progress: " + values[0]); 256 | super.onProgressUpdate(values); 257 | } 258 | 259 | @Override 260 | protected JSONObject doInBackground(String... params) { 261 | publishProgress(0); 262 | final DefaultHttpClient hc = new DefaultHttpClient(); 263 | final String url = params[0]; 264 | final HttpGet req = new HttpGet(url); 265 | JSONObject jo = null; 266 | try { 267 | publishProgress(50); 268 | final HttpResponse res = hc.execute(req); 269 | 270 | final StatusLine status = res.getStatusLine(); 271 | final int statusCode = status.getStatusCode(); 272 | if (statusCode == HttpStatus.SC_NOT_FOUND) { 273 | throw new VersionCheckException(url + " " + status.getReasonPhrase()); 274 | } 275 | if (statusCode != HttpStatus.SC_OK){ 276 | final HttpEntity e = res.getEntity(); 277 | if (e.getContentType().getValue().equals("text/html") || e.getContentLength() > 40){ 278 | // long response body. Serving HTML... 279 | throw new VersionCheckException("Got a HTML instead of expected JSON."); 280 | } 281 | throw new VersionCheckException("HTTP " + res.getStatusLine().getStatusCode() + " "+ res.getStatusLine().getReasonPhrase()); 282 | } 283 | 284 | final HttpEntity ent = res.getEntity(); 285 | 286 | jo = new JSONObject(StreamUtils.inputStreamToString(ent.getContent())); 287 | ent.consumeContent(); 288 | mPrefs.edit().putLong(PREF_LAST_UPDATED, System.currentTimeMillis()).commit(); 289 | 290 | } catch (final Exception e) { 291 | //e.printStackTrace(); 292 | 293 | errorMsg = e.getClass().getSimpleName() + ": " + e.getLocalizedMessage(); 294 | }finally { 295 | publishProgress(100); 296 | } 297 | return jo; 298 | } 299 | 300 | @Override 301 | protected void onPostExecute(JSONObject result) { 302 | if (result == null){ 303 | Log.e(TAG, errorMsg); 304 | }else{ 305 | try { 306 | triggerFromJson(result); 307 | 308 | } catch (final JSONException e) { 309 | Log.e(TAG, "Error in JSON version file.", e); 310 | } 311 | } 312 | versionTask = null; // forget about us, we're done. 313 | }; 314 | }; 315 | } 316 | -------------------------------------------------------------------------------- /src/edu/mit/mobile/android/appupdater/OnAppUpdateListener.java: -------------------------------------------------------------------------------- 1 | package edu.mit.mobile.android.appupdater; 2 | 3 | import java.util.List; 4 | 5 | import android.net.Uri; 6 | 7 | public interface OnAppUpdateListener { 8 | public void appUpdateStatus(boolean isLatestVersion, String latestVersionName, List changelog, Uri downloadUrl); 9 | } -------------------------------------------------------------------------------- /src/edu/mit/mobile/android/appupdater/OnUpdateDialog.java: -------------------------------------------------------------------------------- 1 | package edu.mit.mobile.android.appupdater; 2 | 3 | import java.util.List; 4 | 5 | import android.app.Dialog; 6 | import android.app.AlertDialog.Builder; 7 | import android.content.Context; 8 | import android.content.DialogInterface; 9 | import android.content.Intent; 10 | import android.net.Uri; 11 | import android.os.Handler; 12 | import android.os.Message; 13 | 14 | /** 15 | * A handy pop-up dialog box which lists the changelog and asks if you want to update. 16 | * 17 | * @author steve 18 | * 19 | */ 20 | public class OnUpdateDialog implements OnAppUpdateListener { 21 | private final Context mContext; 22 | private final CharSequence mAppName; 23 | private Uri downloadUri; 24 | private final Handler mHandler; 25 | private static final int MSG_SHOW_DIALOG = 1; 26 | private Dialog mDialog; 27 | 28 | public OnUpdateDialog(Context context, CharSequence appName) { 29 | mContext = context; 30 | mAppName = appName; 31 | mHandler = new Handler(){ 32 | @Override 33 | public void handleMessage(Message msg) { 34 | switch (msg.what){ 35 | case MSG_SHOW_DIALOG: 36 | try{ 37 | // TODO fix this so it'll pop up appropriately 38 | mDialog.show(); 39 | }catch (final Exception e){ 40 | // XXX ignore for the moment 41 | } 42 | 43 | break; 44 | } 45 | } 46 | }; 47 | } 48 | 49 | public void appUpdateStatus(boolean isLatestVersion, 50 | String latestVersionName, List changelog, Uri downloadUri) { 51 | this.downloadUri = downloadUri; 52 | 53 | if (!isLatestVersion){ 54 | final Builder db = new Builder(mContext); 55 | db.setTitle(mAppName); 56 | 57 | final StringBuilder sb = new StringBuilder(); 58 | sb.append(mContext.getString(R.string.app_update_new_version, latestVersionName, mAppName)); 59 | sb.append("\n\n"); 60 | for (final String item: changelog){ 61 | sb.append(" • ").append(item).append("\n"); 62 | } 63 | 64 | db.setMessage(sb); 65 | 66 | db.setPositiveButton(R.string.upgrade, dialogOnClickListener); 67 | db.setNegativeButton(android.R.string.cancel, dialogOnClickListener); 68 | mDialog = db.create(); 69 | mHandler.sendEmptyMessage(MSG_SHOW_DIALOG); 70 | 71 | } 72 | } 73 | 74 | private final DialogInterface.OnClickListener dialogOnClickListener = new DialogInterface.OnClickListener() { 75 | 76 | public void onClick(DialogInterface dialog, int which) { 77 | switch(which){ 78 | case Dialog.BUTTON_POSITIVE: 79 | mContext.startActivity(new Intent(Intent.ACTION_VIEW, downloadUri)); 80 | } 81 | 82 | } 83 | }; 84 | } --------------------------------------------------------------------------------