├── .gitignore
├── .project
├── AndroidManifest.xml
├── LICENSE
├── README.md
├── build.xml
├── default.properties
├── res
├── drawable-hdpi
│ ├── ic_launcher_mapattack.png
│ ├── ic_stat_notify.png
│ ├── map_attack_logo.png
│ └── powered_by_geoloqi.png
├── drawable-ldpi
│ ├── ic_launcher_mapattack.png
│ └── icon.png
├── drawable-mdpi
│ ├── ic_launcher_mapattack.png
│ ├── ic_stat_notify.png
│ ├── map_attack_logo.png
│ └── powered_by_geoloqi.png
├── layout
│ ├── game_list_activity.xml
│ ├── game_list_element.xml
│ ├── main.xml
│ ├── main_header.xml
│ └── sign_in_activity.xml
├── menu
│ └── game_menu.xml
├── raw
│ └── pop.ogg
├── values
│ └── strings.xml
└── xml
│ └── settings.xml
└── src
└── com
└── geoloqi
├── ADB.java
├── Installation.java
├── data
├── Fix.java
├── Game.java
├── Geonote.java
├── Layer.java
├── Place.java
└── SharingLink.java
├── interfaces
├── AuthChangeListener.java
├── GeoloqiClient.java
├── GeoloqiConstantsTemplate.java
├── GeoloqiFixSocket.java
└── RPCException.java
├── mapattack
└── UDPClient.java
├── rpc
├── AccountMonitor.java
├── ExpiredTokenException.java
├── MapAttackClient.java
├── MyRequest.java
└── OAuthToken.java
├── services
├── AndroidPushNotifications.java
└── GeoloqiPositioning.java
├── ui
├── GameListActivity.java
├── MapAttackActivity.java
└── SignInActivity.java
└── widget
└── GameListArrayAdapter.java
/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore system files
2 | .DS_Store
3 |
4 | # Ignore generated files and binaries
5 | bin/
6 | gen/
7 |
8 | # Ignore editor files
9 | *.swp
10 | .settings
11 | .classpath
12 | local.properties
13 | proguard.cfg
14 |
15 | # Ignore Constants file
16 | GeoloqiConstants.java
17 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | MapAttack
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 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
19 |
20 |
24 |
25 |
29 |
30 |
31 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
43 |
44 |
45 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 |
294 | Copyright (C)
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | , 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | About MapAttack
3 | ===============
4 |
5 | http://mapattack.org
6 |
7 | MapAttack is a game of real-time strategy built for real life. Virtual geofences
8 | are scattered onto the map and players must physically go to where they are on the
9 | map in order to capture them. The winner is the team that captures the most points.
10 |
11 | The game is powered by the Geoloqi platform (https://geoloqi.com) and interacts with
12 | its REST and Streaming APIs. See https://developers.geoloqi.com for more information.
13 |
14 | You will also need to run a game server which you can find here: https://github.com/geoloqi/MapAttack
15 |
16 |
17 |
18 | How to Use
19 | ==========
20 |
21 | To run this app, you will first need to create an application on the Geoloqi Developer
22 | website, https://developers.geoloqi.com. Once you create an application you will get
23 | a client ID and secret. Copy GeoloqiConstants.template.java to a file called
24 | GeoloqiConstants.java and fill in your client ID and secret, application ID, and URL
25 | of your game server.
26 |
27 |
28 | License
29 | =======
30 |
31 | Copyright 2011 by Geoloqi LLC and contributors.
32 |
33 | See LICENSE
34 |
35 | If you would like to discuss different licensing options, please contact us at info@geoloqi.com
36 | or on our website, https://geoloqi.com.
37 |
38 |
--------------------------------------------------------------------------------
/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
27 |
28 |
29 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
42 |
54 |
55 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/default.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 | # "build.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 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher_mapattack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoloqi/MapAttack-Android/5b44e707e94e173433e51f54b83e9f7018226b43/res/drawable-hdpi/ic_launcher_mapattack.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_stat_notify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoloqi/MapAttack-Android/5b44e707e94e173433e51f54b83e9f7018226b43/res/drawable-hdpi/ic_stat_notify.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/map_attack_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoloqi/MapAttack-Android/5b44e707e94e173433e51f54b83e9f7018226b43/res/drawable-hdpi/map_attack_logo.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/powered_by_geoloqi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoloqi/MapAttack-Android/5b44e707e94e173433e51f54b83e9f7018226b43/res/drawable-hdpi/powered_by_geoloqi.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/ic_launcher_mapattack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoloqi/MapAttack-Android/5b44e707e94e173433e51f54b83e9f7018226b43/res/drawable-ldpi/ic_launcher_mapattack.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoloqi/MapAttack-Android/5b44e707e94e173433e51f54b83e9f7018226b43/res/drawable-ldpi/icon.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher_mapattack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoloqi/MapAttack-Android/5b44e707e94e173433e51f54b83e9f7018226b43/res/drawable-mdpi/ic_launcher_mapattack.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_stat_notify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoloqi/MapAttack-Android/5b44e707e94e173433e51f54b83e9f7018226b43/res/drawable-mdpi/ic_stat_notify.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/map_attack_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoloqi/MapAttack-Android/5b44e707e94e173433e51f54b83e9f7018226b43/res/drawable-mdpi/map_attack_logo.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/powered_by_geoloqi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoloqi/MapAttack-Android/5b44e707e94e173433e51f54b83e9f7018226b43/res/drawable-mdpi/powered_by_geoloqi.png
--------------------------------------------------------------------------------
/res/layout/game_list_activity.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
23 |
24 |
30 |
31 |
36 |
37 |
38 |
43 |
44 |
45 |
52 |
57 |
58 |
59 |
60 |
65 |
71 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/res/layout/game_list_element.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
16 |
25 |
26 |
--------------------------------------------------------------------------------
/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
14 |
15 |
16 |
21 |
22 |
--------------------------------------------------------------------------------
/res/layout/main_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
14 |
--------------------------------------------------------------------------------
/res/layout/sign_in_activity.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
17 |
21 |
28 |
32 |
37 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/res/menu/game_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/res/raw/pop.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoloqi/MapAttack-Android/5b44e707e94e173433e51f54b83e9f7018226b43/res/raw/pop.ogg
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | MapAttack
4 |
5 |
6 | Games near your location
7 | There are no games in your area.\n\nIf you would like to bring a game to your area, please contact us:\n\nplay@mapattack.org
8 | Searching for games...
9 |
10 |
11 | Joining game...
12 |
13 |
14 | Refresh
15 |
16 |
17 | The game server is not responding. Do you have an active data connection?
18 | Please provide your first and last initial.
19 | Please provide a valid email address.
20 | The game ID provided was invalid.
21 | Failed to join the game! Please try again later.
22 |
--------------------------------------------------------------------------------
/res/xml/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
10 |
14 |
15 |
17 |
22 |
23 |
--------------------------------------------------------------------------------
/src/com/geoloqi/ADB.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 | import android.widget.Toast;
6 |
7 | public abstract class ADB {
8 |
9 | public static boolean DEBUG = false;
10 |
11 | public static void log(String message) {
12 | Log.d("bpl", message);
13 | }
14 |
15 | public static void logo() {
16 | log("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#@@@@@@@@@++++++++++++++++++++++++++++++++++++++++@@@@@#++++++++++");
17 | log("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@@@@@@@@@@@+++++++++++++++++++++++++++++++++++++#@@@@@@@@+++++++++");
18 | log("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@@;,,,,,@@@+++++++++++++++++++++++++++++++++++++@@@@'+@@@@++++++++");
19 | log("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@@,,,,,,@@@++++++++++++++++++++++++++++++++++++@@@,,,,,@@@++++++++");
20 | log("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#@@,,,,,,@@#++++++++++++++++++++++++++++++++++++@@#,,,,,,@@@+++++++");
21 | log("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@@@,,,,,,@@#++++++++++++++++++++++++++++++++++++@@:,,,,,,@@@+++++++");
22 | log("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@@@,,,,,,@@+++++++++++++++++++++++++++++++++++++@@:,,,,,,@@@+++++++");
23 | log("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@@@,,,,,'@@+++++++++++++++++++++++++++++++++++++@@@,,,,,,@@#+++++++");
24 | log("++++++++++++++#@@@@@@@@+#@@@@#+++++#@@@@@@@@@+++++++++@@@@@@@@@#+++@@@,,,,,+@@++++@@@@@@@@@+++++++++@@@@@@@@##@@@@@@@@+,,,,@@@++++++++");
25 | log("+++++++++++++@@@@@@@@@@@@@@@@@@++@@@@@@@@@@@@@@+++++@@@@@@@@@@@@@++@@+,,,,,@@@+#@@@@@@@@@@@@@+++++@@@@@@@@@@@@@@@@@@@@@@@@@@@@++++++++");
26 | log("+++++++++++#@@@@@;,,'@@@@@@@@@@+@@@@@@;,,,+@@@@@++#@@@@@+,,,;@@@@@#@@;,,,,,@@@@@@@@@':::'@@@@@+++@@@@@'::'@@@@@@@@@@@@@@@@@@@#++++++++");
27 | log("+++++++++++@@@'........@@...,@@@@@@..........#@@@#@@@'.........+@@@@@,,,,,,@@@@@@',,,,,,,,,#@@@+@@@@,,,,,,,,;@,,,,@@@,,,,,,@@#++++++++");
28 | log("++++++++++@@@,..............;@@@@@............+@@@@@............:@@@@......@@@@@............+@@#@@@...............@@@......@@#++++++++");
29 | log("++++++++++@@+...............@@@@@..............@@@@..............@@@@......@@@@..............@@@@@................@@@.....:@@+++++++++");
30 | log("+++++++++@@@................@@@@...............@@@@..............,@@@......@@@+..............'@@@:...............:@@@.....'@@+++++++++");
31 | log("+++++++++@@#``````@@@@``````@@@@`````:@@@.`````@@@``````'@@#``````@@@.....;@@@......#@@;......@@@......'@@@,.....'@@@.....#@@+++++++++");
32 | log("+++++++++@@``````@@@@@``````@@@``````@@@@;`````@@@``````@@@@``````@@@.....+@@@.....:@@@@......@@@.....:@@@@#.....@@@+.....@@@+++++++++");
33 | log("++++++++@@@``````@@@@@``````@@@`````,@@@@``````@@+`````@@@@@``````@@+.....@@@;.....@@@@@......@@:.....@@@@@;.....@@@;.....@@@+++++++++");
34 | log("++++++++@@@`````;@@@@@``````@@@```````````````@@@.`````@@@@@``````@@;.....@@@......@@@@@......@@......@@@@@,.....@@@......@@@+++++++++");
35 | log("++++++++@@@`````@@@@@@`````,@@@``````````````:@@@``````@@@@@``````@@......@@@......@@@@@......@@......@@#@@......@@@......@@@+++++++++");
36 | log("++++++++@@+`````@@@@@@`````;@@+`````````````@@@@@``````@@@@@``````@@......@@@......@@@@@.....,@@.....,@@#@@......@@@......@@#+++++++++");
37 | log("++++++++@@'`````@@@@@@`````@@@;`````,,,,;#@@@@@@@``````@@@@@`````;@@......@@@......@@@@@.....#@@.....,@@@@@......@@@......@@++++++++++");
38 | log("++++++++@@;`````@@@@@'`````@@@;`````@@@@@@@@@@@@@``````@@@@:`````@@@``````@@@``````@@@@.`````@@@``````@@@@@`````,@@@`````;@@@+++++++++");
39 | log("++++++++@@+``````@@@@,`````@@@#`````@@@@@@@@@'@@@``````@@@@``````@@@`````.@@@``````@@@@``````@@@``````@@@@@`````;@@@`````'@@@+++++++++");
40 | log("++++++++@@@````````````````@@@@```````,;,````@@@@```````,.``````:@@@```````@@```````,.``````+@@@````````````````@@@@```````@@+++++++++");
41 | log("++++++++@@@````````````````@@@@``````````````@@@@#``````````````@@@@```````@@;``````````````@@@@````````````````@@@@``````,@@+++++++++");
42 | log("++++++++#@@.```````````````@@@@@`````````````@@@@@`````````````@@@@@``````,@@@`````````````@@@@@@```````````````@@@@``````'@@+++++++++");
43 | log("+++++++++@@@``````````````.@@@@@#````````````@@@@@@```````````@@@@@@@`````;@@@@```````````@@@@@@@:``````````````@@@@#`````@@@+++++++++");
44 | log("+++++++++#@@@;`````:@`````;@@+@@@@;```````:@@@@#@@@@#```````#@@@@+@@@@;```@@@@@@+``````.@@@@@++@@@#``````@``````@@@@@@:```@@@+++++++++");
45 | log("++++++++++@@@@@@@@@@@`````+@@++@@@@@@@@@@@@@@@@++@@@@@@@@@@@@@@@+++@@@@@@@@@@@@@@@@@@@@@@@@@++++@@@@@@@@@@``````@@#@@@@@@@@@@+++++++++");
46 | log("++++++++@@@@@@@@@@@@'`````@@@+++#@@@@@@@@@@@@#+++++@@@@@@@@@@@#+++++#@@@@@@@++#@@@@@@@@@@@+++++++@@@@@@@@@`````.@@++@@@@@@@@++++++++++");
47 | log("++++++++@@@@@@@@@@@@``````@@@++++++#@@@@##++++++++++++@@@@#++++++++++++###+++++++#@@@@#++++++++++++#@@@@@@ ;@@+++++###++++++++++++");
48 | log("++++++++@@`.@@@@@@@.``````@@#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@@@ #@@++++++++++++++++++++");
49 | log("+++++++#@@```````````````@@@+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@@' @@@++++++++++++++++++++");
50 | log("+++++++@@@``````````````.@@@+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@@. @@@++++++++++++++++++++");
51 | log("+++++++@@@``````````````@@@++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@@ @@@++++++++++++++++++++");
52 | log("+++++++@@@````````````+@@@#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#@@ @@@++++++++++++++++++++");
53 | log("+++++++@@@@@;,`````;@@@@@#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#@@@@@@@@@@#++++++++++++++++++++");
54 | log("++++++++@@@@@@@@@@@@@@@@+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@@@@@@@@@@+++++++++++++++++++++");
55 | log("+++++++++#@@@@@@@@@@@@#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@@@@@@@@++++++++++++++++++++++");
56 | log("+++++++++++++++###++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
57 | }
58 |
59 | public static Runnable makeToast(final Context context, final String text, final int duration) {
60 | return new Runnable() {
61 | @Override
62 | public void run() {
63 | Toast.makeText(context, text, duration).show();
64 | }
65 | };
66 | }
67 |
68 | // public static doSynchronously(final Context context, Runnable runnable) {
69 | // AsyncTask task = new AsyncTask() {
70 | //
71 | // }
72 | // }
73 | }
74 |
--------------------------------------------------------------------------------
/src/com/geoloqi/Installation.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi;
2 |
3 | import java.io.ByteArrayOutputStream;
4 | import java.io.DataInputStream;
5 | import java.io.DataOutputStream;
6 | import java.io.File;
7 | import java.io.FileInputStream;
8 | import java.io.FileNotFoundException;
9 | import java.io.FileOutputStream;
10 | import java.io.IOException;
11 | import java.io.PrintStream;
12 | import java.util.UUID;
13 |
14 | import android.content.Context;
15 | import android.util.Pair;
16 |
17 | public class Installation {
18 | private static Pair uuid;
19 | private static final String FILE = "INSTALLATION";
20 |
21 | private static void writeUUID(File installation) {
22 | try {
23 | UUID uuid = UUID.randomUUID();
24 | DataOutputStream out = new DataOutputStream(new FileOutputStream(installation));
25 | out.writeLong(uuid.getMostSignificantBits());
26 | out.writeLong(uuid.getLeastSignificantBits());
27 | out.close();
28 | } catch (FileNotFoundException e) {
29 | throw new RuntimeException(e);
30 | } catch (IOException e) {
31 | throw new RuntimeException(e);
32 | }
33 | }
34 |
35 | private static Pair getUUID(Context context) {
36 | if (uuid != null) {
37 | return uuid;
38 | } else {
39 | File uuidFile = new File(context.getFilesDir(), FILE);
40 | if (!uuidFile.exists()) {
41 | writeUUID(uuidFile);
42 | }
43 | uuid = readUUID(uuidFile);
44 | return uuid;
45 | }
46 | }
47 |
48 | public synchronized static byte[] getIDAsBytes(Context context) {
49 | try {
50 | Pair uuid = getUUID(context);
51 | ByteArrayOutputStream baos = new ByteArrayOutputStream();
52 | DataOutputStream dos = new DataOutputStream(baos);
53 | dos.writeLong(uuid.first);
54 | dos.writeLong(uuid.second);
55 | return baos.toByteArray();
56 | } catch (IOException e) {
57 | throw new RuntimeException(e);
58 | }
59 | }
60 |
61 | public synchronized static String getIDAsString(Context context) {
62 | byte[] b = getIDAsBytes(context);
63 | ByteArrayOutputStream baos = new ByteArrayOutputStream();
64 | PrintStream printStream = new PrintStream(baos);
65 | printStream.printf("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]);
66 | return new String(baos.toByteArray());
67 | }
68 |
69 | private static Pair readUUID(File installation) {
70 | if (uuid != null) {
71 | return uuid;
72 | }
73 | try {
74 | DataInputStream in = new DataInputStream(new FileInputStream(installation));
75 | Long msb = in.readLong();
76 | Long lsb = in.readLong();
77 | in.close();
78 | return new Pair(msb, lsb);
79 | } catch (FileNotFoundException e) {
80 | throw new RuntimeException(e);
81 | } catch (IOException e) {
82 | throw new RuntimeException(e);
83 | }
84 | }
85 | }
--------------------------------------------------------------------------------
/src/com/geoloqi/data/Fix.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.data;
2 |
3 | import java.io.Serializable;
4 | import java.text.SimpleDateFormat;
5 | import java.util.Arrays;
6 | import java.util.Date;
7 | import java.util.Iterator;
8 | import java.util.LinkedList;
9 | import java.util.List;
10 | import java.util.Locale;
11 |
12 | import org.json.JSONException;
13 | import org.json.JSONObject;
14 |
15 | import android.location.Location;
16 | import android.net.Uri;
17 | import android.os.Parcel;
18 | import android.os.Parcelable;
19 | import android.util.Pair;
20 |
21 | import com.geoloqi.ADB;
22 | import static com.geoloqi.interfaces.GeoloqiConstants.VERSION;;
23 |
24 | public class Fix extends Location implements Serializable, Parcelable, Comparable {
25 |
26 | private static final long serialVersionUID = 3L;
27 | List> rawData;
28 |
29 | public Fix(Uri uri) {
30 | super("Geoloqi");
31 | String[] data = uri.getPath().split("/");
32 | this.setLatitude(Float.parseFloat(data[1]));
33 | this.setLongitude(Float.parseFloat(data[2]));
34 | this.setAltitude(Float.parseFloat(data[3]));
35 | this.setBearing(Float.parseFloat(data[4]));
36 | this.setSpeed(Float.parseFloat(data[5]));
37 | this.setTime(Long.parseLong(data[6]));
38 | this.setAccuracy(Float.parseFloat(data[7]));
39 |
40 | rawData = new LinkedList>();
41 | for (int i = 0; 9 + 2 * i + 1 < data.length; i++) {
42 | rawData.add(new Pair(data[9 + 2 * i], data[9 + 2 * i + 1]));
43 | }
44 | }
45 |
46 | public Fix(JSONObject json, long timestamp, float bearing) {
47 | super("");
48 | try {
49 | this.setProvider(json.getJSONObject("client").getString("name"));
50 |
51 | JSONObject position = json.getJSONObject("location").getJSONObject("position");
52 | this.setAccuracy((float) position.getDouble("horizontal_accuracy"));
53 | this.setAltitude(position.getDouble("altitude"));
54 | this.setLatitude(position.getDouble("latitude"));
55 | this.setLongitude(position.getDouble("longitude"));
56 | this.setSpeed((float) position.getDouble("speed"));
57 |
58 | rawData = new LinkedList>();
59 | JSONObject raw = json.getJSONObject("raw");
60 | @SuppressWarnings("unchecked")
61 | Iterator keys = raw.keys();
62 | while (keys.hasNext()) {
63 | String key = keys.next();
64 | rawData.add(new Pair(key, raw.getString(key)));
65 | }
66 | } catch (JSONException e) {
67 | throw new RuntimeException(e);
68 | }
69 | this.setBearing(bearing);
70 | this.setTime(timestamp);
71 | }
72 |
73 | public Fix(Location l, Pair... rawData) {
74 | super(l);
75 | this.rawData = Arrays.asList(rawData);
76 | }
77 |
78 | public JSONObject castToJSONObject() {
79 | try {
80 | String version = VERSION;
81 | String platform = "2.1";
82 | String hardware = "unknown";
83 | JSONObject point = new JSONObject();
84 |
85 | point.put("latitude", getLatitude());
86 | point.put("longitude", getLongitude());
87 | point.put("speed", getSpeed());
88 | point.put("altitude", getAltitude());
89 | point.put("horizontal_accuracy", getAccuracy());
90 |
91 | JSONObject location = new JSONObject();
92 | JSONObject raw = new JSONObject();
93 | JSONObject client = new JSONObject();
94 |
95 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US);
96 | Date d = new Date(getTime());
97 |
98 | location.put("type", "point");
99 | location.put("position", point);
100 |
101 | for (Pair datum : rawData) {
102 | raw.put(datum.first, datum.second);
103 | }
104 |
105 | client.put("name", "Geoloqi");
106 | client.put("version", version);
107 | client.put("platform", platform);
108 | client.put("hardware", hardware);
109 |
110 | JSONObject json = new JSONObject();
111 |
112 | json.put("date", sdf.format(d));
113 | json.put("location", location);
114 | json.put("raw", raw);
115 | json.put("client", client);
116 |
117 | return json;
118 | } catch (JSONException e) {
119 | ADB.log("JSON Exception in toJSON: " + e.getMessage());
120 | throw new RuntimeException(e.getMessage());
121 | }
122 | }
123 |
124 | @Override
125 | public void writeToParcel(Parcel out, int flags) {
126 |
127 | }
128 |
129 | @Override
130 | public int compareTo(Fix arg) {
131 | return (int) (this.getTime() - arg.getTime());
132 | }
133 |
134 | }
135 |
--------------------------------------------------------------------------------
/src/com/geoloqi/data/Game.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.data;
2 |
3 | import org.json.JSONException;
4 | import org.json.JSONObject;
5 |
6 | import android.os.Parcel;
7 | import android.os.Parcelable;
8 |
9 | public class Game implements Parcelable {
10 | public String id;
11 | public String name;
12 | public String description;
13 |
14 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
15 | public Game createFromParcel(Parcel in) {
16 | return new Game(in);
17 | }
18 |
19 | public Game[] newArray(int size) {
20 | return new Game[size];
21 | }
22 | };
23 |
24 | public Game(JSONObject data) {
25 | try {
26 | id = data.getString("layer_id");
27 | name = data.getString("name");
28 | description = data.getString("description");
29 | } catch (JSONException e) {
30 | throw new RuntimeException(e);
31 | }
32 | }
33 |
34 | private Game(Parcel in) {
35 | id = in.readString();
36 | name = in.readString();
37 | description = in.readString();
38 | }
39 |
40 | public int describeContents() {
41 | return 0;
42 | }
43 |
44 | public void writeToParcel(Parcel out, int flags) {
45 | out.writeString(id);
46 | out.writeString(name);
47 | out.writeString(description);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/com/geoloqi/data/Geonote.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.data;
2 |
3 | import org.json.JSONException;
4 | import org.json.JSONObject;
5 |
6 | import android.os.Parcel;
7 | import android.os.Parcelable;
8 |
9 | import com.google.android.maps.GeoPoint;
10 |
11 | public class Geonote implements Parcelable {
12 |
13 | public Place place;
14 | public String text;
15 | public long date_created_ts;
16 | public String extra;
17 |
18 | public Geonote(Place place, String text, long timeInSeconds, String extra) {
19 | this.place = place;
20 | this.text = text;
21 | this.date_created_ts = timeInSeconds;
22 | this.extra = extra;
23 | }
24 |
25 | public Geonote(String json) throws JSONException {
26 |
27 | JSONObject obj = new JSONObject(json);
28 | this.date_created_ts = obj.getLong("date_created_ts");
29 | text = obj.getString("text");
30 | int latitude = (int) (obj.getDouble("latitude") * 1000000.);
31 | int longitude = (int) (obj.getDouble("longitude") * 1000000.);
32 | double radius = obj.getDouble("radius");
33 | place = new Place(obj.getString("place_name"), new GeoPoint(latitude, longitude), radius);
34 | }
35 |
36 | public JSONObject castToJSONObject() {
37 | try {
38 | JSONObject obj = new JSONObject();
39 | obj.put("text", text);
40 | obj.put("latitude", place.location.getLatitudeE6() / 1000000.);
41 | obj.put("longitude", place.location.getLongitudeE6() / 1000000.);
42 | obj.put("radius", place.longitudinalRadius);
43 | obj.put("date_created_ts", date_created_ts);
44 | obj.put("place_name", place.name);
45 | if (extra != null) {
46 | obj.put("extra", extra);
47 | }
48 | return obj;
49 | } catch (JSONException e) {
50 | throw new RuntimeException(e);
51 | }
52 | }
53 |
54 | @Override
55 | public int describeContents() {
56 | return 0;
57 | }
58 |
59 | @Override
60 | public void writeToParcel(Parcel out, int flags) {
61 | out.writeParcelable(place, flags);
62 | out.writeString(text);
63 | out.writeLong(date_created_ts);
64 | if (extra == null) {
65 | out.writeInt(0);
66 | } else {
67 | out.writeInt(1);
68 | out.writeString(extra.toString());
69 | }
70 | }
71 |
72 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
73 | @Override
74 | public Geonote createFromParcel(Parcel in) {
75 | Geonote note = new Geonote((Place) in.readParcelable(Place.class.getClassLoader()), in.readString(), in.readLong(), in.readInt() == 1 ? in.readString() : null);
76 | return note;
77 | }
78 |
79 | @Override
80 | public Geonote[] newArray(int size) {
81 | return new Geonote[size];
82 | }
83 | };
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/src/com/geoloqi/data/Layer.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.data;
2 |
3 | import org.json.JSONException;
4 | import org.json.JSONObject;
5 |
6 | public class Layer {
7 |
8 | public String layerId, userId, type, name, icon, description, url;
9 | boolean isPublic, isSubscribed;
10 |
11 | public Layer(JSONObject json) throws JSONException {
12 | layerId = json.getString("layer_id");
13 | userId = json.getString("user_id");
14 | type = json.getString("type");
15 | name = json.getString("name");
16 | icon = json.getString("icon");
17 | description = json.getString("description");
18 | url = json.getString("url");
19 | isPublic = json.getBoolean("subscribed");
20 | isSubscribed = json.getBoolean("subscribed");
21 | }
22 |
23 | public JSONObject castToJSONObject() {
24 | JSONObject obj = new JSONObject();
25 | try {
26 | obj.put("layer_id", layerId);
27 | obj.put("user_id", userId);
28 | obj.put("type", type);
29 | obj.put("name", name);
30 | obj.put("public", isPublic);
31 | obj.put("icon", icon);
32 | obj.put("description", description);
33 | obj.put("subscribed", isSubscribed);
34 | obj.put("url", url);
35 | return obj;
36 | } catch (JSONException e) {
37 | throw new RuntimeException(e);
38 | }
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/com/geoloqi/data/Place.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.data;
2 |
3 | import org.json.JSONException;
4 | import org.json.JSONObject;
5 |
6 | import android.os.Parcel;
7 | import android.os.Parcelable;
8 |
9 | import com.google.android.maps.GeoPoint;
10 |
11 | public class Place implements Parcelable {
12 |
13 | public String name;
14 | public String id = null;
15 | public GeoPoint location;
16 | public double longitudinalRadius;
17 |
18 | public Place(String name, GeoPoint location, double longitudinalRadius) {
19 | this.name = name;
20 | this.location = location;
21 | this.longitudinalRadius = longitudinalRadius;
22 | }
23 |
24 | public Place(String json) throws JSONException {
25 | JSONObject obj = new JSONObject(json);
26 | name = obj.getString("name");
27 | location = new GeoPoint((int) (obj.getDouble("latitude") * 1000000.), (int) (obj.getDouble("longitude") * 1000000.));
28 | longitudinalRadius = obj.getDouble("radius");
29 | if (obj.has("place_id")) {
30 | id = obj.getString("place_id");
31 | }
32 | }
33 |
34 | public boolean isAnonymous() {
35 | return name.equals("");
36 | }
37 |
38 | public JSONObject castToJSONObject() {
39 | try {
40 | JSONObject obj = new JSONObject();
41 | obj.put("name", name);
42 | obj.put("layer_type", "geonotes");
43 | obj.put("latitude", location.getLatitudeE6() / 1000000.);
44 | obj.put("longitude", location.getLongitudeE6() / 1000000.);
45 | obj.put("radius", longitudinalRadius);
46 | if (id != null) {
47 | obj.put("place_id", id);
48 | }
49 | return obj;
50 | } catch (JSONException e) {
51 | throw new RuntimeException(e);
52 | }
53 | }
54 |
55 | @Override
56 | public void writeToParcel(Parcel out, int flags) {
57 | out.writeString(name);
58 | out.writeInt(location.getLatitudeE6());
59 | out.writeInt(location.getLongitudeE6());
60 | out.writeDouble(longitudinalRadius);
61 | if (id == null) {
62 | out.writeInt(0);
63 | } else {
64 | out.writeInt(1);
65 | out.writeString(id);
66 | }
67 | }
68 |
69 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
70 | @Override
71 | public Place createFromParcel(Parcel in) {
72 | Place place = new Place(in.readString(), new GeoPoint(in.readInt(), in.readInt()), in.readDouble());
73 | if (in.readInt() == 1) {
74 | place.id = in.readString();
75 | }
76 | return place;
77 | }
78 |
79 | @Override
80 | public Place[] newArray(int size) {
81 | return new Place[size];
82 | }
83 | };
84 |
85 | @Override
86 | public int describeContents() {
87 | return 0;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/com/geoloqi/data/SharingLink.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.data;
2 |
3 | import org.json.JSONException;
4 | import org.json.JSONObject;
5 |
6 | import android.net.Uri;
7 |
8 | public class SharingLink {
9 |
10 | public Uri link;
11 | public Uri shortLink;
12 | public String token;
13 |
14 | public SharingLink(JSONObject json) throws JSONException {
15 | this.link = Uri.parse(json.getString("link"));
16 | this.shortLink = Uri.parse(json.getString("shortlink"));
17 | this.token = json.getString("token");
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/com/geoloqi/interfaces/AuthChangeListener.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.interfaces;
2 |
3 | public interface AuthChangeListener {
4 |
5 | public void onAuthChanged(int authLevel);
6 |
7 | }
--------------------------------------------------------------------------------
/src/com/geoloqi/interfaces/GeoloqiClient.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.interfaces;
2 |
3 | import org.json.JSONObject;
4 |
5 | import com.geoloqi.data.Fix;
6 | import com.geoloqi.data.Geonote;
7 | import com.geoloqi.data.Layer;
8 | import com.geoloqi.data.Place;
9 | import com.geoloqi.data.SharingLink;
10 |
11 | public interface GeoloqiClient {
12 |
13 | public static final int LOGGED_OUT = 0;
14 | public static final int ANONYMOUS = 1;
15 | public static final int LOGGED_IN = 2;
16 |
17 | public void registerAuthChangeListener(AuthChangeListener authChangeListener);
18 |
19 | public int getAuthLevel();
20 |
21 | public void createAnonymousAccount() throws RPCException;
22 |
23 | public void signUp(String email) throws RPCException;
24 |
25 | public JSONObject logIn(String email, String password) throws RPCException;
26 |
27 | public void logOut();
28 |
29 | public String getDisplayName();
30 |
31 | public void postLocationUpdate(Fix[] locations) throws RPCException;
32 |
33 | public SharingLink postSharingLink(Integer minutes, String message) throws RPCException;
34 |
35 | public String postGeonote(Geonote note) throws RPCException;
36 |
37 | public String postPlace(Place place) throws RPCException;
38 |
39 | public Geonote[] getSetGeonotes() throws RPCException;
40 |
41 | public Geonote[] getRecentGeonotes() throws RPCException;
42 |
43 | public Place[] getPlaces() throws RPCException;
44 |
45 | public Layer[][] getLayers() throws RPCException;
46 |
47 | public String getAccessToken() throws RPCException;
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/com/geoloqi/interfaces/GeoloqiConstantsTemplate.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.interfaces;
2 |
3 | /**
4 | * This is a template file. Rename this class to GeoloqiConstants
5 | * and update GEOLOQI_ID and GEOLOQI_SECRET with your API keys to get started.
6 | *
7 | * @author tristanw
8 | */
9 | public abstract interface GeoloqiConstantsTemplate {
10 | public static final String GEOLOQI_ID = "";
11 | public static final String GEOLOQI_SECRET = "";
12 |
13 | public static final String URL_BASE = "https://api.geoloqi.com/1/";
14 | public static final String GAME_LIST_ADDRESS = URL_BASE + "layer/nearby?application_id=?";
15 | public static final String GAME_REQUEST_ADDRESS = "http://mapattack.org/join/";
16 |
17 | public static final String UPLOAD_ADDRESS = "loki.geoloqi.com";
18 | public static final String DOWNLOAD_ADDRESS = "loki.geoloqi.com";
19 |
20 | public static final int UPLOAD_PORT = 40000;
21 | public static final int DOWNLOAD_PORT = 40001;
22 |
23 | public static final String PREFERENCES_FILE = "GEOLOQIHTTPCLIENT";
24 | public static final String VERSION = "1";
25 | }
26 |
--------------------------------------------------------------------------------
/src/com/geoloqi/interfaces/GeoloqiFixSocket.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.interfaces;
2 |
3 | import com.geoloqi.data.Fix;
4 |
5 | public interface GeoloqiFixSocket {
6 |
7 | public void pushFix(Fix fix);
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/src/com/geoloqi/interfaces/RPCException.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.interfaces;
2 |
3 | @SuppressWarnings("serial")
4 | public class RPCException extends Exception {
5 | public RPCException(String message) {
6 | super(message);
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/com/geoloqi/mapattack/UDPClient.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.mapattack;
2 |
3 | import java.io.IOException;
4 | import java.math.BigDecimal;
5 | import java.math.MathContext;
6 | import java.net.DatagramPacket;
7 | import java.net.DatagramSocket;
8 | import java.net.InetSocketAddress;
9 | import java.util.concurrent.locks.ReentrantLock;
10 |
11 | import android.content.Context;
12 |
13 | import com.geoloqi.Installation;
14 | import com.geoloqi.data.Fix;
15 | import com.geoloqi.interfaces.GeoloqiFixSocket;
16 |
17 | public class UDPClient implements GeoloqiFixSocket {
18 |
19 | public final String uploadHost = "loki.geoloqi.com";
20 | public final int uploadPort = 40000;
21 |
22 | private ReentrantLock lock = new ReentrantLock(true);
23 | private DatagramSocket uploadSocket;
24 |
25 | private static UDPClient client = null;
26 |
27 | Context context;
28 |
29 | public static UDPClient getApplicationClient(Context context) {
30 | if (client == null) {
31 | client = new UDPClient(context);
32 | }
33 | return client;
34 | }
35 |
36 | private UDPClient(Context context) {
37 | this.context = context;
38 | try {
39 | uploadSocket = new DatagramSocket();
40 | uploadSocket.connect(new InetSocketAddress(uploadHost, uploadPort));
41 | uploadSocket.send(new DatagramPacket(new byte[] { 0, 0, 0 }, 3));
42 | } catch (Exception e) {
43 | throw new RuntimeException(e);
44 | }
45 | }
46 |
47 | @Override
48 | public void pushFix(Fix fix) {
49 | lock.lock();
50 | try {
51 | uploadSocket.send(encode(fix));
52 | } catch (IOException e) {
53 | throw new RuntimeException(e);
54 | } finally {
55 | lock.unlock();
56 | }
57 | }
58 |
59 | public void close() {
60 | lock.lock();
61 | try {
62 | uploadSocket.close();
63 | } finally {
64 | lock.unlock();
65 | }
66 | }
67 |
68 | static MathContext math = MathContext.DECIMAL64;
69 | private static final BigDecimal NINETY = new BigDecimal(90., math);
70 | private static final BigDecimal ONE_EIGHTY = new BigDecimal(180., math);
71 | private static final BigDecimal THREE_SIXTY = new BigDecimal(360., math);
72 | private static final BigDecimal MAX = new BigDecimal(4294967295., math);
73 |
74 | private DatagramPacket encode(Fix fix) {
75 | byte[] bytes = new byte[39];
76 | long lat = new BigDecimal(fix.getLatitude()).add(NINETY).divide(ONE_EIGHTY, math).multiply(MAX).round(math).longValue();
77 | long lng = new BigDecimal(fix.getLongitude()).add(ONE_EIGHTY).divide(THREE_SIXTY, math).multiply(MAX).round(math).longValue();
78 |
79 | bytes[0] = 0x41;
80 | blitInt(fix.getTime() / 1000L, bytes, 1);
81 | blitInt(lat, bytes, 5);
82 | blitInt(lng, bytes, 9);
83 | blitShort(fix.getSpeed(), bytes, 13);
84 | blitShort(fix.getBearing(), bytes, 15);
85 | blitShort(fix.getAltitude(), bytes, 17);
86 | blitShort(fix.getAccuracy(), bytes, 19);
87 | blitShort(fix.getExtras() == null ? 0 : fix.getExtras().getInt("battery"), bytes, 21);
88 | blitUUID(bytes, 23);
89 | return new DatagramPacket(bytes, bytes.length);
90 | }
91 |
92 | private void blitInt(long val, byte[] out, int offset) {
93 | out[offset] = (byte) ((val & 0xff000000) >>> 24);
94 | out[offset + 1] = (byte) ((val & 0x00ff0000) >>> 16);
95 | out[offset + 2] = (byte) ((val & 0x0000ff00) >>> 8);
96 | out[offset + 3] = (byte) ((val & 0x000000ff));
97 | }
98 |
99 | private void blitShort(int integer, byte[] out, int offset) {
100 | out[offset] = (byte) ((integer & 0x0000ff00) >>> 8);
101 | out[offset + 1] = (byte) ((integer & 0x000000ff));
102 | }
103 |
104 | private void blitShort(double val, byte[] out, int offset) {
105 | blitShort((int) val, out, offset);
106 | }
107 |
108 | private void blitUUID(byte[] out, int offset) {
109 | byte[] uuid = Installation.getIDAsBytes(context);
110 | for (int i = 0; i < uuid.length; i++) {
111 | out[offset + i] = uuid[i];
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/com/geoloqi/rpc/AccountMonitor.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.rpc;
2 |
3 | import java.util.concurrent.locks.Condition;
4 | import java.util.concurrent.locks.ReentrantLock;
5 |
6 | import android.content.Context;
7 |
8 | import com.geoloqi.interfaces.GeoloqiConstants;
9 | import com.geoloqi.interfaces.RPCException;
10 |
11 | public class AccountMonitor {
12 |
13 | private static ReentrantLock lock = new ReentrantLock();
14 | private static Condition userIDReceived = lock.newCondition();
15 |
16 | public static void createUserID(final Context context) {
17 | new Thread() {
18 | @Override
19 | public void run() {
20 | lock.lock();
21 | try {
22 | MapAttackClient.getApplicationClient(context).createAnonymousAccount();
23 | userIDReceived.signalAll();
24 | } catch (RPCException e) {
25 | throw new RuntimeException(e);
26 | } finally {
27 | lock.unlock();
28 | }
29 | }
30 | }.start();
31 | }
32 |
33 | public static String getUserID(Context context) {
34 | lock.lock();
35 | try {
36 | if (!context.getSharedPreferences(GeoloqiConstants.PREFERENCES_FILE, Context.MODE_PRIVATE).contains("userID")) {
37 | userIDReceived.awaitUninterruptibly();
38 | }
39 | return context.getSharedPreferences(GeoloqiConstants.PREFERENCES_FILE, Context.MODE_PRIVATE).getString("userID", null);
40 | } finally {
41 | lock.unlock();
42 | }
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/com/geoloqi/rpc/ExpiredTokenException.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.rpc;
2 |
3 | import com.geoloqi.interfaces.RPCException;
4 |
5 | @SuppressWarnings("serial")
6 | class ExpiredTokenException extends RPCException {
7 | ExpiredTokenException() {
8 | super("Expired token.");
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/com/geoloqi/rpc/MapAttackClient.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.rpc;
2 |
3 | import java.io.IOException;
4 | import java.util.ArrayList;
5 |
6 | import org.apache.http.Header;
7 | import org.apache.http.ParseException;
8 | import org.apache.http.auth.AuthenticationException;
9 | import org.apache.http.auth.UsernamePasswordCredentials;
10 | import org.apache.http.client.ClientProtocolException;
11 | import org.apache.http.client.HttpClient;
12 | import org.apache.http.impl.auth.BasicScheme;
13 | import org.apache.http.impl.client.DefaultHttpClient;
14 | import org.apache.http.message.BasicNameValuePair;
15 | import org.apache.http.params.BasicHttpParams;
16 | import org.apache.http.params.HttpConnectionParams;
17 | import org.apache.http.params.HttpParams;
18 | import org.apache.http.util.EntityUtils;
19 | import org.json.JSONArray;
20 | import org.json.JSONException;
21 | import org.json.JSONObject;
22 |
23 | import android.content.Context;
24 | import android.content.SharedPreferences;
25 | import android.util.Log;
26 |
27 | import com.geoloqi.ADB;
28 | import com.geoloqi.Installation;
29 | import com.geoloqi.data.Game;
30 | import com.geoloqi.interfaces.GeoloqiConstants;
31 | import com.geoloqi.interfaces.RPCException;
32 |
33 | public class MapAttackClient implements GeoloqiConstants {
34 | private final String TAG = "MapAttackClient";
35 |
36 | private static final int TIMEOUT = 60000;
37 |
38 | private static final HttpParams httpParams = new BasicHttpParams();
39 | private static HttpClient client;
40 | private static MapAttackClient singleton = null;
41 | private Context context;
42 |
43 | public static MapAttackClient getApplicationClient(Context context) {
44 | if (singleton == null) {
45 | singleton = new MapAttackClient(context);
46 | }
47 | return singleton;
48 | }
49 |
50 | private MapAttackClient(Context context) {
51 | this.context = context;
52 | HttpConnectionParams.setConnectionTimeout(httpParams, TIMEOUT);
53 | HttpConnectionParams.setSoTimeout(httpParams, TIMEOUT);
54 | client = new DefaultHttpClient(httpParams);
55 | }
56 |
57 | public void createAnonymousAccount() throws RPCException {
58 | try {
59 | String name, deviceID, platform, hardware;
60 | {// Initialize variables.
61 | name = context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE).getString("initials", null);
62 | deviceID = Installation.getIDAsString(context);
63 | platform = android.os.Build.VERSION.RELEASE;
64 | hardware = android.os.Build.MODEL;
65 | }
66 |
67 | MyRequest request;
68 | {
69 | request = new MyRequest(MyRequest.POST, URL_BASE + "user/create_anon");
70 | request.addHeaders(new BasicScheme().authenticate(new UsernamePasswordCredentials(GEOLOQI_ID, GEOLOQI_SECRET), request.getRequest()));
71 | request.addEntityParams(pair("name", name), pair("device_id", deviceID), pair("platform", platform), pair("hardware", hardware));
72 | }
73 |
74 | JSONObject response = send(request);
75 |
76 | {//Save Results
77 | saveToken(new OAuthToken(response));
78 | context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE).edit().putString("userID", response.getString("user_id")).commit();
79 | }
80 | } catch (JSONException e) {
81 | throw new RuntimeException(e.getMessage());
82 | } catch (AuthenticationException e) {
83 | throw new RuntimeException(e);
84 | }
85 | }
86 |
87 | protected void saveToken(OAuthToken token) {
88 | context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE).edit().putString("authToken", token.accessToken).commit();
89 | }
90 |
91 | public boolean hasToken() {
92 | return context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE).contains("authToken");
93 | }
94 |
95 | public String getToken() {
96 | return context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE).getString("authToken", null);
97 | }
98 |
99 | public ArrayList getGames(Double latitude, Double longitude) throws RPCException {
100 | MyRequest request = new MyRequest(MyRequest.GET,
101 | GAME_LIST_ADDRESS + "&latitude=" + latitude + "&longitude=" + longitude);
102 | Header authHeader;
103 | try {
104 | authHeader = new BasicScheme().authenticate(new UsernamePasswordCredentials(GEOLOQI_ID, GEOLOQI_SECRET), request.getRequest());
105 | } catch (AuthenticationException e) {
106 | throw new RPCException(e.getMessage());
107 | }
108 | request.addHeaders(authHeader);
109 | JSONObject response = send(request);
110 | try {
111 | JSONArray gamesArray = response.getJSONArray("nearby");
112 | ArrayList games = new ArrayList();
113 | for (int i = 0; i < gamesArray.length(); i++) {
114 | games.add(new Game(gamesArray.getJSONObject(i)));
115 | }
116 | return games;
117 | } catch (JSONException e) {
118 | throw new RuntimeException(e);
119 | }
120 | }
121 |
122 | /**
123 | * Get the best guess intersection name for the given latitude and longitude.
124 | *
125 | * @param latitude
126 | * @param longitude
127 | * @return The nearest intersection as a String or null.
128 | */
129 | public String getNearestIntersection(final Double latitude, final Double longitude) {
130 | // Build the request
131 | final MyRequest request = new MyRequest(MyRequest.GET,
132 | String.format("%slocation/context?latitude=%s&longitude=%s", URL_BASE, latitude, longitude));
133 |
134 | try {
135 | // Sign the request
136 | Header authHeader = new BasicScheme().authenticate(
137 | new UsernamePasswordCredentials(GEOLOQI_ID, GEOLOQI_SECRET), request.getRequest());
138 | request.addHeaders(authHeader);
139 |
140 | try {
141 | // Get the response
142 | JSONObject response = send(request);
143 | return response.getString("best_name");
144 | } catch (RPCException e) {
145 | Log.e(TAG, "Got an RPCException when fetching the nearest intersection name!", e);
146 | } catch (JSONException e) {
147 | Log.e(TAG, "Got a JSONException when fetching the nearest intersection name!", e);
148 | }
149 | } catch (AuthenticationException e) {
150 | Log.e(TAG, "Got an AuthenticationException when fetching the nearest intersection name!", e);
151 | }
152 |
153 | return null;
154 | }
155 |
156 | public void joinGame(String id) throws RPCException {
157 | String token, email, initials;
158 | {// Initialize variables
159 | SharedPreferences prefs = context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE);
160 | token = prefs.getString("authToken", null);
161 | email = prefs.getString("email", null);
162 | initials = prefs.getString("initials", null);
163 | }
164 | MyRequest request;
165 | {// Initialize the request.
166 | request = new MyRequest(MyRequest.POST, "http://mapattack.org/game/" + id + "/join");
167 | request.addEntityParams(pair("access_token", token), pair("email", email), pair("initials", initials));
168 | }
169 |
170 | try {// Send will throw a RuntimeException for the non-JSON return value.
171 | send(request);
172 | } catch (RuntimeException e) {
173 | }
174 | }
175 |
176 | protected synchronized JSONObject send(MyRequest request) throws RPCException {
177 | ADB.log("param " + request.getRequest().getURI());
178 | JSONObject response;
179 | try {
180 | response = new JSONObject(EntityUtils.toString(client.execute(request.getRequest()).getEntity()));
181 | } catch (ParseException e) {
182 | ADB.log("ParseException: " + e.getMessage());
183 | throw new RuntimeException(e.getMessage());
184 | } catch (JSONException e) {
185 | ADB.log("JSONException: " + e.getMessage());
186 | throw new RuntimeException(e.getMessage());
187 | } catch (ClientProtocolException e) {
188 | ADB.log("ClientProtocolException: " + e.getMessage());
189 | throw new RPCException(e.getMessage());
190 | } catch (IOException e) {
191 | ADB.log("IOException: " + e.getMessage());
192 | throw new RPCException(e.getMessage());
193 | }
194 |
195 | if (response.has("error")) {
196 | try {
197 | throw new RPCException(response.getString("error"));
198 | } catch (JSONException e) {
199 | throw new RuntimeException(e);
200 | }
201 | } else {
202 | return response;
203 | }
204 | }
205 |
206 | private static BasicNameValuePair pair(String key, String val) {
207 | return new BasicNameValuePair(key, val);
208 | }
209 |
210 | }
211 |
--------------------------------------------------------------------------------
/src/com/geoloqi/rpc/MyRequest.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.rpc;
2 |
3 | import java.io.UnsupportedEncodingException;
4 | import java.util.ArrayList;
5 |
6 | import org.apache.http.Header;
7 | import org.apache.http.client.entity.UrlEncodedFormEntity;
8 | import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
9 | import org.apache.http.client.methods.HttpGet;
10 | import org.apache.http.client.methods.HttpPost;
11 | import org.apache.http.client.methods.HttpRequestBase;
12 | import org.apache.http.entity.AbstractHttpEntity;
13 | import org.apache.http.message.BasicHeader;
14 | import org.apache.http.message.BasicNameValuePair;
15 | import org.apache.http.params.BasicHttpParams;
16 |
17 | import com.geoloqi.ADB;
18 |
19 | class MyRequest {
20 | static final int GET = 0;
21 | static final int POST = 1;
22 |
23 | private ArrayList headers = new ArrayList();
24 | private BasicHttpParams params = new BasicHttpParams();
25 | private ArrayList entityParams = new ArrayList();
26 | private AbstractHttpEntity entity = null;
27 |
28 | private final HttpRequestBase request;
29 |
30 | MyRequest(int requestType, String url) {
31 | switch (requestType) {
32 | case GET:
33 | request = new HttpGet(url);
34 | break;
35 | case POST:
36 | request = new HttpPost(url);
37 | break;
38 | default:
39 | throw new IllegalArgumentException("Request type must be one of the static types.");
40 | }
41 | }
42 |
43 | synchronized void authorize(OAuthToken token) {
44 | ADB.log("In authorize");
45 | // Remove the old authorization header.
46 | for (Header header : headers) {
47 | if (header.getName().equals("Authorization")) {
48 | headers.remove(header);
49 | }
50 | }
51 | // Insert the new authorization header.
52 | headers.add(new BasicHeader("Authorization", "OAuth " + token.accessToken));
53 | }
54 |
55 | synchronized void addHeaders(Header... headers) {
56 | ADB.log("In addHeaders");
57 | for (int i = 0; i < headers.length; i++) {
58 | this.headers.add(headers[i]);
59 | }
60 | }
61 |
62 | synchronized void addParams(BasicNameValuePair... pairs) {
63 | ADB.log("In addParams");
64 | for (int i = 0; i < pairs.length; i++) {
65 | params.setParameter(pairs[i].getName(), pairs[i].getValue());
66 | }
67 | }
68 |
69 | synchronized void addEntityParams(BasicNameValuePair... pairs) {
70 | ADB.log("In addEntityParams");
71 | if (request instanceof HttpEntityEnclosingRequestBase) {
72 | for (int i = 0; i < pairs.length; i++) {
73 | entityParams.add(pairs[i]);
74 | }
75 | } else {
76 | throw new RuntimeException("Request must be PUT or POST to enclose an entity.");
77 | }
78 | }
79 |
80 | synchronized void setEntity(AbstractHttpEntity entity) {
81 | ADB.log("In setEntity");
82 | if (request instanceof HttpEntityEnclosingRequestBase) {
83 | this.entity = entity;
84 | } else {
85 | throw new RuntimeException("Request must be PUT or POST to enclose an entity.");
86 | }
87 | }
88 |
89 | synchronized public HttpRequestBase getRequest() {
90 | ADB.log("------------------------------------------");
91 | ADB.log("URI: " + request.getURI());
92 | for (Header header : headers) {
93 | ADB.log("Header: " + header.getName() + "=" + header.getValue());
94 | }
95 | ADB.log("Parameters: " + params.toString());
96 | for (BasicNameValuePair param : entityParams) {
97 | ADB.log("Entity Parameter: " + param.getName() + "=" + param.getValue());
98 | }
99 | //Set headers
100 | if (headers.size() > 0) {
101 | request.setHeaders(headers.toArray(new Header[headers.size()]));
102 | }
103 | //Set params
104 | request.setParams(params);
105 | //Set entity
106 | if (request instanceof HttpEntityEnclosingRequestBase) {
107 | if (entityParams.size() > 0) {
108 | if (entity != null) {
109 | throw new RuntimeException("Entity conflict");
110 | } else {
111 | try {
112 | ((HttpEntityEnclosingRequestBase) request).setEntity(new UrlEncodedFormEntity(entityParams));
113 | } catch (UnsupportedEncodingException e) {
114 | throw new RuntimeException(e.getMessage());
115 | }
116 | }
117 | } else if (entity != null) {
118 | ((HttpEntityEnclosingRequestBase) request).setEntity(entity);
119 | }
120 | }
121 | return request;
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/com/geoloqi/rpc/OAuthToken.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.rpc;
2 |
3 | import org.json.JSONException;
4 | import org.json.JSONObject;
5 |
6 | class OAuthToken {
7 | public final String accessToken;
8 |
9 | public OAuthToken(JSONObject json) throws JSONException {
10 | accessToken = json.get("access_token").toString();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/com/geoloqi/services/AndroidPushNotifications.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.services;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 | import java.io.InputStreamReader;
6 | import java.io.PrintWriter;
7 | import java.net.Socket;
8 | import java.util.Hashtable;
9 |
10 | import org.json.JSONException;
11 | import org.json.JSONObject;
12 |
13 | import android.app.Notification;
14 | import android.app.NotificationManager;
15 | import android.app.PendingIntent;
16 | import android.app.Service;
17 | import android.content.Context;
18 | import android.content.Intent;
19 | import android.media.AudioManager;
20 | import android.media.SoundPool;
21 | import android.os.IBinder;
22 | import android.util.Log;
23 |
24 | import com.geoloqi.ADB;
25 | import com.geoloqi.Installation;
26 | import com.geoloqi.mapattack.R;
27 | import com.geoloqi.ui.MapAttackActivity;
28 |
29 | import static com.geoloqi.interfaces.GeoloqiConstants.PREFERENCES_FILE;
30 | import static com.geoloqi.interfaces.GeoloqiConstants.DOWNLOAD_ADDRESS;
31 | import static com.geoloqi.interfaces.GeoloqiConstants.DOWNLOAD_PORT;
32 |
33 | public class AndroidPushNotifications extends Service {
34 | public static final String TAG = "AndroidPushNotifications";
35 |
36 | Notifier notifier;
37 | boolean running = true;
38 | int exceptions = 0;
39 |
40 | private SoundPool soundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
41 | Hashtable sounds = new Hashtable();
42 |
43 | private void initializeSounds() {
44 | sounds.put("pop", soundPool.load(this, R.raw.pop, 1));
45 | }
46 |
47 | private void playSound(String name) {
48 | if (sounds.containsKey(name)) {
49 | soundPool.play(sounds.get(name), 1f, 1f, 0, 0, 1f);
50 | } else {
51 | ADB.log("Could not play sound: " + name);
52 | }
53 | }
54 |
55 | @Override
56 | public IBinder onBind(Intent arg0) {
57 | return null;
58 | }
59 |
60 | @Override
61 | public void onStart(Intent intent, int startId) {
62 | initializeSounds();
63 | notifier = new Notifier();
64 | notifier.start();
65 | }
66 |
67 | @Override
68 | public int onStartCommand(Intent intent, int flags, int startId) {
69 | onStart(intent, startId);
70 | return Service.START_REDELIVER_INTENT;
71 | }
72 |
73 | @Override
74 | public void onDestroy() {
75 | super.onDestroy();
76 | running = false;
77 |
78 | }
79 |
80 | class Notifier extends Thread {
81 |
82 | Socket socket;
83 | String userID;
84 |
85 | @Override
86 | public void run() {
87 | try {
88 | {//Initialize variables.
89 | userID = AndroidPushNotifications.this.getSharedPreferences(
90 | PREFERENCES_FILE, Context.MODE_PRIVATE).getString("userID", null);
91 | socket = new Socket(DOWNLOAD_ADDRESS, DOWNLOAD_PORT);
92 | }
93 | while (running) {
94 | PrintWriter out = new PrintWriter(socket.getOutputStream());
95 | out.print(Installation.getIDAsString(AndroidPushNotifications.this));
96 | out.flush();
97 | BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
98 | if (in != null && in.readLine().equals("ok")) {
99 | while (running) {
100 | handleJSON(in.readLine());
101 | }
102 | }
103 | }
104 | } catch (IOException e) {
105 | if (running) {
106 | exceptions++;
107 | if (exceptions < 10) {
108 | run();
109 | } else {
110 | Log.w(TAG, "Too many IOExceptions! Lost network connectivity?");
111 | // TODO: Restart when connectivity resumes
112 | running = false;
113 | }
114 | }
115 | }
116 | }
117 |
118 | public void handleJSON(String json) {
119 | try {
120 | if (json != null) {
121 | JSONObject obj = new JSONObject(json);
122 | if (obj.has("aps")) {
123 | JSONObject aps = obj.getJSONObject("aps");
124 | if (aps.has("alert")) {
125 | notify(aps.getJSONObject("alert").getString("body"));
126 | }
127 | } else if (obj.has("mapattack")) {
128 | JSONObject subObj = obj.getJSONObject("mapattack");
129 | if (subObj.has("triggered_user_id")) {
130 | String id = subObj.getString("triggered_user_id");
131 | if (id.equals(userID)) {
132 | playSound("pop");
133 | }
134 | }
135 | }
136 | if (!obj.has("aps") || obj.length() > 2) {
137 | forward(json);
138 | }
139 | }
140 | } catch (JSONException e) {
141 | ADB.log("Could not parse string: " + json);
142 | return;
143 | }
144 | }
145 |
146 | public void notify(String tickerText) {
147 | Notification notification = new Notification(R.drawable.ic_stat_notify, tickerText, System.currentTimeMillis());
148 | CharSequence contentTitle = "Geoloqi";
149 | CharSequence contentText = tickerText;
150 | Intent contentIntent = new Intent(AndroidPushNotifications.this, MapAttackActivity.class);
151 | contentIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
152 | PendingIntent notificationIntent = PendingIntent.getActivity(AndroidPushNotifications.this, 0, contentIntent, 0);
153 | notification.flags = Notification.FLAG_AUTO_CANCEL;
154 | notification.setLatestEventInfo(AndroidPushNotifications.this, contentTitle, contentText, notificationIntent);
155 | ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).notify(1, notification);
156 | }
157 |
158 | public void forward(String json) {
159 | Intent notifyPush = new Intent("PUSH");
160 | notifyPush.putExtra("json", json);
161 | sendBroadcast(notifyPush);
162 | }
163 |
164 | };
165 |
166 | }
167 |
--------------------------------------------------------------------------------
/src/com/geoloqi/services/GeoloqiPositioning.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.services;
2 |
3 | import android.app.Service;
4 | import android.content.BroadcastReceiver;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.IntentFilter;
8 | import android.location.Location;
9 | import android.location.LocationListener;
10 | import android.location.LocationManager;
11 | import android.net.ConnectivityManager;
12 | import android.net.NetworkInfo;
13 | import android.os.Bundle;
14 | import android.os.IBinder;
15 | import android.util.Log;
16 | import android.util.Pair;
17 |
18 | import com.geoloqi.ADB;
19 | import com.geoloqi.data.Fix;
20 | import com.geoloqi.interfaces.GeoloqiFixSocket;
21 | import com.geoloqi.mapattack.UDPClient;
22 |
23 | public class GeoloqiPositioning extends Service implements LocationListener {
24 | public static final String TAG = "GeoloqiPositioning";
25 |
26 | private int batteryLevel = 0;
27 |
28 | GeoloqiFixSocket fixSocket;
29 |
30 | @Override
31 | public void onCreate() {
32 | if (isConnected()) {
33 | fixSocket = UDPClient.getApplicationClient(this);
34 | } else {
35 | // TODO: This is a crude check. Should probably be rolled into UDPClient class directly.
36 | Log.w(TAG, "Network unavailable! Stopping positioning service.");
37 | stopSelf();
38 | }
39 | }
40 |
41 | @Override
42 | public IBinder onBind(Intent intent) {
43 | return null;
44 | }
45 |
46 | @Override
47 | public void onStart(Intent intent, int startid) {
48 | registerReceiver(batteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
49 | for (String provider : ((LocationManager) getSystemService(LOCATION_SERVICE)).getAllProviders()) {
50 | if (!provider.equals("passive")) {
51 | ADB.log("Registering for updates with " + provider);
52 | ((LocationManager) getSystemService(LOCATION_SERVICE)).requestLocationUpdates(provider, 0, 0, this);
53 | }
54 | }
55 | }
56 |
57 | public void onStop() {
58 | unregisterReceiver(batteryReceiver);
59 | ((LocationManager) getSystemService(LOCATION_SERVICE)).removeUpdates(this);
60 | }
61 |
62 | @Override
63 | public void onDestroy() {
64 | unregisterReceiver(batteryReceiver);
65 | ((LocationManager) getSystemService(LOCATION_SERVICE)).removeUpdates(this);
66 | }
67 |
68 | @Override
69 | public int onStartCommand(Intent intent, int flags, int startid) {
70 | onStart(intent, startid);
71 | return Service.START_REDELIVER_INTENT;
72 | }
73 |
74 | @Override
75 | public void onLocationChanged(Location location) {
76 | @SuppressWarnings("unchecked")
77 | Fix lqLocation = new Fix(location, new Pair("battery", "" + batteryLevel));
78 |
79 | if (isConnected()) {
80 | fixSocket.pushFix(lqLocation);
81 | } else {
82 | // TODO: This is a crude check. Should probably be rolled into UDPClient class directly.
83 | Log.w(TAG, "Network unavailable, failed to push location fix!");
84 | }
85 | }
86 |
87 | @Override
88 | public void onProviderDisabled(String provider) {
89 | }
90 |
91 | @Override
92 | public void onProviderEnabled(String provider) {
93 | }
94 |
95 | @Override
96 | public void onStatusChanged(String provider, int status, Bundle extras) {
97 | }
98 |
99 | BroadcastReceiver batteryReceiver = new BroadcastReceiver() {
100 |
101 | @Override
102 | public void onReceive(Context context, Intent intent) {
103 | batteryLevel = intent.getIntExtra("level", 0);
104 | }
105 | };
106 |
107 | /** Determine if the network is connected and available. */
108 | private boolean isConnected() {
109 | ConnectivityManager manager = (ConnectivityManager) getApplicationContext()
110 | .getSystemService(Context.CONNECTIVITY_SERVICE);
111 | NetworkInfo activeNetwork = manager.getActiveNetworkInfo();
112 |
113 | return (activeNetwork != null && activeNetwork.isConnected());
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/com/geoloqi/ui/GameListActivity.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.ui;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import android.app.ListActivity;
7 | import android.app.ProgressDialog;
8 | import android.content.Context;
9 | import android.content.Intent;
10 | import android.location.Location;
11 | import android.location.LocationManager;
12 | import android.net.Uri;
13 | import android.os.AsyncTask;
14 | import android.os.Bundle;
15 | import android.os.Parcelable;
16 | import android.text.TextUtils;
17 | import android.util.Log;
18 | import android.view.View;
19 | import android.view.View.OnClickListener;
20 | import android.widget.Button;
21 | import android.widget.ImageButton;
22 | import android.widget.ListView;
23 | import android.widget.ProgressBar;
24 | import android.widget.TextView;
25 |
26 | import com.geoloqi.mapattack.R;
27 | import com.geoloqi.data.Game;
28 | import com.geoloqi.interfaces.RPCException;
29 | import com.geoloqi.rpc.MapAttackClient;
30 | import com.geoloqi.services.GeoloqiPositioning;
31 | import com.geoloqi.widget.GameListArrayAdapter;
32 |
33 | public class GameListActivity extends ListActivity implements OnClickListener {
34 | public static final String TAG = "GameListActivity";
35 |
36 | public static final String PARAM_GAME_LIST = "game_list";
37 | public static final String PARAM_NEAREST_INTERSECTION = "nearest_intersection";
38 | public static final String PARAM_SYNC_ON_START = "sync_on_start";
39 |
40 | private boolean mSyncOnStart = true;
41 | private Intent mPositioningIntent;
42 | private ArrayList mGameList = null;
43 | private String mNearestIntersection = null;
44 |
45 | @Override
46 | public void onCreate(Bundle savedInstanceState) {
47 | super.onCreate(savedInstanceState);
48 | setContentView(R.layout.game_list_activity);
49 |
50 | // Find our views
51 | final Button refreshButton = (Button) findViewById(R.id.refresh_button);
52 | final ImageButton geoloqiButton = (ImageButton) findViewById(R.id.geoloqi);
53 |
54 | // Set our on click listeners
55 | refreshButton.setOnClickListener(this);
56 | geoloqiButton.setOnClickListener(this);
57 |
58 | // Reference our positioning service Intent
59 | mPositioningIntent = new Intent(this, GeoloqiPositioning.class);
60 |
61 | if (savedInstanceState != null) {
62 | // Restore our saved instance state
63 | mSyncOnStart = savedInstanceState.getBoolean(PARAM_SYNC_ON_START, true);
64 | mNearestIntersection = savedInstanceState.getString(PARAM_NEAREST_INTERSECTION);
65 | mGameList = savedInstanceState.getParcelableArrayList(PARAM_GAME_LIST);
66 |
67 | setNearestIntersection(mNearestIntersection);
68 | populateGameList(mGameList);
69 | }
70 |
71 | if (mSyncOnStart || mGameList.isEmpty()) {
72 | // Start our positioning service
73 | stopService(mPositioningIntent);
74 | startService(mPositioningIntent);
75 |
76 | // Search for nearby games
77 | setLoading(true);
78 | new RequestGamesListTask(this, getLastKnownLocation(), false).execute();
79 | }
80 | }
81 |
82 | @Override
83 | public void onDestroy() {
84 | super.onDestroy();
85 | stopService(mPositioningIntent);
86 | }
87 |
88 | @Override
89 | public void onSaveInstanceState(Bundle outState) {
90 | super.onSaveInstanceState(outState);
91 |
92 | outState.putBoolean(PARAM_SYNC_ON_START, false);
93 | outState.putString(PARAM_NEAREST_INTERSECTION, mNearestIntersection);
94 | outState.putParcelableArrayList(PARAM_GAME_LIST,
95 | (ArrayList extends Parcelable>) mGameList);
96 | }
97 |
98 | /**
99 | * Populate the ListView with a new GameListArrayAdapter
100 | * from the provided List of Game objects.
101 | *
102 | * @param games
103 | */
104 | private void populateGameList(final ArrayList games) {
105 | setLoading(false);
106 | if (games != null) {
107 | mGameList = games;
108 | setListAdapter(new GameListArrayAdapter(this, R.layout.game_list_element,
109 | mGameList.toArray(new Game[mGameList.size()])));
110 | }
111 | }
112 |
113 | /**
114 | * Set the game list label with the nearest intersection.
115 | *
116 | * @param intersection
117 | */
118 | private void setNearestIntersection(final String intersection) {
119 | if (!TextUtils.isEmpty(intersection)) {
120 | mNearestIntersection = intersection;
121 | TextView textView = (TextView) findViewById(R.id.game_list_label);
122 | if (textView != null) {
123 | textView.setText(String.format("Games near %s", intersection));
124 | }
125 | }
126 | }
127 |
128 | /** Get the last known location from the device. */
129 | private Location getLastKnownLocation() {
130 | LocationManager lm = ((LocationManager) getSystemService(LOCATION_SERVICE));
131 | List providers = lm.getAllProviders();
132 | for (String provider : providers) {
133 | Location last = lm.getLastKnownLocation(provider);
134 | if (last != null) {
135 | return last;
136 | }
137 | }
138 | return null;
139 | }
140 |
141 | @Override
142 | public void onListItemClick(ListView l, View v, int position, long id) {
143 | final Game selection = (Game) l.getItemAtPosition(position);
144 |
145 | // Start the MapAttackActivity for the indicated game
146 | Intent intent = new Intent(this, MapAttackActivity.class);
147 | intent.putExtra(MapAttackActivity.PARAM_GAME_ID, selection.id);
148 | startActivity(intent);
149 | }
150 |
151 | @Override
152 | public void onClick(View view) {
153 | switch(view.getId()) {
154 | case R.id.refresh_button:
155 | new RequestGamesListTask(this, getLastKnownLocation()).execute();
156 | break;
157 | case R.id.geoloqi:
158 | final Intent geoloqiIntent = new Intent(Intent.ACTION_VIEW,
159 | Uri.parse("https://geoloqi.com/"));
160 | geoloqiIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
161 | geoloqiIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
162 | startActivity(geoloqiIntent);
163 | break;
164 | }
165 | }
166 |
167 | /** Show or hide the loading indicator. */
168 | private void setLoading(boolean loading) {
169 | ProgressBar spinner = (ProgressBar) findViewById(R.id.loading);
170 | ListView listView = getListView();
171 | View emptyView = listView.getEmptyView();
172 |
173 | if (loading) {
174 | spinner.setVisibility(View.VISIBLE);
175 | listView.setVisibility(View.GONE);
176 | emptyView.setVisibility(View.GONE);
177 | } else {
178 | spinner.setVisibility(View.GONE);
179 | listView.setVisibility(View.VISIBLE);
180 | emptyView.setVisibility(View.GONE);
181 | }
182 | }
183 |
184 | /**
185 | * A simple AsyncTask to request the game list from the server.
186 | * @TODO: Move this to an external class file.
187 | * */
188 | private static class RequestGamesListTask extends AsyncTask> {
189 | private final Context mContext;
190 | private final Location mLocation;
191 |
192 | private String mIntersection = null;
193 | private ProgressDialog mProgressDialog = null;
194 |
195 | public RequestGamesListTask(final Context context, final Location location) {
196 | this(context, location, true);
197 | }
198 |
199 | public RequestGamesListTask(final Context context, final Location location, final boolean displayDialog) {
200 | mContext = context;
201 | mLocation = location;
202 |
203 | // Build a progress dialog
204 | if (displayDialog) {
205 | mProgressDialog = new ProgressDialog(context);
206 | mProgressDialog.setTitle(null);
207 | mProgressDialog.setMessage(context.getString(R.string.game_list_loading_text));
208 | }
209 | }
210 |
211 | @Override
212 | protected void onPreExecute() {
213 | // Show our progress dialog
214 | if (mProgressDialog != null) {
215 | mProgressDialog.show();
216 | }
217 | }
218 |
219 | @Override
220 | protected ArrayList doInBackground(Void... params) {
221 | if (mLocation != null) {
222 | try {
223 | // Get the MapAttackClient
224 | final MapAttackClient client = MapAttackClient.getApplicationClient(mContext);
225 |
226 | // Get the nearest intersection
227 | mIntersection = client.getNearestIntersection(mLocation.getLatitude(),
228 | mLocation.getLongitude());
229 |
230 | // Get the game list
231 | return client.getGames(mLocation.getLatitude(), mLocation.getLongitude());
232 | } catch (RPCException e) {
233 | Log.e(TAG, "Got an RPCException when looking for nearby games.", e);
234 | }
235 | }
236 | return new ArrayList();
237 | }
238 |
239 | @Override
240 | protected void onPostExecute(ArrayList games) {
241 | try {
242 | final GameListActivity activity = (GameListActivity) mContext;
243 | activity.setNearestIntersection(mIntersection);
244 | activity.populateGameList(games);
245 | } catch (ClassCastException e) {
246 | Log.w(TAG, "Got a ClassCastException when trying to update the game list!", e);
247 | }
248 |
249 | // Dismiss our progress dialog
250 | if (mProgressDialog != null) {
251 | mProgressDialog.dismiss();
252 | }
253 | }
254 | }
255 | }
256 |
--------------------------------------------------------------------------------
/src/com/geoloqi/ui/MapAttackActivity.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.ui;
2 |
3 | import android.app.Activity;
4 | import android.content.BroadcastReceiver;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.IntentFilter;
8 | import android.os.Bundle;
9 | import android.util.Log;
10 | import android.view.Menu;
11 | import android.view.MenuInflater;
12 | import android.view.MenuItem;
13 | import android.view.View;
14 | import android.view.WindowManager;
15 | import android.webkit.WebView;
16 | import android.webkit.WebViewClient;
17 | import android.widget.ProgressBar;
18 | import android.widget.Toast;
19 |
20 | import com.geoloqi.mapattack.R;
21 | import com.geoloqi.interfaces.RPCException;
22 | import com.geoloqi.rpc.AccountMonitor;
23 | import com.geoloqi.rpc.MapAttackClient;
24 | import com.geoloqi.services.AndroidPushNotifications;
25 |
26 | public class MapAttackActivity extends Activity {
27 | public static final String TAG = "MapAttackActivity";
28 |
29 | public static final String PARAM_GAME_ID = "game_id";
30 |
31 | private String mGameId;
32 | private String mGameUrl;
33 | private WebView mWebView;
34 | private Intent mPushNotificationIntent;
35 |
36 | @Override
37 | public void onCreate(Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 | setContentView(R.layout.main);
40 |
41 | final Bundle extras = getIntent().getExtras();
42 | if (extras != null) {
43 | mGameId = getIntent().getExtras().getString(PARAM_GAME_ID);
44 | }
45 |
46 | // Keep the screen lit while this Activity is visible
47 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
48 |
49 | // Build game
50 | mGameUrl = String.format("http://mapattack.org/game/%s", mGameId);
51 | mWebView = (WebView) findViewById(R.id.webView);
52 | mPushNotificationIntent = new Intent(this, AndroidPushNotifications.class);
53 |
54 | // Prepare the web view
55 | mWebView.clearCache(false);
56 | mWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
57 | mWebView.getSettings().setJavaScriptEnabled(true);
58 | mWebView.setWebViewClient(mWebViewClient);
59 |
60 | // Show the loading indicator
61 | setLoading(true);
62 | }
63 |
64 | @Override
65 | public void onStart() {
66 | super.onStart();
67 |
68 | final MapAttackClient client = MapAttackClient.getApplicationClient(this);
69 |
70 | // Check for a valid account token
71 | if (!client.hasToken()) {
72 | // Kick user out to the sign in activity
73 | Intent intent = new Intent(this, SignInActivity.class);
74 | intent.putExtra(MapAttackActivity.PARAM_GAME_ID, mGameId);
75 | startActivity(intent);
76 | finish();
77 | } else {
78 | try {
79 | // Stop any previously started services and broadcast receivers
80 | unregisterReceiver(mPushReceiver);
81 | stopService(mPushNotificationIntent);
82 | } catch (IllegalArgumentException e) {
83 | Log.w(TAG, "Trying to unregister an inactive push receiver.");
84 | }
85 |
86 | // Start our services
87 | registerReceiver(mPushReceiver, new IntentFilter("PUSH"));
88 | startService(mPushNotificationIntent);
89 |
90 | try {
91 | // Join the game
92 | client.joinGame(mGameId);
93 |
94 | // Load the game into the WebView
95 | mWebView.loadUrl(String.format("%s?id=%s", mGameUrl,
96 | AccountMonitor.getUserID(this)));
97 | } catch (RPCException e) {
98 | Log.e(TAG, "Got an RPCException when trying to join the game!", e);
99 | Toast.makeText(this, R.string.error_join_game, Toast.LENGTH_LONG).show();
100 | finish();
101 | }
102 | }
103 | }
104 |
105 | @Override
106 | public void onStop() {
107 | super.onStop();
108 | try {
109 | unregisterReceiver(mPushReceiver);
110 | stopService(mPushNotificationIntent);
111 | } catch (IllegalArgumentException e) {
112 | Log.w(TAG, "Trying to unregister an inactive push receiver.");
113 | }
114 | }
115 |
116 | @Override
117 | public boolean onCreateOptionsMenu(Menu menu) {
118 | MenuInflater inflater = getMenuInflater();
119 | inflater.inflate(R.menu.game_menu, menu);
120 | return super.onCreateOptionsMenu(menu);
121 | }
122 |
123 | @Override
124 | public boolean onOptionsItemSelected(MenuItem item) {
125 | switch (item.getItemId()) {
126 | case R.id.share:
127 | Intent shareIntent = new Intent(Intent.ACTION_SEND);
128 | shareIntent.setType("text/plain");
129 | shareIntent.putExtra(Intent.EXTRA_TEXT,
130 | String.format("Map Attack! %s #mapattack", mGameUrl));
131 | startActivity(Intent.createChooser(shareIntent, "Share this map: "));
132 | return true;
133 | case R.id.quit:
134 | finish();
135 | return true;
136 | }
137 | return false;
138 | }
139 |
140 | /** Show or hide the loading indicator. */
141 | private void setLoading(boolean loading) {
142 | ProgressBar spinner = (ProgressBar) findViewById(R.id.loading);
143 |
144 | if (loading) {
145 | spinner.setVisibility(View.VISIBLE);
146 | mWebView.setVisibility(View.GONE);
147 | } else {
148 | spinner.setVisibility(View.GONE);
149 | mWebView.setVisibility(View.VISIBLE);
150 | }
151 | }
152 |
153 | /** A reference to the WebViewClient that hosts the MapAttack game. */
154 | private WebViewClient mWebViewClient = new WebViewClient() {
155 | @Override
156 | public boolean shouldOverrideUrlLoading(WebView view, String url) {
157 | view.loadUrl(url);
158 | return true;
159 | }
160 |
161 | @Override
162 | public void onPageFinished(WebView view, String url) {
163 | super.onPageFinished(view, url);
164 |
165 | // Make WebView visible and hide loading indicator
166 | setLoading(false);
167 | }
168 | };
169 |
170 | /** The broadcast receiver used to push game data to the server. */
171 | private BroadcastReceiver mPushReceiver = new BroadcastReceiver() {
172 | @Override
173 | public void onReceive(Context ctxt, Intent intent) {
174 | mWebView.loadUrl(String.format("javascript:LQHandlePushData(%s)",
175 | intent.getExtras().getString("json")));
176 | }
177 | };
178 | }
--------------------------------------------------------------------------------
/src/com/geoloqi/ui/SignInActivity.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.ui;
2 |
3 | import java.util.regex.Pattern;
4 |
5 | import android.app.Activity;
6 | import android.app.ProgressDialog;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.content.SharedPreferences;
10 | import android.content.SharedPreferences.Editor;
11 | import android.os.AsyncTask;
12 | import android.os.Bundle;
13 | import android.text.TextUtils;
14 | import android.util.Log;
15 | import android.view.View;
16 | import android.view.View.OnClickListener;
17 | import android.widget.EditText;
18 | import android.widget.TextView;
19 | import android.widget.Toast;
20 |
21 | import com.geoloqi.mapattack.R;
22 | import com.geoloqi.interfaces.GeoloqiConstants;
23 | import com.geoloqi.interfaces.RPCException;
24 | import com.geoloqi.rpc.MapAttackClient;
25 |
26 | public class SignInActivity extends Activity implements OnClickListener {
27 | public static final String TAG = "SignInActivity";
28 |
29 | /** Validates an email address. */
30 | public static final Pattern EMAIL_PATTERN = Pattern.compile("^[\\w\\.-]+@([\\w\\-]+\\.)+[a-z]{2,4}$",
31 | Pattern.CASE_INSENSITIVE);
32 |
33 | /** The id of the game to launch when finished. */
34 | private String mGameId;
35 |
36 | @Override
37 | public void onCreate(Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 | setContentView(R.layout.sign_in_activity);
40 |
41 | final Bundle extras = getIntent().getExtras();
42 | if (extras != null) {
43 | mGameId = extras.getString(MapAttackActivity.PARAM_GAME_ID);
44 | }
45 |
46 | // Load saved user information
47 | final SharedPreferences sharedPreferences = getSharedPreferences(
48 | GeoloqiConstants.PREFERENCES_FILE, Context.MODE_PRIVATE);
49 | if (sharedPreferences != null) {
50 | final TextView initialsView = (TextView) findViewById(R.id.initials);
51 | final TextView emailView = (TextView) findViewById(R.id.email);
52 |
53 | initialsView.setText(sharedPreferences.getString("initials", ""));
54 | emailView.setText(sharedPreferences.getString("email", ""));
55 | }
56 |
57 | // Listen for form submission
58 | findViewById(R.id.submit_button).setOnClickListener(this);
59 | }
60 |
61 | @Override
62 | public void onClick(View view) {
63 | switch (view.getId()) {
64 | case R.id.submit_button:
65 | final EditText initialsField = (EditText) findViewById(R.id.initials);
66 | final EditText emailField = (EditText) findViewById(R.id.email);
67 |
68 | final String initials = initialsField.getText().toString();
69 | final String email = emailField.getText().toString().toLowerCase();
70 |
71 | // Validate input
72 | if (initials.length() == 2) {
73 | if (EMAIL_PATTERN.matcher(email).matches()) {
74 | new CreateAnonymousAccountTask(this, initials, email).execute();
75 | } else {
76 | Toast.makeText(this, R.string.error_email,
77 | Toast.LENGTH_LONG).show();
78 | }
79 | } else {
80 | Toast.makeText(this, R.string.error_initials,
81 | Toast.LENGTH_LONG).show();
82 | }
83 | }
84 | }
85 |
86 | /** Stub */
87 | private void finishLogin(boolean result) {
88 | if (result) {
89 | if (!TextUtils.isEmpty(mGameId)) {
90 | // Launch the map attack activity
91 | Intent intent = new Intent(this, MapAttackActivity.class);
92 | intent.putExtra(MapAttackActivity.PARAM_GAME_ID, mGameId);
93 | startActivity(intent);
94 | } else {
95 | Log.e(TAG, "Got an empty game ID when trying to finish login!");
96 | Toast.makeText(this, R.string.error_invalid_game_id, Toast.LENGTH_LONG).show();
97 | }
98 | } else {
99 | Toast.makeText(this, R.string.error_join_game, Toast.LENGTH_LONG).show();
100 | }
101 |
102 | // Finish the login activity
103 | finish();
104 | }
105 |
106 | /** TODO: Move this to an external class file. */
107 | private static class CreateAnonymousAccountTask extends AsyncTask {
108 | private final ProgressDialog mProgressDialog;
109 | private final Context mContext;
110 | private final String mInitials;
111 | private final String mEmail;
112 |
113 | public CreateAnonymousAccountTask(final Context context, final String initials,
114 | final String email) {
115 | mProgressDialog = new ProgressDialog(context);
116 | mProgressDialog.setTitle(null);
117 | mProgressDialog.setMessage(context.getString(R.string.sign_in_loading_text));
118 |
119 | mContext = context;
120 | mInitials = initials;
121 | mEmail = email;
122 | }
123 |
124 | @Override
125 | public void onPreExecute() {
126 | mProgressDialog.show();
127 | }
128 |
129 | @Override
130 | protected Boolean doInBackground(String... params) {
131 | // TODO: Use the default shared preferences here:
132 | //PreferenceManager.getDefaultSharedPreferences(this)
133 | Editor prefs = (Editor) mContext.getSharedPreferences(
134 | GeoloqiConstants.PREFERENCES_FILE, Context.MODE_PRIVATE).edit();
135 | prefs.putString("initials", mInitials);
136 | prefs.putString("email", mEmail);
137 | prefs.commit();
138 |
139 | try {
140 | // Start login.
141 | final MapAttackClient client = MapAttackClient.getApplicationClient(mContext);
142 | client.createAnonymousAccount();
143 | } catch (RPCException e) {
144 | Log.e(TAG, "Got an RPCException when trying to create an anonymous account.", e);
145 | return false;
146 | }
147 |
148 | return true;
149 | }
150 |
151 | @Override
152 | public void onPostExecute(Boolean result) {
153 | mProgressDialog.dismiss();
154 |
155 | try {
156 | ((SignInActivity) mContext).finishLogin(result);
157 | } catch (ClassCastException e) {
158 | Log.w(TAG, "Got a ClassCastException when trying to finish login!", e);
159 | }
160 | }
161 | }
162 | }
--------------------------------------------------------------------------------
/src/com/geoloqi/widget/GameListArrayAdapter.java:
--------------------------------------------------------------------------------
1 | package com.geoloqi.widget;
2 |
3 | import com.geoloqi.data.Game;
4 | import com.geoloqi.mapattack.R;
5 |
6 | import android.content.Context;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.ArrayAdapter;
11 | import android.widget.TextView;
12 |
13 |
14 | /** A custom ArrayAdapter implementation for displaying Map Attack games. */
15 | public class GameListArrayAdapter extends ArrayAdapter {
16 | private final int mLayoutResourceId;
17 | private final LayoutInflater mInflater;
18 | private final Game[] mGames;
19 |
20 | /** A simple class to cache references to view resources. */
21 | private static class ViewHolder {
22 | public TextView name;
23 | public TextView description;
24 | }
25 |
26 | /**
27 | * Gets a layout inflater and stores it for use by the getView method.
28 | *
29 | * @param context
30 | * @param textViewResourceId
31 | * @param games
32 | */
33 | public GameListArrayAdapter(Context context, int textViewResourceId, Game[] games) {
34 | super(context, textViewResourceId, games);
35 |
36 | // Store our arguments as object members
37 | mLayoutResourceId = textViewResourceId;
38 | mGames = games;
39 |
40 | // Get a layout inflater
41 | mInflater = LayoutInflater.from(context);
42 | }
43 |
44 | @Override
45 | public View getView(int position, View convertView, ViewGroup parent) {
46 | final ViewHolder holder;
47 |
48 | if (convertView == null) {
49 | // Inflate our row layout
50 | convertView = mInflater.inflate(mLayoutResourceId, parent, false);
51 |
52 | // Cache the row elements for efficient retrieval
53 | holder = new ViewHolder();
54 | holder.name = (TextView) convertView.findViewById(R.id.name);
55 | holder.description = (TextView) convertView.findViewById(R.id.description);
56 |
57 | // Store the holder object on the row
58 | convertView.setTag(holder);
59 | } else {
60 | holder = (ViewHolder) convertView.getTag();
61 | }
62 |
63 | // Populate our game data
64 | final Game game = mGames[position];
65 | holder.name.setText(game.name);
66 | holder.description.setText(game.description);
67 |
68 | return convertView;
69 | }
70 | }
--------------------------------------------------------------------------------