Initial Commit - Copy from Altus Metrum AltOS
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ao-tools/libaltos/altos.dll export-ignore
|
||||||
|
ao-tools/libaltos/libaltos.dylib export-ignore
|
68
.gitignore
vendored
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
*~
|
||||||
|
*.a
|
||||||
|
*.adb
|
||||||
|
*.asm
|
||||||
|
*.cdb
|
||||||
|
*.ihx
|
||||||
|
*.lnk
|
||||||
|
*.lst
|
||||||
|
*.map
|
||||||
|
*.mdwn
|
||||||
|
*.mem
|
||||||
|
*.o
|
||||||
|
*.rel
|
||||||
|
*.rst
|
||||||
|
*.sym
|
||||||
|
.deps
|
||||||
|
TAGS
|
||||||
|
aclocal.m4
|
||||||
|
libtool
|
||||||
|
ltmain.sh
|
||||||
|
src/Version
|
||||||
|
src/ao_flight_test
|
||||||
|
src/ao_gps_test
|
||||||
|
src/ao_gps_test_skytraq
|
||||||
|
src/ao_kalman.h
|
||||||
|
ao-teledongle.h
|
||||||
|
ao-telemetrum.h
|
||||||
|
ao-teleterra.h
|
||||||
|
ao-tidongle.h
|
||||||
|
ao-tools/ao-bitbang/ao-bitbang
|
||||||
|
ao-tools/ao-dbg/ao-dbg
|
||||||
|
ao-tools/ao-dumplog/ao-dumplog
|
||||||
|
ao-tools/ao-dump-up/ao-dump-up
|
||||||
|
ao-tools/ao-eeprom/ao-eeprom
|
||||||
|
ao-tools/ao-edit-telem/ao-edit-telem
|
||||||
|
ao-tools/ao-elftohex/ao-elftohex
|
||||||
|
ao-tools/ao-list/ao-list
|
||||||
|
ao-tools/ao-load/ao-load
|
||||||
|
ao-tools/ao-postflight/ao-postflight
|
||||||
|
ao-tools/ao-rawload/ao-rawload
|
||||||
|
ao-tools/ao-send-telem/ao-send-telem
|
||||||
|
ao-tools/ao-sky-flash/ao-sky-flash
|
||||||
|
ao-tools/ao-usbload/ao-usbload
|
||||||
|
ao-tools/ao-view/ao-view
|
||||||
|
ao-view/Makefile
|
||||||
|
ao-view/ao-view
|
||||||
|
autom4te.cache
|
||||||
|
compile
|
||||||
|
config.*
|
||||||
|
config.h
|
||||||
|
config.h.in
|
||||||
|
config.log
|
||||||
|
config.status
|
||||||
|
build-stamp
|
||||||
|
configure-stamp
|
||||||
|
configure
|
||||||
|
depcomp
|
||||||
|
install-sh
|
||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
|
missing
|
||||||
|
stamp-h1
|
||||||
|
tags
|
||||||
|
doc/telemetrum.fo
|
||||||
|
doc/telemetrum.html
|
||||||
|
doc/telemetrum.pdf
|
||||||
|
altosui/altos-windows.log
|
||||||
|
pdclib-root
|
2
AUTHORS
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Keith Packard <keithp@keithp.com>
|
||||||
|
Bdale Garbee <bdale@gag.com>
|
339
COPYING
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Lesser General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
source code as you receive it, in any medium, provided that you
|
||||||
|
conspicuously and appropriately publish on each copy an appropriate
|
||||||
|
copyright notice and disclaimer of warranty; keep intact all the
|
||||||
|
notices that refer to this License and to the absence of any warranty;
|
||||||
|
and give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Program, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, complete source
|
||||||
|
code means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the executable. However, as a
|
||||||
|
special exception, the source code distributed need not include
|
||||||
|
anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component
|
||||||
|
itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering
|
||||||
|
access to copy from a designated place, then offering equivalent
|
||||||
|
access to copy the source code from the same place counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
void, and will automatically terminate your rights under this License.
|
||||||
|
However, parties who have received copies, or rights, from you under
|
||||||
|
this License will not have their licenses terminated so long as such
|
||||||
|
parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program subject to
|
||||||
|
these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system, which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Program
|
||||||
|
specifies a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals
|
||||||
|
of preserving the free status of all derivatives of our free software and
|
||||||
|
of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1989
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License.
|
365
INSTALL
Normal file
@ -0,0 +1,365 @@
|
|||||||
|
Installation Instructions
|
||||||
|
*************************
|
||||||
|
|
||||||
|
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
|
||||||
|
2006, 2007, 2008, 2009 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Copying and distribution of this file, with or without modification,
|
||||||
|
are permitted in any medium without royalty provided the copyright
|
||||||
|
notice and this notice are preserved. This file is offered as-is,
|
||||||
|
without warranty of any kind.
|
||||||
|
|
||||||
|
Basic Installation
|
||||||
|
==================
|
||||||
|
|
||||||
|
Briefly, the shell commands `./configure; make; make install' should
|
||||||
|
configure, build, and install this package. The following
|
||||||
|
more-detailed instructions are generic; see the `README' file for
|
||||||
|
instructions specific to this package. Some packages provide this
|
||||||
|
`INSTALL' file but do not implement all of the features documented
|
||||||
|
below. The lack of an optional feature in a given package is not
|
||||||
|
necessarily a bug. More recommendations for GNU packages can be found
|
||||||
|
in *note Makefile Conventions: (standards)Makefile Conventions.
|
||||||
|
|
||||||
|
The `configure' shell script attempts to guess correct values for
|
||||||
|
various system-dependent variables used during compilation. It uses
|
||||||
|
those values to create a `Makefile' in each directory of the package.
|
||||||
|
It may also create one or more `.h' files containing system-dependent
|
||||||
|
definitions. Finally, it creates a shell script `config.status' that
|
||||||
|
you can run in the future to recreate the current configuration, and a
|
||||||
|
file `config.log' containing compiler output (useful mainly for
|
||||||
|
debugging `configure').
|
||||||
|
|
||||||
|
It can also use an optional file (typically called `config.cache'
|
||||||
|
and enabled with `--cache-file=config.cache' or simply `-C') that saves
|
||||||
|
the results of its tests to speed up reconfiguring. Caching is
|
||||||
|
disabled by default to prevent problems with accidental use of stale
|
||||||
|
cache files.
|
||||||
|
|
||||||
|
If you need to do unusual things to compile the package, please try
|
||||||
|
to figure out how `configure' could check whether to do them, and mail
|
||||||
|
diffs or instructions to the address given in the `README' so they can
|
||||||
|
be considered for the next release. If you are using the cache, and at
|
||||||
|
some point `config.cache' contains results you don't want to keep, you
|
||||||
|
may remove or edit it.
|
||||||
|
|
||||||
|
The file `configure.ac' (or `configure.in') is used to create
|
||||||
|
`configure' by a program called `autoconf'. You need `configure.ac' if
|
||||||
|
you want to change it or regenerate `configure' using a newer version
|
||||||
|
of `autoconf'.
|
||||||
|
|
||||||
|
The simplest way to compile this package is:
|
||||||
|
|
||||||
|
1. `cd' to the directory containing the package's source code and type
|
||||||
|
`./configure' to configure the package for your system.
|
||||||
|
|
||||||
|
Running `configure' might take a while. While running, it prints
|
||||||
|
some messages telling which features it is checking for.
|
||||||
|
|
||||||
|
2. Type `make' to compile the package.
|
||||||
|
|
||||||
|
3. Optionally, type `make check' to run any self-tests that come with
|
||||||
|
the package, generally using the just-built uninstalled binaries.
|
||||||
|
|
||||||
|
4. Type `make install' to install the programs and any data files and
|
||||||
|
documentation. When installing into a prefix owned by root, it is
|
||||||
|
recommended that the package be configured and built as a regular
|
||||||
|
user, and only the `make install' phase executed with root
|
||||||
|
privileges.
|
||||||
|
|
||||||
|
5. Optionally, type `make installcheck' to repeat any self-tests, but
|
||||||
|
this time using the binaries in their final installed location.
|
||||||
|
This target does not install anything. Running this target as a
|
||||||
|
regular user, particularly if the prior `make install' required
|
||||||
|
root privileges, verifies that the installation completed
|
||||||
|
correctly.
|
||||||
|
|
||||||
|
6. You can remove the program binaries and object files from the
|
||||||
|
source code directory by typing `make clean'. To also remove the
|
||||||
|
files that `configure' created (so you can compile the package for
|
||||||
|
a different kind of computer), type `make distclean'. There is
|
||||||
|
also a `make maintainer-clean' target, but that is intended mainly
|
||||||
|
for the package's developers. If you use it, you may have to get
|
||||||
|
all sorts of other programs in order to regenerate files that came
|
||||||
|
with the distribution.
|
||||||
|
|
||||||
|
7. Often, you can also type `make uninstall' to remove the installed
|
||||||
|
files again. In practice, not all packages have tested that
|
||||||
|
uninstallation works correctly, even though it is required by the
|
||||||
|
GNU Coding Standards.
|
||||||
|
|
||||||
|
8. Some packages, particularly those that use Automake, provide `make
|
||||||
|
distcheck', which can by used by developers to test that all other
|
||||||
|
targets like `make install' and `make uninstall' work correctly.
|
||||||
|
This target is generally not run by end users.
|
||||||
|
|
||||||
|
Compilers and Options
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Some systems require unusual options for compilation or linking that
|
||||||
|
the `configure' script does not know about. Run `./configure --help'
|
||||||
|
for details on some of the pertinent environment variables.
|
||||||
|
|
||||||
|
You can give `configure' initial values for configuration parameters
|
||||||
|
by setting variables in the command line or in the environment. Here
|
||||||
|
is an example:
|
||||||
|
|
||||||
|
./configure CC=c99 CFLAGS=-g LIBS=-lposix
|
||||||
|
|
||||||
|
*Note Defining Variables::, for more details.
|
||||||
|
|
||||||
|
Compiling For Multiple Architectures
|
||||||
|
====================================
|
||||||
|
|
||||||
|
You can compile the package for more than one kind of computer at the
|
||||||
|
same time, by placing the object files for each architecture in their
|
||||||
|
own directory. To do this, you can use GNU `make'. `cd' to the
|
||||||
|
directory where you want the object files and executables to go and run
|
||||||
|
the `configure' script. `configure' automatically checks for the
|
||||||
|
source code in the directory that `configure' is in and in `..'. This
|
||||||
|
is known as a "VPATH" build.
|
||||||
|
|
||||||
|
With a non-GNU `make', it is safer to compile the package for one
|
||||||
|
architecture at a time in the source code directory. After you have
|
||||||
|
installed the package for one architecture, use `make distclean' before
|
||||||
|
reconfiguring for another architecture.
|
||||||
|
|
||||||
|
On MacOS X 10.5 and later systems, you can create libraries and
|
||||||
|
executables that work on multiple system types--known as "fat" or
|
||||||
|
"universal" binaries--by specifying multiple `-arch' options to the
|
||||||
|
compiler but only a single `-arch' option to the preprocessor. Like
|
||||||
|
this:
|
||||||
|
|
||||||
|
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
||||||
|
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
||||||
|
CPP="gcc -E" CXXCPP="g++ -E"
|
||||||
|
|
||||||
|
This is not guaranteed to produce working output in all cases, you
|
||||||
|
may have to build one architecture at a time and combine the results
|
||||||
|
using the `lipo' tool if you have problems.
|
||||||
|
|
||||||
|
Installation Names
|
||||||
|
==================
|
||||||
|
|
||||||
|
By default, `make install' installs the package's commands under
|
||||||
|
`/usr/local/bin', include files under `/usr/local/include', etc. You
|
||||||
|
can specify an installation prefix other than `/usr/local' by giving
|
||||||
|
`configure' the option `--prefix=PREFIX', where PREFIX must be an
|
||||||
|
absolute file name.
|
||||||
|
|
||||||
|
You can specify separate installation prefixes for
|
||||||
|
architecture-specific files and architecture-independent files. If you
|
||||||
|
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
|
||||||
|
PREFIX as the prefix for installing programs and libraries.
|
||||||
|
Documentation and other data files still use the regular prefix.
|
||||||
|
|
||||||
|
In addition, if you use an unusual directory layout you can give
|
||||||
|
options like `--bindir=DIR' to specify different values for particular
|
||||||
|
kinds of files. Run `configure --help' for a list of the directories
|
||||||
|
you can set and what kinds of files go in them. In general, the
|
||||||
|
default for these options is expressed in terms of `${prefix}', so that
|
||||||
|
specifying just `--prefix' will affect all of the other directory
|
||||||
|
specifications that were not explicitly provided.
|
||||||
|
|
||||||
|
The most portable way to affect installation locations is to pass the
|
||||||
|
correct locations to `configure'; however, many packages provide one or
|
||||||
|
both of the following shortcuts of passing variable assignments to the
|
||||||
|
`make install' command line to change installation locations without
|
||||||
|
having to reconfigure or recompile.
|
||||||
|
|
||||||
|
The first method involves providing an override variable for each
|
||||||
|
affected directory. For example, `make install
|
||||||
|
prefix=/alternate/directory' will choose an alternate location for all
|
||||||
|
directory configuration variables that were expressed in terms of
|
||||||
|
`${prefix}'. Any directories that were specified during `configure',
|
||||||
|
but not in terms of `${prefix}', must each be overridden at install
|
||||||
|
time for the entire installation to be relocated. The approach of
|
||||||
|
makefile variable overrides for each directory variable is required by
|
||||||
|
the GNU Coding Standards, and ideally causes no recompilation.
|
||||||
|
However, some platforms have known limitations with the semantics of
|
||||||
|
shared libraries that end up requiring recompilation when using this
|
||||||
|
method, particularly noticeable in packages that use GNU Libtool.
|
||||||
|
|
||||||
|
The second method involves providing the `DESTDIR' variable. For
|
||||||
|
example, `make install DESTDIR=/alternate/directory' will prepend
|
||||||
|
`/alternate/directory' before all installation names. The approach of
|
||||||
|
`DESTDIR' overrides is not required by the GNU Coding Standards, and
|
||||||
|
does not work on platforms that have drive letters. On the other hand,
|
||||||
|
it does better at avoiding recompilation issues, and works well even
|
||||||
|
when some directory options were not specified in terms of `${prefix}'
|
||||||
|
at `configure' time.
|
||||||
|
|
||||||
|
Optional Features
|
||||||
|
=================
|
||||||
|
|
||||||
|
If the package supports it, you can cause programs to be installed
|
||||||
|
with an extra prefix or suffix on their names by giving `configure' the
|
||||||
|
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
||||||
|
|
||||||
|
Some packages pay attention to `--enable-FEATURE' options to
|
||||||
|
`configure', where FEATURE indicates an optional part of the package.
|
||||||
|
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
||||||
|
is something like `gnu-as' or `x' (for the X Window System). The
|
||||||
|
`README' should mention any `--enable-' and `--with-' options that the
|
||||||
|
package recognizes.
|
||||||
|
|
||||||
|
For packages that use the X Window System, `configure' can usually
|
||||||
|
find the X include and library files automatically, but if it doesn't,
|
||||||
|
you can use the `configure' options `--x-includes=DIR' and
|
||||||
|
`--x-libraries=DIR' to specify their locations.
|
||||||
|
|
||||||
|
Some packages offer the ability to configure how verbose the
|
||||||
|
execution of `make' will be. For these packages, running `./configure
|
||||||
|
--enable-silent-rules' sets the default to minimal output, which can be
|
||||||
|
overridden with `make V=1'; while running `./configure
|
||||||
|
--disable-silent-rules' sets the default to verbose, which can be
|
||||||
|
overridden with `make V=0'.
|
||||||
|
|
||||||
|
Particular systems
|
||||||
|
==================
|
||||||
|
|
||||||
|
On HP-UX, the default C compiler is not ANSI C compatible. If GNU
|
||||||
|
CC is not installed, it is recommended to use the following options in
|
||||||
|
order to use an ANSI C compiler:
|
||||||
|
|
||||||
|
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
|
||||||
|
|
||||||
|
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
|
||||||
|
|
||||||
|
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
|
||||||
|
parse its `<wchar.h>' header file. The option `-nodtk' can be used as
|
||||||
|
a workaround. If GNU CC is not installed, it is therefore recommended
|
||||||
|
to try
|
||||||
|
|
||||||
|
./configure CC="cc"
|
||||||
|
|
||||||
|
and if that doesn't work, try
|
||||||
|
|
||||||
|
./configure CC="cc -nodtk"
|
||||||
|
|
||||||
|
On Solaris, don't put `/usr/ucb' early in your `PATH'. This
|
||||||
|
directory contains several dysfunctional programs; working variants of
|
||||||
|
these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
|
||||||
|
in your `PATH', put it _after_ `/usr/bin'.
|
||||||
|
|
||||||
|
On Haiku, software installed for all users goes in `/boot/common',
|
||||||
|
not `/usr/local'. It is recommended to use the following options:
|
||||||
|
|
||||||
|
./configure --prefix=/boot/common
|
||||||
|
|
||||||
|
Specifying the System Type
|
||||||
|
==========================
|
||||||
|
|
||||||
|
There may be some features `configure' cannot figure out
|
||||||
|
automatically, but needs to determine by the type of machine the package
|
||||||
|
will run on. Usually, assuming the package is built to be run on the
|
||||||
|
_same_ architectures, `configure' can figure that out, but if it prints
|
||||||
|
a message saying it cannot guess the machine type, give it the
|
||||||
|
`--build=TYPE' option. TYPE can either be a short name for the system
|
||||||
|
type, such as `sun4', or a canonical name which has the form:
|
||||||
|
|
||||||
|
CPU-COMPANY-SYSTEM
|
||||||
|
|
||||||
|
where SYSTEM can have one of these forms:
|
||||||
|
|
||||||
|
OS
|
||||||
|
KERNEL-OS
|
||||||
|
|
||||||
|
See the file `config.sub' for the possible values of each field. If
|
||||||
|
`config.sub' isn't included in this package, then this package doesn't
|
||||||
|
need to know the machine type.
|
||||||
|
|
||||||
|
If you are _building_ compiler tools for cross-compiling, you should
|
||||||
|
use the option `--target=TYPE' to select the type of system they will
|
||||||
|
produce code for.
|
||||||
|
|
||||||
|
If you want to _use_ a cross compiler, that generates code for a
|
||||||
|
platform different from the build platform, you should specify the
|
||||||
|
"host" platform (i.e., that on which the generated programs will
|
||||||
|
eventually be run) with `--host=TYPE'.
|
||||||
|
|
||||||
|
Sharing Defaults
|
||||||
|
================
|
||||||
|
|
||||||
|
If you want to set default values for `configure' scripts to share,
|
||||||
|
you can create a site shell script called `config.site' that gives
|
||||||
|
default values for variables like `CC', `cache_file', and `prefix'.
|
||||||
|
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
||||||
|
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
||||||
|
`CONFIG_SITE' environment variable to the location of the site script.
|
||||||
|
A warning: not all `configure' scripts look for a site script.
|
||||||
|
|
||||||
|
Defining Variables
|
||||||
|
==================
|
||||||
|
|
||||||
|
Variables not defined in a site shell script can be set in the
|
||||||
|
environment passed to `configure'. However, some packages may run
|
||||||
|
configure again during the build, and the customized values of these
|
||||||
|
variables may be lost. In order to avoid this problem, you should set
|
||||||
|
them in the `configure' command line, using `VAR=value'. For example:
|
||||||
|
|
||||||
|
./configure CC=/usr/local2/bin/gcc
|
||||||
|
|
||||||
|
causes the specified `gcc' to be used as the C compiler (unless it is
|
||||||
|
overridden in the site shell script).
|
||||||
|
|
||||||
|
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
|
||||||
|
an Autoconf bug. Until the bug is fixed you can use this workaround:
|
||||||
|
|
||||||
|
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
|
||||||
|
|
||||||
|
`configure' Invocation
|
||||||
|
======================
|
||||||
|
|
||||||
|
`configure' recognizes the following options to control how it
|
||||||
|
operates.
|
||||||
|
|
||||||
|
`--help'
|
||||||
|
`-h'
|
||||||
|
Print a summary of all of the options to `configure', and exit.
|
||||||
|
|
||||||
|
`--help=short'
|
||||||
|
`--help=recursive'
|
||||||
|
Print a summary of the options unique to this package's
|
||||||
|
`configure', and exit. The `short' variant lists options used
|
||||||
|
only in the top level, while the `recursive' variant lists options
|
||||||
|
also present in any nested packages.
|
||||||
|
|
||||||
|
`--version'
|
||||||
|
`-V'
|
||||||
|
Print the version of Autoconf used to generate the `configure'
|
||||||
|
script, and exit.
|
||||||
|
|
||||||
|
`--cache-file=FILE'
|
||||||
|
Enable the cache: use and save the results of the tests in FILE,
|
||||||
|
traditionally `config.cache'. FILE defaults to `/dev/null' to
|
||||||
|
disable caching.
|
||||||
|
|
||||||
|
`--config-cache'
|
||||||
|
`-C'
|
||||||
|
Alias for `--cache-file=config.cache'.
|
||||||
|
|
||||||
|
`--quiet'
|
||||||
|
`--silent'
|
||||||
|
`-q'
|
||||||
|
Do not print messages saying which checks are being made. To
|
||||||
|
suppress all normal output, redirect it to `/dev/null' (any error
|
||||||
|
messages will still be shown).
|
||||||
|
|
||||||
|
`--srcdir=DIR'
|
||||||
|
Look for the package's source code in directory DIR. Usually
|
||||||
|
`configure' can determine that directory automatically.
|
||||||
|
|
||||||
|
`--prefix=DIR'
|
||||||
|
Use DIR as the installation prefix. *note Installation Names::
|
||||||
|
for more details, including other options available for fine-tuning
|
||||||
|
the installation locations.
|
||||||
|
|
||||||
|
`--no-create'
|
||||||
|
`-n'
|
||||||
|
Run the configure checks, but stop before creating any output
|
||||||
|
files.
|
||||||
|
|
||||||
|
`configure' also accepts some other, not widely useful, options. Run
|
||||||
|
`configure --help' for more details.
|
||||||
|
|
81
Makefile.am
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
SUBDIRS=ao-tools src doc icon altoslib libaltos altosuilib altosui micropeak ao-utils altosdroid telegps map-server
|
||||||
|
|
||||||
|
EXTRA_DIST = ChangeLog
|
||||||
|
|
||||||
|
MAINTAINERCLEANFILES = ChangeLog
|
||||||
|
|
||||||
|
.PHONY: ChangeLog
|
||||||
|
|
||||||
|
ChangeLog:
|
||||||
|
(GIT_DIR=$(top_srcdir)/.git git log > .changelog.tmp && mv .changelog.tmp ChangeLog; rm -f .changelog.tmp) || \
|
||||||
|
(touch ChangeLog; echo 'git directory not found: installing possibly empty changelog.' >&2)
|
||||||
|
|
||||||
|
dist-hook: ChangeLog
|
||||||
|
|
||||||
|
if FATINSTALL
|
||||||
|
fat-install: fat
|
||||||
|
cd altosui && $(MAKE) fat-install
|
||||||
|
cd telegps && $(MAKE) fat-install
|
||||||
|
cd micropeak && $(MAKE) fat-install
|
||||||
|
endif
|
||||||
|
|
||||||
|
fat: all-recursive
|
||||||
|
cd libaltos && $(MAKE) fat
|
||||||
|
cd altoslib && $(MAKE) all
|
||||||
|
cd icon && $(MAKE) fat
|
||||||
|
cd altosui && $(MAKE) fat
|
||||||
|
cd micropeak && $(MAKE) fat
|
||||||
|
cd telegps && $(MAKE) fat
|
||||||
|
|
||||||
|
fat_linux = \
|
||||||
|
altosui/Altos-Linux-$(VERSION).sh altosui/Altos-Linux-$(VERSION).tar.bz2 \
|
||||||
|
telegps/TeleGPS-Linux-$(VERSION).sh telegps/TeleGPS-Linux-$(VERSION).tar.bz2 \
|
||||||
|
micropeak/MicroPeak-Linux-$(VERSION).sh micropeak/MicroPeak-Linux-$(VERSION).tar.bz2
|
||||||
|
|
||||||
|
fat_mac = \
|
||||||
|
altosui/Altos-Mac-$(VERSION).dmg \
|
||||||
|
telegps/TeleGPS-Mac-$(VERSION).dmg \
|
||||||
|
micropeak/MicroPeak-Mac-$(VERSION).dmg
|
||||||
|
|
||||||
|
fat_windows = \
|
||||||
|
altosui/Altos-Windows-$(VERSION_DASH).exe \
|
||||||
|
telegps/TeleGPS-Windows-$(VERSION_DASH).exe \
|
||||||
|
micropeak/MicroPeak-Windows-$(VERSION_DASH).exe
|
||||||
|
|
||||||
|
fat_android = \
|
||||||
|
altosdroid/bin/AltosDroid-debug.apk \
|
||||||
|
altosdroid/bin/AltosDroid-release.apk
|
||||||
|
|
||||||
|
fat_altos = \
|
||||||
|
src/easymega-v1.0/easymega-v1.0-$(VERSION).ihx \
|
||||||
|
src/easymega-v2.0/easymega-v2.0-$(VERSION).ihx \
|
||||||
|
src/easymini-v1.0/easymini-v1.0-$(VERSION).ihx \
|
||||||
|
src/easymini-v2.0/easymini-v2.0-$(VERSION).ihx \
|
||||||
|
src/easymini-v3.0/easymini-v3.0-$(VERSION).ihx \
|
||||||
|
src/easymotor-v3/easymotor-v3-$(VERSION).ihx \
|
||||||
|
src/easytimer-v1/easytimer-v1-$(VERSION).ihx \
|
||||||
|
src/easytimer-v2/easytimer-v2-$(VERSION).ihx \
|
||||||
|
src/telebt-v3.0/telebt-v3.0-$(VERSION).ihx \
|
||||||
|
src/telebt-v4.0/telebt-v4.0-$(VERSION).ihx \
|
||||||
|
src/teledongle-v3.0/teledongle-v3.0-$(VERSION).ihx \
|
||||||
|
src/telegps-v1.0/telegps-v1.0-$(VERSION).ihx \
|
||||||
|
src/telegps-v2.0/telegps-v2.0-$(VERSION).ihx \
|
||||||
|
src/telemega-v1.0/telemega-v1.0-$(VERSION).ihx \
|
||||||
|
src/telemega-v2.0/telemega-v2.0-$(VERSION).ihx \
|
||||||
|
src/telemega-v3.0/telemega-v3.0-$(VERSION).ihx \
|
||||||
|
src/telemega-v4.0/telemega-v4.0-$(VERSION).ihx \
|
||||||
|
src/telemega-v5.0/telemega-v5.0-$(VERSION).ihx \
|
||||||
|
src/telemega-v6.0/telemega-v6.0-$(VERSION).ihx \
|
||||||
|
src/telemetrum-v2.0/telemetrum-v2.0-$(VERSION).ihx \
|
||||||
|
src/telemetrum-v3.0/telemetrum-v3.0-$(VERSION).ihx \
|
||||||
|
src/telemetrum-v4.0/telemetrum-v4.0-$(VERSION).ihx \
|
||||||
|
src/telelco-v2.0/telelco-v2.0-$(VERSION).ihx \
|
||||||
|
src/telefireeight-v1.0/telefireeight-v1.0-$(VERSION).ihx \
|
||||||
|
src/telefireeight-v2.0/telefireeight-v2.0-$(VERSION).ihx
|
||||||
|
|
||||||
|
keithp-fat: fat
|
||||||
|
ssh keithp.com mkdir -p public_html/altos-$(VERSION)
|
||||||
|
scp -p $(fat_linux) $(fat_mac) $(fat_windows) $(fat_android) $(fat_altos) keithp.com:public_html/altos-$(VERSION)
|
||||||
|
|
||||||
|
set-java-versions:
|
||||||
|
$(top_srcdir)/fix-java-versions org.altusmetrum.altoslib=$(ALTOSLIB_VERSION) org.altusmetrum.altosuilib=$(ALTOSUILIB_VERSION)
|
115
README
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
AltOS - 8051 operating system for Altus-Metrum projects
|
||||||
|
|
||||||
|
Copyright and License
|
||||||
|
|
||||||
|
Copyright © 2009 Keith Packard <keithp@keithp.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
|
Tasking API:
|
||||||
|
|
||||||
|
* Multi-tasking
|
||||||
|
* Non-preemptive
|
||||||
|
* Unix-style sleep/wakeup scheduling
|
||||||
|
* Strict round-robin, no priorities
|
||||||
|
|
||||||
|
uint8_t ao_sleep(void *wchan)
|
||||||
|
|
||||||
|
Puts current task to sleep. Will wake up when wchan is
|
||||||
|
signalled (returning 0), or when an alarm has expired
|
||||||
|
(returning 1).
|
||||||
|
|
||||||
|
void ao_wakeup(void *wchan)
|
||||||
|
|
||||||
|
Wakeup all tasks sleeping on wchan
|
||||||
|
|
||||||
|
void ao_add_task(struct ao_task *task, void (*start)(void))
|
||||||
|
|
||||||
|
Adds a task to the queue of available tasks
|
||||||
|
|
||||||
|
void ao_start_scheduler(void)
|
||||||
|
|
||||||
|
Invokes the scheduler, starting the operating system
|
||||||
|
|
||||||
|
void ao_yield(void)
|
||||||
|
|
||||||
|
Switches to another task which is ready to run. Allows
|
||||||
|
tasks which want to run for a while to give up the CPU
|
||||||
|
without needing to sleep
|
||||||
|
|
||||||
|
void ao_panic(uint8_t reason)
|
||||||
|
|
||||||
|
Any fatal error should call this function, which can
|
||||||
|
display the error code in some cryptic fashion.
|
||||||
|
|
||||||
|
void ao_wake_task(struct ao_task *task)
|
||||||
|
|
||||||
|
Wake the task as if the wchan it is waiting on had
|
||||||
|
been signalled.
|
||||||
|
|
||||||
|
void ao_alarm(uint16_t delay)
|
||||||
|
|
||||||
|
Queue an alarm to expire 'delay' ticks in the future.
|
||||||
|
The alarm will cause the task to wake from ao_sleep
|
||||||
|
and return 1. Alarms are cancelled if the task is
|
||||||
|
awoken by ao_wakeup or ao_wake_task.
|
||||||
|
|
||||||
|
void ao_exit(void)
|
||||||
|
|
||||||
|
Stop the current task and remove it from the list of
|
||||||
|
available tasks.
|
||||||
|
|
||||||
|
Timer API
|
||||||
|
|
||||||
|
* Regular timer ticks (at 100Hz, by default)
|
||||||
|
* All time values are in ticks
|
||||||
|
|
||||||
|
uint16_t ao_time(void)
|
||||||
|
|
||||||
|
Returns the curent tick count
|
||||||
|
|
||||||
|
void ao_delay(uint16_t delay)
|
||||||
|
|
||||||
|
Suspend task execution for 'delay' ticks.
|
||||||
|
|
||||||
|
DMA API
|
||||||
|
|
||||||
|
* Static DMA entry allocation
|
||||||
|
|
||||||
|
uint8_t ao_dma_alloc(uint8_t *done)
|
||||||
|
|
||||||
|
Allocates one of the 5 DMA units on the cc1111
|
||||||
|
processor. When this DMA unit is finished, it will
|
||||||
|
set *done to 1 and wakeup anyone waiting on that.
|
||||||
|
|
||||||
|
void ao_dma_set_transfer(uint8_t id, void *src, void *dst,
|
||||||
|
uint16_t count, uint8_t cfg0, uint8_t cfg1)
|
||||||
|
|
||||||
|
Prepares the specified DMA unit for operation.
|
||||||
|
|
||||||
|
void ao_dma_start(uint8_t id)
|
||||||
|
|
||||||
|
Start DMA on the specified channel.
|
||||||
|
|
||||||
|
void ao_dma_trigger(uint8_t id)
|
||||||
|
|
||||||
|
Manually trigger a DMA channel
|
||||||
|
|
||||||
|
void ao_dma_abort(uint8_t id)
|
||||||
|
|
||||||
|
Abort a pending DMA transfer, signalling
|
||||||
|
that by setting the associated 'done' to
|
||||||
|
AO_DMA_ABORTED and waking any task
|
||||||
|
sleeping on that.
|
182
Releasing
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
Adding a product to the release
|
||||||
|
|
||||||
|
Make sure the firmware and loader get built by default in
|
||||||
|
src/Makefile
|
||||||
|
|
||||||
|
Add firmware targets to fat_altos target in Makefile.am
|
||||||
|
|
||||||
|
Add the firmware to altosui/Makefile.am
|
||||||
|
|
||||||
|
Add the firmware to altosui/altos-windows.nsi.in
|
||||||
|
|
||||||
|
Add the firmware to telegps/Makefile.am
|
||||||
|
|
||||||
|
Add the firmware to telegps/telegps-windows.nsi.in
|
||||||
|
|
||||||
|
Add the firmware to Releasing
|
||||||
|
|
||||||
|
These are Keith's notes on how to do a release
|
||||||
|
|
||||||
|
- update the version and date in configure.ac if Bdale hasn't already
|
||||||
|
|
||||||
|
- update the ANDROID_VERSION in configure.ac if releasing AltosDroid
|
||||||
|
|
||||||
|
- follow instructions in doc/RELNOTES
|
||||||
|
|
||||||
|
- make sure there is a doc/release-notes-<version>.inc
|
||||||
|
|
||||||
|
- make sure doc/release-notes.inc points at that
|
||||||
|
|
||||||
|
- make sure doc/Makefile points at that too
|
||||||
|
|
||||||
|
- confirm doc/header.inc has correct copyright year
|
||||||
|
|
||||||
|
|
||||||
|
These are Bdale's notes on how to do a release.
|
||||||
|
|
||||||
|
- make sure Debian build environment is up to date
|
||||||
|
sudo cowbuilder --update
|
||||||
|
|
||||||
|
- make sure fat build environment is up to date
|
||||||
|
sudo apt update && sudo apt upgrade && sudo apt autoremove
|
||||||
|
|
||||||
|
- ensure i386 build support is available, and we have tools to build
|
||||||
|
installers for Windows and Mac OS X
|
||||||
|
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install genisoimage nsis \
|
||||||
|
gcc-i686-linux-gnu gcc-aarch64-linux-gnu \
|
||||||
|
gcc-arm-linux-gnueabi gcc-arm-linux-gnueabihf \
|
||||||
|
gcc-mingw-w64-i686-posix gcc-mingw-w64-x86-64-win32
|
||||||
|
|
||||||
|
- make sure jsign is installed so we can sign Windows installers:
|
||||||
|
|
||||||
|
https://github.com/ebourg/jsign/releases/download/5.0/jsign_5.0_all.deb
|
||||||
|
|
||||||
|
- make sure ~/web/altusmetrum has no pending pullable commits
|
||||||
|
|
||||||
|
git checkout master
|
||||||
|
|
||||||
|
- update the version in configure.ac if Keith hasn't already
|
||||||
|
|
||||||
|
- if this is an x.y release, then:
|
||||||
|
git checkout -b branch-<version>
|
||||||
|
|
||||||
|
- if this is an x.y.z release, then:
|
||||||
|
git checkout branch-<version> # the x.y parts only
|
||||||
|
- cherry-pick or merge appropriate content from master
|
||||||
|
|
||||||
|
- make sure there is a doc/release-notes-<version>.inc
|
||||||
|
- make sure that doc/*.txt have the right copyright year and the
|
||||||
|
new release is included
|
||||||
|
- confirm doc/header.inc has correct copyright year
|
||||||
|
- make absolutely sure checked-out tree is "clean"
|
||||||
|
- make absolutely sure any commits Keith might have pushed to branches
|
||||||
|
like debian are already pulled
|
||||||
|
|
||||||
|
git log > ChangeLog
|
||||||
|
git commit -a -m'update ChangeLog for <version> release'
|
||||||
|
git tag -a <version> -m'Releasing <version>' # full x.y.z version
|
||||||
|
|
||||||
|
- make sure .git/gbp.conf set to use branch-<version> as upstream
|
||||||
|
|
||||||
|
git checkout debian
|
||||||
|
git merge branch-<version>
|
||||||
|
|
||||||
|
- verify debian/changelog is "clean" ending in last release version
|
||||||
|
- craft a suitable debian/changelog entry, possibly using:
|
||||||
|
export EMAIL=bdale@gag.com
|
||||||
|
gbp dch --release --multimaint-merge --new-version=<version>-1
|
||||||
|
|
||||||
|
git commit -n debian/changelog -m "update changelog for Debian build"
|
||||||
|
|
||||||
|
- if this is a -1 release, then
|
||||||
|
gbp buildpackage --git-no-pristine-tar \
|
||||||
|
--git-upstream-branch=branch-<version> \ # eg 1.3
|
||||||
|
--git-upstream-tag=<version> # eg 1.3.1
|
||||||
|
pristine-tar commit \
|
||||||
|
../build-area/altos_<version>.orig.tar.gz \
|
||||||
|
branch-<version>
|
||||||
|
else if this is not a -1 release
|
||||||
|
gbp buildpackage
|
||||||
|
|
||||||
|
git tag debian/<version>
|
||||||
|
|
||||||
|
- at this point we have packages in ~/debian/build-area/altos, now
|
||||||
|
we move on to the non-Debian part of the release process
|
||||||
|
|
||||||
|
make distclean (just to be sure, this should do nothing)
|
||||||
|
|
||||||
|
./autogen.sh --enable-multi-arch \
|
||||||
|
--with-fat-dir=/home/bdale/web/altusmetrum/
|
||||||
|
make && make fat
|
||||||
|
|
||||||
|
- store a stable copy of ARM binaries for production use
|
||||||
|
|
||||||
|
cp src/chaoskey-v1.0/{*.elf,*.ihx,*.bin,*.map} \
|
||||||
|
src/easymega-v[1-2].0/{*.elf,*.ihx,*.map} \
|
||||||
|
src/easymini-v[1-3].0/{*.elf,*.ihx,*.map} \
|
||||||
|
src/easymotor-v3/{*.elf,*.ihx,*.map} \
|
||||||
|
src/easytimer-v1/{*.elf,*.ihx,*.map} \
|
||||||
|
src/easytimer-v2/{*.elf,*.ihx,*.map} \
|
||||||
|
src/telebt-v[3-4].0/{*.elf,*.ihx,*.map} \
|
||||||
|
src/teledongle-v3.0/{*.elf,*.ihx,*.map} \
|
||||||
|
src/telegps-v[1-3].0/{*.elf,*.ihx,*.map} \
|
||||||
|
src/telemega-v[1-6].0/{*.elf,*.ihx,*.map} \
|
||||||
|
src/telemetrum-v[2-4].0/{*.elf,*.ihx,*.map} \
|
||||||
|
src/telemini-v3.0/{*.elf,*.ihx,*.map} \
|
||||||
|
src/telelco-v2.0/{*.elf,*.ihx,*.map} \
|
||||||
|
src/telefireeight-v[1-2].0/{*.elf,*.ihx,*.map} \
|
||||||
|
~/altusmetrumllc/Binaries/
|
||||||
|
cp src/chaoskey-v1.0/flash-loader/{*.elf,*.bin,*.map} \
|
||||||
|
src/easymega-v[1-2].0/flash-loader/*.elf \
|
||||||
|
src/easymini-v[1-3].0/flash-loader/*.elf \
|
||||||
|
src/easymotor-v3/flash-loader/*.elf \
|
||||||
|
src/easytimer-v1/flash-loader/*.elf \
|
||||||
|
src/easytimer-v2/flash-loader/*.elf \
|
||||||
|
src/telebt-v[3-4].0/flash-loader/{*.elf,*.bin,*.map} \
|
||||||
|
src/teledongle-v3.0/flash-loader/*.elf \
|
||||||
|
src/telegps-v[1-3].0/flash-loader/{*.elf,*.bin,*.map} \
|
||||||
|
src/telemega-v[1-6].0/flash-loader/*.elf \
|
||||||
|
src/telemetrum-v[2-4].0/flash-loader/*.elf \
|
||||||
|
src/telemini-v3.0/flash-loader/{*.elf,*.bin,*.map} \
|
||||||
|
src/telelco-v2.0/flash-loader/*.elf \
|
||||||
|
src/telefireeight-v[1-2].0/flash-loader/*.elf \
|
||||||
|
~/altusmetrumllc/Binaries/loaders/
|
||||||
|
(cd ~/altusmetrumllc ; git add Binaries ; git commit -a)
|
||||||
|
- remove previous versions (only keep latest release)
|
||||||
|
(cd ~/altusmetrumllc ; git push)
|
||||||
|
|
||||||
|
- Push new release to web site
|
||||||
|
|
||||||
|
make fat-install
|
||||||
|
(cd doc ; make publish)
|
||||||
|
|
||||||
|
(cd ~/web/altusmetrum/ && \
|
||||||
|
git add */releases && git commit -m'Release <rev>' && \
|
||||||
|
git push origin master)
|
||||||
|
|
||||||
|
- upload the Debian package
|
||||||
|
|
||||||
|
- clean up
|
||||||
|
|
||||||
|
sudo debian/rules clean
|
||||||
|
git push origin master branch-<version> debian pristine-tar
|
||||||
|
git push --tags
|
||||||
|
|
||||||
|
Testing before a release
|
||||||
|
|
||||||
|
To verify that a build works, the following need to be checked
|
||||||
|
on each platform:
|
||||||
|
|
||||||
|
1) Install package
|
||||||
|
|
||||||
|
2) Connect TM *and* TD devices. Verify that you can Monitor
|
||||||
|
Flight from the TD and Configure Telemetrum from the TM.
|
||||||
|
|
||||||
|
3) Replay Flight, using your favorite .eeprom file. Check
|
||||||
|
each tab, especially the 'Site Map' tab. Make sure the
|
||||||
|
sound works.
|
||||||
|
|
||||||
|
4) Graph Data. Graph a favorite .eeprom file. Make sure you
|
||||||
|
can zoom in on some region of the graph.
|
11
altosdroid/.gitignore
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
.gradle
|
||||||
|
.idea
|
||||||
|
build/
|
||||||
|
app/build/
|
||||||
|
app/libs/
|
||||||
|
app/app.iml
|
||||||
|
altosdroid.iml
|
||||||
|
local.properties
|
||||||
|
app/src/main/AndroidManifest.xml
|
||||||
|
app/src/main/java/org/altusmetrum/AltosDroid/BuildInfo.java
|
||||||
|
app/src/main/res/drawable/*led.png
|
43
altosdroid/.project
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>AltosDroid</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
|
||||||
|
<triggers>auto,full,incremental,</triggers>
|
||||||
|
<arguments>
|
||||||
|
<dictionary>
|
||||||
|
<key>LaunchConfigHandle</key>
|
||||||
|
<value><project>/.externalToolBuilders/Generate BuildInfo.java.launch</value>
|
||||||
|
</dictionary>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
106
altosdroid/Makefile.am
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
DBG_APK=app/build/outputs/apk/debug/app-debug.apk
|
||||||
|
REL_APK=app/build/outputs/apk/release/app-release.apk
|
||||||
|
|
||||||
|
if ANDROID
|
||||||
|
all_target=$(DBG_APK)
|
||||||
|
all_target += bin/AltosDroid-debug.apk
|
||||||
|
if ANDROID_RELEASE
|
||||||
|
all_target+=$(REL_APK)
|
||||||
|
all_target += bin/AltosDroid-release.apk
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
all_target=
|
||||||
|
endif
|
||||||
|
|
||||||
|
SDK=$(ANDROID_SDK)
|
||||||
|
SDK_TARGET=$(shell sed -ne 's/^target=//p' project.properties)
|
||||||
|
|
||||||
|
DX=$(SDK)/platform-tools/dx
|
||||||
|
ADB=$(SDK)/platform-tools/adb
|
||||||
|
AAPT=$(SDK)/platform-tools/aapt
|
||||||
|
APKBUILDER=$(SDK)/tools/apkbuilder
|
||||||
|
ZIPALIGN_A=$(SDK)/tools/zipalign
|
||||||
|
ZIPALIGN_B=$(shell ls $(SDK)/build-tools/*/zipalign | tail -1)
|
||||||
|
|
||||||
|
JAVA_SRC_DIR=app/src/main/java/org/altusmetrum/AltosDroid
|
||||||
|
EXT_LIBDIR=app/libs
|
||||||
|
RES_DIR=app/src/main/res
|
||||||
|
DRAWABLE_DIR=$(RES_DIR)/drawable
|
||||||
|
LAYOUT_DIR=$(RES_DIR)/layout
|
||||||
|
MENU_DIR=$(RES_DIR)/menu
|
||||||
|
VALUES_DIR=$(RES_DIR)/values
|
||||||
|
XML_DIR=$(RES_DIR)/xml
|
||||||
|
ALTOSLIB_SRCDIR=../altoslib
|
||||||
|
ALTOSLIB_JAR=altoslib_$(ALTOSLIB_VERSION).jar
|
||||||
|
|
||||||
|
ALTOSLIB=$(EXT_LIBDIR)/$(ALTOSLIB_JAR)
|
||||||
|
|
||||||
|
JAVA_SRC=$(JAVA_SRC_DIR)/*.java $(JAVA_SRC_DIR)/BuildInfo.java
|
||||||
|
|
||||||
|
DRAWABLES=\
|
||||||
|
$(DRAWABLE_DIR)/redled.png \
|
||||||
|
$(DRAWABLE_DIR)/greenled.png \
|
||||||
|
$(DRAWABLE_DIR)/grayled.png
|
||||||
|
|
||||||
|
GRADLEW=JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 bash ./gradlew --no-daemon
|
||||||
|
|
||||||
|
LAYOUTS=$(LAYOUT_DIR)/*.xml
|
||||||
|
MENUS=$(MENU_DIR)/*.xml
|
||||||
|
VALUES=$(VALUES_DIR)/*.xml
|
||||||
|
XMLS=$(XML_DIR)/*.xml app/src/main/AndroidManifest.xml
|
||||||
|
|
||||||
|
RES=$(LAYOUTS) $(MENUS) $(VALUES) $(XMLS)
|
||||||
|
|
||||||
|
SRC=$(JAVA_SRC) $(DRAWABLES) $(RES)
|
||||||
|
|
||||||
|
all: $(all_target)
|
||||||
|
|
||||||
|
bin/AltosDroid-debug.apk: $(DBG_APK)
|
||||||
|
mkdir -p bin
|
||||||
|
cp $^ $@
|
||||||
|
|
||||||
|
bin/AltosDroid-release.apk: $(REL_APK)
|
||||||
|
mkdir -p bin
|
||||||
|
cp $^ $@
|
||||||
|
|
||||||
|
.NOTPARALLEL:
|
||||||
|
|
||||||
|
$(ALTOSLIB): $(ALTOSLIB_SRCDIR)/$(ALTOSLIB_JAR)
|
||||||
|
mkdir -p $(EXT_LIBDIR)
|
||||||
|
cd $(EXT_LIBDIR) && ln -sf $(shell echo $(EXT_LIBDIR) | sed 's|[^/]\+|..|g')/$(ALTOSLIB_SRCDIR)/$(ALTOSLIB_JAR) .
|
||||||
|
|
||||||
|
$(JAVA_SRC_DIR)/BuildInfo.java: $(filter-out $(JAVA_SRC_DIR)/BuildInfo.java,$(shell echo $(JAVA_SRC)))
|
||||||
|
./buildinfo.sh
|
||||||
|
|
||||||
|
$(DRAWABLE_DIR)/%.png: ../icon/%.png
|
||||||
|
cd $(DRAWABLE_DIR) && ln -sf $(shell echo $(DRAWABLE_DIR) | sed 's|[^/]\+|..|g')/$< .
|
||||||
|
|
||||||
|
if ANDROID
|
||||||
|
install-release: $(REL_APK)
|
||||||
|
$(ADB) install -r $(REL_APK)
|
||||||
|
|
||||||
|
install-debug: $(DBG_APK)
|
||||||
|
$(ADB) install -r $(DBG_APK)
|
||||||
|
|
||||||
|
$(DBG_APK): $(SRC) $(ALTOSLIB)
|
||||||
|
$(GRADLEW) assembleDebug
|
||||||
|
|
||||||
|
$(REL_APK): $(SRC) $(ALTOSLIB)
|
||||||
|
$(GRADLEW) build
|
||||||
|
|
||||||
|
release: $(REL_APK)
|
||||||
|
|
||||||
|
clean-local: $(GOOGLE_PLAY_SERVICES_LIB)
|
||||||
|
$(GRADLEW) clean
|
||||||
|
rm -f $(JAVA_SRC_DIR)/BuildInfo.java
|
||||||
|
rm -f $(DRAWABLES)
|
||||||
|
rm -rf $(EXT_LIBDIR)
|
||||||
|
rm -rf $(GOOGLE_PLAY_SERVICES_LIB)
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
clean-local:
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
clean: clean-local
|
108
altosdroid/Notebook
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
Desired AltosDroid feature list
|
||||||
|
|
||||||
|
*) GPS satellite status tab. Monitor GPS C/N0 numbers and
|
||||||
|
SVIDs. Provides more info before GPS lock is acquired.
|
||||||
|
|
||||||
|
*) Channel scanning. Provide the ability to search for telemetry
|
||||||
|
signals like AltosUI does.
|
||||||
|
|
||||||
|
*) Random frequency selection. Provide some mechanism to input
|
||||||
|
arbitrary radio frequencies. Could be like AltosUI which allows
|
||||||
|
you to edit the list of frequencies and assign names to them,
|
||||||
|
or perhaps something better.
|
||||||
|
|
||||||
|
*) TM configuration from the droid
|
||||||
|
|
||||||
|
*) Monitor-idle mode
|
||||||
|
|
||||||
|
*) Online maps comes up tracking object at 0,0
|
||||||
|
|
||||||
|
*) Have names for each serial number, default to callsign
|
||||||
|
|
||||||
|
Completed features
|
||||||
|
|
||||||
|
*) Highlight current frequency in the frequency list.
|
||||||
|
|
||||||
|
Placed current frequency in title bar
|
||||||
|
|
||||||
|
*) Remember most-recently-used TBT and frequency, perhaps
|
||||||
|
auto-connect at startup.
|
||||||
|
|
||||||
|
Done
|
||||||
|
|
||||||
|
*) Re-loading flight data from .telem file to get back to
|
||||||
|
'find my rocket' mode after shutting down the application.
|
||||||
|
|
||||||
|
Done
|
||||||
|
|
||||||
|
*) Imperial Units mode
|
||||||
|
|
||||||
|
Done
|
||||||
|
|
||||||
|
*) Select satellite imaging mode
|
||||||
|
|
||||||
|
Done
|
||||||
|
|
||||||
|
*) Deal with long bluetooth list. Currently, a list longer than
|
||||||
|
the screen makes it impossible to use entries off the bottom.
|
||||||
|
|
||||||
|
Done
|
||||||
|
|
||||||
|
*) Pickle/unpickle state instead of reloading entire history from
|
||||||
|
file. Current restart time is lengthy.
|
||||||
|
|
||||||
|
Done
|
||||||
|
|
||||||
|
*) Offline maps
|
||||||
|
|
||||||
|
Done
|
||||||
|
|
||||||
|
*) Multi-tracker management
|
||||||
|
|
||||||
|
Done
|
||||||
|
|
||||||
|
*) Provide units for age field, turn red if old
|
||||||
|
|
||||||
|
Done
|
||||||
|
|
||||||
|
*) TeleBT battery voltage
|
||||||
|
|
||||||
|
Done
|
||||||
|
|
||||||
|
*) Evaluate performance issues
|
||||||
|
|
||||||
|
Done. Offline maps were duplicating tabs at every redisplay.
|
||||||
|
|
||||||
|
*) Merge offline/online maps into single tab with mode
|
||||||
|
|
||||||
|
Done.
|
||||||
|
|
||||||
|
*) Auto select tracker after long delay
|
||||||
|
|
||||||
|
Done.
|
||||||
|
|
||||||
|
*) Select tracker by clicking map
|
||||||
|
|
||||||
|
Done.
|
||||||
|
|
||||||
|
*) Convert to four tab design:
|
||||||
|
|
||||||
|
Done.
|
||||||
|
|
||||||
|
*) Make voice responses depend on selected tab
|
||||||
|
|
||||||
|
Done.
|
||||||
|
|
||||||
|
*) Monitor TeleMega igniters
|
||||||
|
|
||||||
|
Done. Visible only in Pad tab
|
||||||
|
|
||||||
|
*) Make it harder to switch trackers in map view. Too easy to touch
|
||||||
|
the screen and switch on accident.
|
||||||
|
|
||||||
|
Done. A menu pops up with trackers within a small radius of
|
||||||
|
the touch point, letting you cancel if that wasn't your intent.
|
||||||
|
|
||||||
|
*) Make sure it keeps talking with the screen blanked
|
||||||
|
|
||||||
|
Done. Don't shut down voice when stopping UI.
|
1
altosdroid/app/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
build.gradle
|
47
altosdroid/app/build.gradle.in
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
|
def keystorePropertiesFile = file(System.properties['user.home'] + "/altusmetrumllc/android_keystore.properties")
|
||||||
|
def keystoreProperties = new Properties()
|
||||||
|
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||||
|
|
||||||
|
android {
|
||||||
|
signingConfigs {
|
||||||
|
release {
|
||||||
|
storeFile file(System.properties['user.home'] + "/altusmetrumllc/android_keystore.jks")
|
||||||
|
storePassword keystoreProperties['storePassword']
|
||||||
|
keyAlias keystoreProperties['keyAlias']
|
||||||
|
keyPassword keystoreProperties['keyPassword']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileSdkVersion 28
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "org.altusmetrum.AltosDroid"
|
||||||
|
minSdkVersion 21
|
||||||
|
targetSdkVersion 31
|
||||||
|
versionCode @ANDROID_VERSION@
|
||||||
|
versionName "@VERSION@"
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
signingConfig signingConfigs.release
|
||||||
|
minifyEnabled false
|
||||||
|
debuggable false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
debug {
|
||||||
|
debuggable true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lintOptions {
|
||||||
|
abortOnError false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.0.0'
|
||||||
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
|
implementation 'androidx.core:core:1.2.0'
|
||||||
|
implementation 'com.google.android.gms:play-services-maps:17.0.0'
|
||||||
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
}
|
21
altosdroid/app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
116
altosdroid/app/src/main/AndroidManifest.xml.in
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2012 Mike Beattie <mike@ethernal.org>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="org.altusmetrum.AltosDroid">
|
||||||
|
<!-- Google Maps -->
|
||||||
|
<uses-feature android:glEsVersion="0x00020000" android:required="true"/>
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
|
||||||
|
<!-- Permissions needed to access bluetooth -->
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||||
|
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||||
|
<!-- Permissions needed to save Telemetry logs to SD card -->
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
<!-- Permissions needed for GoogleMaps -->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
|
|
||||||
|
<permission android:name="org.altusmetrum.AltosDroid.permission.MAPS_RECEIVE"
|
||||||
|
android:protectionLevel="signature"/>
|
||||||
|
<uses-permission android:name="org.altusmetrum.AltosDroid.permission.MAPS_RECEIVE"/>
|
||||||
|
|
||||||
|
<!-- Permissions needed to access USB OTG -->
|
||||||
|
<uses-feature android:name="android.hardware.usb.host" android:required="false" />
|
||||||
|
|
||||||
|
<application android:label="@string/app_name"
|
||||||
|
android:icon="@drawable/app_icon"
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:theme="@style/Medium">
|
||||||
|
|
||||||
|
<activity android:name="org.altusmetrum.AltosDroid.AltosDroid"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:configChanges="orientation|keyboardHidden"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
|
||||||
|
</intent-filter>
|
||||||
|
<meta-data
|
||||||
|
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
|
||||||
|
android:resource="@xml/device_filter" />
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<activity android:name=".DeviceListActivity"
|
||||||
|
android:label="@string/select_device"
|
||||||
|
android:theme="@android:style/Theme.Dialog"
|
||||||
|
android:configChanges="orientation|keyboardHidden" />
|
||||||
|
|
||||||
|
<activity android:name=".SelectTrackerActivity"
|
||||||
|
android:label="@string/select_tracker"
|
||||||
|
android:theme="@android:style/Theme.Dialog"
|
||||||
|
android:configChanges="orientation|keyboardHidden" />
|
||||||
|
|
||||||
|
<activity android:name=".PreloadMapActivity"
|
||||||
|
android:label="@string/preload_maps"
|
||||||
|
android:theme="@android:style/Theme.Dialog"
|
||||||
|
android:configChanges="orientation|keyboardHidden" />
|
||||||
|
|
||||||
|
<activity android:name=".MapTypeActivity"
|
||||||
|
android:label="@string/map_type"
|
||||||
|
android:theme="@android:style/Theme.Dialog"
|
||||||
|
android:configChanges="orientation|keyboardHidden" />
|
||||||
|
|
||||||
|
<activity android:name=".IdleModeActivity"
|
||||||
|
android:label="@string/idle_mode"
|
||||||
|
android:theme="@android:style/Theme.Dialog"
|
||||||
|
android:configChanges="orientation|keyboardHidden" />
|
||||||
|
|
||||||
|
<activity android:name=".IgniterActivity"
|
||||||
|
android:label="@string/igniters"
|
||||||
|
android:theme="@android:style/Theme.Dialog"
|
||||||
|
android:configChanges="orientation|keyboardHidden" />
|
||||||
|
|
||||||
|
<activity android:name=".SetupActivity"
|
||||||
|
android:label="@string/setup"
|
||||||
|
android:theme="@android:style/Theme.Dialog"
|
||||||
|
android:configChanges="orientation" />
|
||||||
|
|
||||||
|
<activity android:name=".ManageFrequenciesActivity"
|
||||||
|
android:label="@string/manage_frequencies"
|
||||||
|
android:theme="@android:style/Theme.Dialog"
|
||||||
|
android:configChanges="orientation|keyboard" />
|
||||||
|
|
||||||
|
<service android:name=".TelemetryService" />
|
||||||
|
|
||||||
|
<meta-data android:name="com.google.android.maps.v2.API_KEY"
|
||||||
|
android:value="@GOOGLEKEY@"/>
|
||||||
|
<meta-data android:name="com.google.android.gms.version"
|
||||||
|
android:value="@integer/google_play_services_version" />
|
||||||
|
</application>
|
||||||
|
</manifest>
|
@ -0,0 +1,224 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2011 Keith Packard <keithp@keithp.com>
|
||||||
|
* Copyright © 2012 Mike Beattie <mike@ethernal.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothSocket;
|
||||||
|
import android.os.Handler;
|
||||||
|
|
||||||
|
public class AltosBluetooth extends AltosDroidLink {
|
||||||
|
|
||||||
|
private ConnectThread connect_thread = null;
|
||||||
|
|
||||||
|
private BluetoothDevice device;
|
||||||
|
private BluetoothSocket socket;
|
||||||
|
private InputStream input;
|
||||||
|
private OutputStream output;
|
||||||
|
private boolean pause;
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
public AltosBluetooth(BluetoothDevice device, Handler handler, boolean pause) {
|
||||||
|
super(handler);
|
||||||
|
this.device = device;
|
||||||
|
this.handler = handler;
|
||||||
|
this.pause = pause;
|
||||||
|
|
||||||
|
connect_thread = new ConnectThread();
|
||||||
|
connect_thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void connected() {
|
||||||
|
if (closed()) {
|
||||||
|
AltosDebug.debug("connected after closed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AltosDebug.check_ui("connected\n");
|
||||||
|
try {
|
||||||
|
synchronized(this) {
|
||||||
|
if (socket != null) {
|
||||||
|
input = socket.getInputStream();
|
||||||
|
output = socket.getOutputStream();
|
||||||
|
super.connected();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
connect_failed();
|
||||||
|
} catch (IOException io) {
|
||||||
|
connect_failed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void connect_failed() {
|
||||||
|
if (closed()) {
|
||||||
|
AltosDebug.debug("connect_failed after closed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
close_device();
|
||||||
|
input = null;
|
||||||
|
output = null;
|
||||||
|
handler.obtainMessage(TelemetryService.MSG_CONNECT_FAILED, this).sendToTarget();
|
||||||
|
AltosDebug.error("ConnectThread: Failed to establish connection");
|
||||||
|
}
|
||||||
|
|
||||||
|
void close_device() {
|
||||||
|
BluetoothSocket tmp_socket;
|
||||||
|
|
||||||
|
synchronized(this) {
|
||||||
|
tmp_socket = socket;
|
||||||
|
socket = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmp_socket != null) {
|
||||||
|
try {
|
||||||
|
tmp_socket.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
AltosDebug.error("close_socket failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
super.close();
|
||||||
|
input = null;
|
||||||
|
output = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
|
||||||
|
|
||||||
|
private void create_socket(BluetoothDevice device) {
|
||||||
|
|
||||||
|
BluetoothSocket tmp_socket = null;
|
||||||
|
|
||||||
|
AltosDebug.check_ui("create_socket\n");
|
||||||
|
try {
|
||||||
|
tmp_socket = device.createInsecureRfcommSocketToServiceRecord(SPP_UUID);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
if (socket != null) {
|
||||||
|
AltosDebug.debug("Socket already allocated %s", socket.toString());
|
||||||
|
close_device();
|
||||||
|
}
|
||||||
|
synchronized (this) {
|
||||||
|
socket = tmp_socket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ConnectThread extends Thread {
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
AltosDebug.debug("ConnectThread: BEGIN (pause %b)", pause);
|
||||||
|
setName("ConnectThread");
|
||||||
|
|
||||||
|
if (pause) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(4000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
create_socket(device);
|
||||||
|
// Always cancel discovery because it will slow down a connection
|
||||||
|
try {
|
||||||
|
BluetoothAdapter.getDefaultAdapter().cancelDiscovery();
|
||||||
|
} catch (Exception e) {
|
||||||
|
AltosDebug.debug("cancelDiscovery exception %s", e.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
BluetoothSocket local_socket = null;
|
||||||
|
|
||||||
|
synchronized (AltosBluetooth.this) {
|
||||||
|
if (!closed())
|
||||||
|
local_socket = socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (local_socket != null) {
|
||||||
|
try {
|
||||||
|
// Make a connection to the BluetoothSocket
|
||||||
|
// This is a blocking call and will only return on a
|
||||||
|
// successful connection or an exception
|
||||||
|
local_socket.connect();
|
||||||
|
} catch (Exception e) {
|
||||||
|
AltosDebug.debug("Connect exception %s", e.toString());
|
||||||
|
try {
|
||||||
|
local_socket.close();
|
||||||
|
} catch (Exception ce) {
|
||||||
|
AltosDebug.debug("Close exception %s", ce.toString());
|
||||||
|
}
|
||||||
|
local_socket = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (local_socket != null) {
|
||||||
|
connected();
|
||||||
|
} else {
|
||||||
|
connect_failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
AltosDebug.debug("ConnectThread: completed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void wait_connected() throws InterruptedException, IOException {
|
||||||
|
AltosDebug.check_ui("wait_connected\n");
|
||||||
|
if (input == null && socket != null) {
|
||||||
|
AltosDebug.debug("wait_connected...");
|
||||||
|
wait();
|
||||||
|
AltosDebug.debug("wait_connected done");
|
||||||
|
}
|
||||||
|
if (socket == null)
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
|
||||||
|
int write(byte[] buffer, int len) {
|
||||||
|
if (output == null)
|
||||||
|
return -1;
|
||||||
|
try {
|
||||||
|
output.write(buffer, 0, len);
|
||||||
|
} catch (IOException ie) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int read(byte[] buffer, int len) {
|
||||||
|
if (input == null)
|
||||||
|
return -1;
|
||||||
|
try {
|
||||||
|
return input.read(buffer, 0, len);
|
||||||
|
} catch (IOException ie) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stubs of required methods when extending AltosLink
|
||||||
|
public boolean can_cancel_reply() { return false; }
|
||||||
|
public boolean show_reply_timeout() { return true; }
|
||||||
|
public void hide_reply_timeout() { }
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2015 Keith Packard <keithp@keithp.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import java.lang.*;
|
||||||
|
|
||||||
|
import android.content.*;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.os.*;
|
||||||
|
import android.content.pm.*;
|
||||||
|
|
||||||
|
public class AltosDebug {
|
||||||
|
// Debugging
|
||||||
|
static final String TAG = "AltosDroid";
|
||||||
|
|
||||||
|
static boolean D = true;
|
||||||
|
|
||||||
|
static void init(Context context) {
|
||||||
|
ApplicationInfo app_info = context.getApplicationInfo();
|
||||||
|
|
||||||
|
if ((app_info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
|
||||||
|
Log.d(TAG, "Enable debugging\n");
|
||||||
|
D = true;
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Disable debugging\n");
|
||||||
|
D = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void info(String format, Object ... arguments) {
|
||||||
|
Log.i(TAG, String.format(format, arguments));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void debug(String format, Object ... arguments) {
|
||||||
|
if (D)
|
||||||
|
Log.d(TAG, String.format(format, arguments));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void error(String format, Object ... arguments) {
|
||||||
|
Log.e(TAG, String.format(format, arguments));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void trace(String format, Object ... arguments) {
|
||||||
|
error(format, arguments);
|
||||||
|
for (StackTraceElement el : Thread.currentThread().getStackTrace())
|
||||||
|
Log.e(TAG, "\t" + el.toString() + "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check_ui(String format, Object ... arguments) {
|
||||||
|
if (Looper.myLooper() == Looper.getMainLooper())
|
||||||
|
trace("ON UI THREAD " + format, arguments);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2015 Keith Packard <keithp@keithp.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
public abstract class AltosDroidLink extends AltosLink {
|
||||||
|
|
||||||
|
Handler handler;
|
||||||
|
|
||||||
|
Thread input_thread = null;
|
||||||
|
|
||||||
|
public double frequency() {
|
||||||
|
return frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int telemetry_rate() {
|
||||||
|
return telemetry_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save_frequency() {
|
||||||
|
AltosPreferences.set_frequency(0, frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save_telemetry_rate() {
|
||||||
|
AltosPreferences.set_telemetry_rate(0, telemetry_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object closed_lock = new Object();
|
||||||
|
boolean closing = false;
|
||||||
|
boolean closed = false;
|
||||||
|
|
||||||
|
public boolean closed() {
|
||||||
|
synchronized(closed_lock) {
|
||||||
|
return closing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void connected() throws InterruptedException {
|
||||||
|
input_thread = new Thread(this);
|
||||||
|
input_thread.start();
|
||||||
|
|
||||||
|
// Configure the newly connected device for telemetry
|
||||||
|
print("~\nE 0\n");
|
||||||
|
set_monitor(false);
|
||||||
|
AltosDebug.debug("ConnectThread: connected");
|
||||||
|
|
||||||
|
/* Let TelemetryService know we're connected
|
||||||
|
*/
|
||||||
|
handler.obtainMessage(TelemetryService.MSG_CONNECTED, this).sendToTarget();
|
||||||
|
|
||||||
|
/* Notify other waiting threads that we're connected now
|
||||||
|
*/
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closing() {
|
||||||
|
synchronized(closed_lock) {
|
||||||
|
AltosDebug.debug("Marked closing true");
|
||||||
|
closing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean actually_closed() {
|
||||||
|
synchronized(closed_lock) {
|
||||||
|
return closed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract void close_device();
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
AltosDebug.debug("close(): begin");
|
||||||
|
|
||||||
|
closing();
|
||||||
|
|
||||||
|
flush_output();
|
||||||
|
|
||||||
|
synchronized (closed_lock) {
|
||||||
|
AltosDebug.debug("Marked closed true");
|
||||||
|
closed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
close_device();
|
||||||
|
|
||||||
|
synchronized(this) {
|
||||||
|
|
||||||
|
if (input_thread != null) {
|
||||||
|
AltosDebug.debug("close(): stopping input_thread");
|
||||||
|
try {
|
||||||
|
AltosDebug.debug("close(): input_thread.interrupt().....");
|
||||||
|
input_thread.interrupt();
|
||||||
|
AltosDebug.debug("close(): input_thread.join().....");
|
||||||
|
input_thread.join();
|
||||||
|
} catch (Exception e) {}
|
||||||
|
input_thread = null;
|
||||||
|
}
|
||||||
|
notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract int write(byte[] buffer, int len);
|
||||||
|
|
||||||
|
abstract int read(byte[] buffer, int len);
|
||||||
|
|
||||||
|
private static final int buffer_size = 64;
|
||||||
|
|
||||||
|
private byte[] in_buffer = new byte[buffer_size];
|
||||||
|
private byte[] out_buffer = new byte[buffer_size];
|
||||||
|
private int buffer_len = 0;
|
||||||
|
private int buffer_off = 0;
|
||||||
|
private int out_buffer_off = 0;
|
||||||
|
|
||||||
|
private byte[] debug_chars = new byte[buffer_size];
|
||||||
|
private int debug_off;
|
||||||
|
|
||||||
|
private void debug_input(byte b) {
|
||||||
|
if (b == '\n') {
|
||||||
|
AltosDebug.debug(" " + new String(debug_chars, 0, debug_off));
|
||||||
|
debug_off = 0;
|
||||||
|
} else {
|
||||||
|
if (debug_off < buffer_size)
|
||||||
|
debug_chars[debug_off++] = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disconnected() {
|
||||||
|
if (closed()) {
|
||||||
|
AltosDebug.debug("disconnected after closed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AltosDebug.debug("Sending disconnected message");
|
||||||
|
handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, this).sendToTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getchar() {
|
||||||
|
|
||||||
|
if (actually_closed())
|
||||||
|
return ERROR;
|
||||||
|
|
||||||
|
while (buffer_off == buffer_len) {
|
||||||
|
buffer_len = read(in_buffer, buffer_size);
|
||||||
|
if (buffer_len < 0) {
|
||||||
|
AltosDebug.debug("ERROR returned from getchar()");
|
||||||
|
disconnected();
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
buffer_off = 0;
|
||||||
|
}
|
||||||
|
// if (AltosDebug.D)
|
||||||
|
// debug_input(in_buffer[buffer_off]);
|
||||||
|
return in_buffer[buffer_off++];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void flush_output() {
|
||||||
|
super.flush_output();
|
||||||
|
|
||||||
|
if (actually_closed()) {
|
||||||
|
out_buffer_off = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (out_buffer_off != 0) {
|
||||||
|
int sent = write(out_buffer, out_buffer_off);
|
||||||
|
|
||||||
|
if (sent <= 0) {
|
||||||
|
AltosDebug.debug("flush_output() failed");
|
||||||
|
out_buffer_off = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sent < out_buffer_off)
|
||||||
|
System.arraycopy(out_buffer, 0, out_buffer, sent, out_buffer_off - sent);
|
||||||
|
|
||||||
|
out_buffer_off -= sent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putchar(byte c) {
|
||||||
|
out_buffer[out_buffer_off++] = c;
|
||||||
|
if (out_buffer_off == buffer_size)
|
||||||
|
flush_output();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void print(String data) {
|
||||||
|
byte[] bytes = data.getBytes();
|
||||||
|
// AltosDebug.debug(data.replace('\n', '\\'));
|
||||||
|
for (byte b : bytes)
|
||||||
|
putchar(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AltosDroidLink(Handler handler) {
|
||||||
|
this.handler = handler;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2015 Keith Packard <keithp@keithp.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import android.location.Location;
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
public interface AltosDroidMapInterface {
|
||||||
|
public void onCreateView(AltosDroid altos_droid);
|
||||||
|
|
||||||
|
public void onDestroyView();
|
||||||
|
|
||||||
|
public void set_visible(boolean visible);
|
||||||
|
|
||||||
|
public void center(double lat, double lon, double accuracy);
|
||||||
|
|
||||||
|
public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver);
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2016 Keith Packard <keithp@keithp.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
public interface AltosDroidMapSourceListener {
|
||||||
|
public void map_source_changed(int map_source);
|
||||||
|
}
|
@ -0,0 +1,186 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2014 Keith Packard <keithp@keithp.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
public class AltosDroidPreferences extends AltosPreferences {
|
||||||
|
|
||||||
|
/* Active device preference name */
|
||||||
|
final static String activeDeviceAddressPreference = "ACTIVE-DEVICE-ADDRESS";
|
||||||
|
final static String activeDeviceNamePreference = "ACTIVE-DEVICE-NAME";
|
||||||
|
|
||||||
|
public static final int font_size_small = 0;
|
||||||
|
public static final int font_size_medium = 1;
|
||||||
|
public static final int font_size_large = 2;
|
||||||
|
public static final int font_size_extra = 3;
|
||||||
|
|
||||||
|
final static String fontSizePreference = "FONT-SIZE";
|
||||||
|
|
||||||
|
static int font_size = font_size_medium;
|
||||||
|
|
||||||
|
static DeviceAddress active_device_address;
|
||||||
|
|
||||||
|
/* Map source preference name */
|
||||||
|
final static String mapSourcePreference = "MAP-SOURCE";
|
||||||
|
|
||||||
|
static final int MAP_SOURCE_OFFLINE = 0;
|
||||||
|
static final int MAP_SOURCE_ONLINE = 1;
|
||||||
|
|
||||||
|
static int map_source;
|
||||||
|
|
||||||
|
/* Tracker sort selection */
|
||||||
|
final static String trackerSortPreference = "TRACKER-SORT";
|
||||||
|
|
||||||
|
static int tracker_sort;
|
||||||
|
|
||||||
|
public static void init(Context context) {
|
||||||
|
if (backend != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AltosPreferences.init(new AltosDroidPreferencesBackend(context));
|
||||||
|
|
||||||
|
font_size = backend.getInt(fontSizePreference, font_size_medium);
|
||||||
|
|
||||||
|
String address = backend.getString(activeDeviceAddressPreference, null);
|
||||||
|
String name = backend.getString(activeDeviceNamePreference, null);
|
||||||
|
|
||||||
|
if (address != null && name != null)
|
||||||
|
active_device_address = new DeviceAddress (address, name);
|
||||||
|
|
||||||
|
map_source = backend.getInt(mapSourcePreference, MAP_SOURCE_ONLINE);
|
||||||
|
|
||||||
|
tracker_sort = backend.getInt(trackerSortPreference, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void set_active_device(DeviceAddress address) {
|
||||||
|
if (backend == null)
|
||||||
|
return;
|
||||||
|
synchronized(backend) {
|
||||||
|
active_device_address = address;
|
||||||
|
if (active_device_address != null) {
|
||||||
|
backend.putString(activeDeviceAddressPreference, active_device_address.address);
|
||||||
|
backend.putString(activeDeviceNamePreference, active_device_address.name);
|
||||||
|
} else {
|
||||||
|
backend.remove(activeDeviceAddressPreference);
|
||||||
|
backend.remove(activeDeviceNamePreference);
|
||||||
|
}
|
||||||
|
flush_preferences();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DeviceAddress active_device() {
|
||||||
|
if (backend == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
synchronized(backend) {
|
||||||
|
return active_device_address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static LinkedList<AltosDroidMapSourceListener> map_source_listeners;
|
||||||
|
|
||||||
|
public static void set_map_source(int map_source) {
|
||||||
|
if (backend == null)
|
||||||
|
return;
|
||||||
|
synchronized(backend) {
|
||||||
|
AltosDroidPreferences.map_source = map_source;
|
||||||
|
backend.putInt(mapSourcePreference, map_source);
|
||||||
|
flush_preferences();
|
||||||
|
}
|
||||||
|
if (map_source_listeners != null) {
|
||||||
|
for (AltosDroidMapSourceListener l : map_source_listeners) {
|
||||||
|
l.map_source_changed(map_source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int map_source() {
|
||||||
|
if (backend == null)
|
||||||
|
return MAP_SOURCE_ONLINE;
|
||||||
|
synchronized(backend) {
|
||||||
|
return map_source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void register_map_source_listener(AltosDroidMapSourceListener l) {
|
||||||
|
if (backend == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
synchronized(backend) {
|
||||||
|
if (map_source_listeners == null)
|
||||||
|
map_source_listeners = new LinkedList<AltosDroidMapSourceListener>();
|
||||||
|
map_source_listeners.add(l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void unregister_map_source_listener(AltosDroidMapSourceListener l) {
|
||||||
|
if (backend == null)
|
||||||
|
return;
|
||||||
|
synchronized(backend) {
|
||||||
|
map_source_listeners.remove(l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int font_size() {
|
||||||
|
if (backend == null)
|
||||||
|
return font_size_medium;
|
||||||
|
|
||||||
|
synchronized (backend) {
|
||||||
|
return font_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void set_font_size(int new_font_size) {
|
||||||
|
if (backend == null)
|
||||||
|
return;
|
||||||
|
synchronized (backend) {
|
||||||
|
if (font_size != new_font_size) {
|
||||||
|
font_size = new_font_size;
|
||||||
|
backend.putInt(fontSizePreference, font_size);
|
||||||
|
flush_preferences();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static int tracker_sort() {
|
||||||
|
if (backend == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
synchronized(backend) {
|
||||||
|
return tracker_sort;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void set_tracker_sort(int new_tracker_sort) {
|
||||||
|
if (backend == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
synchronized(backend) {
|
||||||
|
if (tracker_sort != new_tracker_sort) {
|
||||||
|
tracker_sort = new_tracker_sort;
|
||||||
|
backend.putInt(trackerSortPreference, tracker_sort);
|
||||||
|
flush_preferences();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2012 Mike Beattie <mike@ethernal.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Map;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.util.*;
|
||||||
|
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
public class AltosDroidPreferencesBackend extends AltosPreferencesBackend {
|
||||||
|
public final static String NAME = "org.altusmetrum.AltosDroid";
|
||||||
|
private Context context = null;
|
||||||
|
private SharedPreferences prefs = null;
|
||||||
|
private SharedPreferences.Editor editor = null;
|
||||||
|
|
||||||
|
public AltosDroidPreferencesBackend(Context in_context) {
|
||||||
|
this(in_context, NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AltosDroidPreferencesBackend(Context in_context, String in_prefs) {
|
||||||
|
context = in_context;
|
||||||
|
prefs = context.getSharedPreferences(in_prefs, 0);
|
||||||
|
editor = prefs.edit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] keys() {
|
||||||
|
Map<String, ?> all = prefs.getAll();
|
||||||
|
Object[] ao = all.keySet().toArray();
|
||||||
|
|
||||||
|
String[] as = new String[ao.length];
|
||||||
|
for (int i = 0; i < ao.length; i++)
|
||||||
|
as[i] = (String) ao[i];
|
||||||
|
return as;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AltosPreferencesBackend node(String key) {
|
||||||
|
if (!nodeExists(key))
|
||||||
|
putBoolean(key, true);
|
||||||
|
return new AltosDroidPreferencesBackend(context, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean nodeExists(String key) {
|
||||||
|
return prefs.contains(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getBoolean(String key, boolean def) {
|
||||||
|
return prefs.getBoolean(key, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getDouble(String key, double def) {
|
||||||
|
Float f = Float.valueOf(prefs.getFloat(key, (float)def));
|
||||||
|
return f.doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInt(String key, int def) {
|
||||||
|
return prefs.getInt(key, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getString(String key, String def) {
|
||||||
|
String ret;
|
||||||
|
if (key.equals(AltosPreferences.logdirPreference))
|
||||||
|
ret = null;
|
||||||
|
else
|
||||||
|
ret = prefs.getString(key, def);
|
||||||
|
AltosDebug.debug("AltosDroidPreferencesBackend get string %s:\n", key);
|
||||||
|
if (ret == null)
|
||||||
|
AltosDebug.debug(" (null)\n");
|
||||||
|
else {
|
||||||
|
String[] lines = ret.split("\n");
|
||||||
|
for (String l : lines)
|
||||||
|
AltosDebug.debug(" %s\n", l);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getBytes(String key, byte[] def) {
|
||||||
|
String save = prefs.getString(key, null);
|
||||||
|
|
||||||
|
if (save == null)
|
||||||
|
return def;
|
||||||
|
|
||||||
|
byte[] bytes = Base64.decode(save, Base64.DEFAULT);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putBoolean(String key, boolean value) {
|
||||||
|
editor.putBoolean(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putDouble(String key, double value) {
|
||||||
|
editor.putFloat(key, (float)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putInt(String key, int value) {
|
||||||
|
editor.putInt(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putString(String key, String value) {
|
||||||
|
// AltosDebug.debug("AltosDroidPreferencesBackend put string %s:\n", key);
|
||||||
|
// String[] lines = value.split("\n");
|
||||||
|
// for (String l : lines)
|
||||||
|
// AltosDebug.debug(" %s\n", l);
|
||||||
|
editor.putString(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putBytes(String key, byte[] bytes) {
|
||||||
|
String save = Base64.encodeToString(bytes, Base64.DEFAULT);
|
||||||
|
editor.putString(key, save);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(String key) {
|
||||||
|
AltosDebug.debug("remove preference %s\n", key);
|
||||||
|
editor.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void flush() {
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public File homeDirectory() {
|
||||||
|
return context.getExternalMediaDirs()[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void debug(String format, Object ... arguments) {
|
||||||
|
AltosDebug.debug(format, arguments);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.*;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
public abstract class AltosDroidTab extends Fragment implements AltosUnitsListener {
|
||||||
|
TelemetryState last_telem_state;
|
||||||
|
AltosState last_state;
|
||||||
|
AltosGreatCircle last_from_receiver;
|
||||||
|
Location last_receiver;
|
||||||
|
AltosDroid altos_droid;
|
||||||
|
|
||||||
|
public abstract void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver);
|
||||||
|
|
||||||
|
public abstract String tab_name();
|
||||||
|
|
||||||
|
public void units_changed(boolean imperial_units) {
|
||||||
|
if (!isHidden())
|
||||||
|
show(last_telem_state, last_state, last_from_receiver, last_receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set_value(TextView text_view,
|
||||||
|
AltosUnits units,
|
||||||
|
int width,
|
||||||
|
double value) {
|
||||||
|
if (value == AltosLib.MISSING)
|
||||||
|
text_view.setText("");
|
||||||
|
else
|
||||||
|
text_view.setText(units.show(width, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set_visible(boolean visible) {
|
||||||
|
FragmentTransaction ft = AltosDroid.fm.beginTransaction();
|
||||||
|
AltosDebug.debug("set visible %b %s\n", visible, tab_name());
|
||||||
|
if (visible) {
|
||||||
|
ft.show(this);
|
||||||
|
show(last_telem_state, last_state, last_from_receiver, last_receiver);
|
||||||
|
} else
|
||||||
|
ft.hide(this);
|
||||||
|
try {
|
||||||
|
ft.commitAllowingStateLoss();
|
||||||
|
} catch (IllegalStateException ie) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
AltosDebug.debug("tab onAttach %s %s\n", tab_name(), this);
|
||||||
|
super.onAttach(context);
|
||||||
|
altos_droid = (AltosDroid) context;
|
||||||
|
altos_droid.registerTab(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetach() {
|
||||||
|
AltosDebug.debug("tab onDetach %s %s\n", tab_name(), this);
|
||||||
|
super.onDetach();
|
||||||
|
altos_droid.unregisterTab(this);
|
||||||
|
altos_droid = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
AltosDebug.debug("onResume tab %s %s\n", tab_name(), this);
|
||||||
|
set_visible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update_ui(TelemetryState telem_state, AltosState state,
|
||||||
|
AltosGreatCircle from_receiver, Location receiver, boolean is_current)
|
||||||
|
{
|
||||||
|
AltosDebug.debug("update_ui %s is_current %b\n", tab_name(), is_current);
|
||||||
|
last_telem_state = telem_state;
|
||||||
|
last_state = state;
|
||||||
|
last_from_receiver = from_receiver;
|
||||||
|
last_receiver = receiver;
|
||||||
|
if (is_current)
|
||||||
|
show(telem_state, state, from_receiver, receiver);
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,527 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2015 Keith Packard <keithp@keithp.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
import android.graphics.*;
|
||||||
|
import android.view.*;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.content.*;
|
||||||
|
import android.util.*;
|
||||||
|
|
||||||
|
class Rocket implements Comparable {
|
||||||
|
AltosLatLon position;
|
||||||
|
String name;
|
||||||
|
int serial;
|
||||||
|
long last_packet;
|
||||||
|
boolean active;
|
||||||
|
AltosMapOffline map_offline;
|
||||||
|
|
||||||
|
void paint() {
|
||||||
|
map_offline.draw_bitmap(position, map_offline.rocket_bitmap, map_offline.rocket_off_x, map_offline.rocket_off_y);
|
||||||
|
map_offline.draw_text(position, name, 0, 3*map_offline.rocket_bitmap.getHeight()/4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_position(AltosLatLon position, long last_packet) {
|
||||||
|
this.position = position;
|
||||||
|
this.last_packet = last_packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_active(boolean active) {
|
||||||
|
this.active = active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compareTo(Object o) {
|
||||||
|
Rocket other = (Rocket) o;
|
||||||
|
|
||||||
|
if (active && !other.active)
|
||||||
|
return 1;
|
||||||
|
if (other.active && !active)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
long diff = last_packet - other.last_packet;
|
||||||
|
|
||||||
|
if (diff > 0)
|
||||||
|
return 1;
|
||||||
|
if (diff < 0)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rocket(int serial, AltosMapOffline map_offline) {
|
||||||
|
this.serial = serial;
|
||||||
|
this.name = String.format("%d", serial);
|
||||||
|
this.map_offline = map_offline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AltosMapOffline extends View implements ScaleGestureDetector.OnScaleGestureListener, AltosMapInterface, AltosDroidMapInterface, AltosMapTypeListener {
|
||||||
|
ScaleGestureDetector scale_detector;
|
||||||
|
boolean scaling;
|
||||||
|
AltosMap map;
|
||||||
|
AltosDroid altos_droid;
|
||||||
|
|
||||||
|
static int scale = 1;
|
||||||
|
|
||||||
|
AltosLatLon here;
|
||||||
|
AltosLatLon there;
|
||||||
|
AltosLatLon pad;
|
||||||
|
|
||||||
|
Canvas canvas;
|
||||||
|
Paint paint;
|
||||||
|
|
||||||
|
Bitmap pad_bitmap;
|
||||||
|
int pad_off_x, pad_off_y;
|
||||||
|
Bitmap rocket_bitmap;
|
||||||
|
int rocket_off_x, rocket_off_y;
|
||||||
|
Bitmap here_bitmap;
|
||||||
|
int here_off_x, here_off_y;
|
||||||
|
|
||||||
|
static final int WHITE = 0xffffffff;
|
||||||
|
static final int RED = 0xffff0000;
|
||||||
|
static final int PINK = 0xffff8080;
|
||||||
|
static final int YELLOW= 0xffffff00;
|
||||||
|
static final int CYAN = 0xff00ffff;
|
||||||
|
static final int BLUE = 0xff0000ff;
|
||||||
|
static final int BLACK = 0xff000000;
|
||||||
|
|
||||||
|
public static final int stateColors[] = {
|
||||||
|
WHITE, // startup
|
||||||
|
WHITE, // idle
|
||||||
|
WHITE, // pad
|
||||||
|
RED, // boost
|
||||||
|
PINK, // fast
|
||||||
|
YELLOW, // coast
|
||||||
|
CYAN, // drogue
|
||||||
|
BLUE, // main
|
||||||
|
BLACK, // landed
|
||||||
|
BLACK, // invalid
|
||||||
|
CYAN, // stateless
|
||||||
|
};
|
||||||
|
|
||||||
|
/* AltosMapInterface */
|
||||||
|
public void debug(String format, Object ... arguments) {
|
||||||
|
AltosDebug.debug(format, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
class MapTile extends AltosMapTile {
|
||||||
|
public void paint(AltosMapTransform t) {
|
||||||
|
AltosPointInt pt = new AltosPointInt(t.screen(upper_left));
|
||||||
|
|
||||||
|
if (canvas.quickReject(pt.x, pt.y, pt.x + px_size, pt.y + px_size, Canvas.EdgeType.AA))
|
||||||
|
return;
|
||||||
|
|
||||||
|
AltosImage altos_image = this.get_image();
|
||||||
|
|
||||||
|
MapImage map_image = (MapImage) altos_image;
|
||||||
|
|
||||||
|
Bitmap bitmap = null;
|
||||||
|
|
||||||
|
if (map_image != null)
|
||||||
|
bitmap = map_image.bitmap;
|
||||||
|
|
||||||
|
if (bitmap != null) {
|
||||||
|
canvas.drawBitmap(bitmap, pt.x, pt.y, paint);
|
||||||
|
} else {
|
||||||
|
paint.setColor(0xff808080);
|
||||||
|
canvas.drawRect(pt.x, pt.y, pt.x + px_size, pt.y + px_size, paint);
|
||||||
|
if (t.has_location()) {
|
||||||
|
String message = null;
|
||||||
|
switch (status) {
|
||||||
|
case AltosMapTile.fetching:
|
||||||
|
message = "Fetching...";
|
||||||
|
break;
|
||||||
|
case AltosMapTile.bad_request:
|
||||||
|
message = "Internal error";
|
||||||
|
break;
|
||||||
|
case AltosMapTile.failed:
|
||||||
|
message = "Network error";
|
||||||
|
break;
|
||||||
|
case AltosMapTile.forbidden:
|
||||||
|
message = "Outside of known launch areas";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (message != null) {
|
||||||
|
Rect bounds = new Rect();
|
||||||
|
paint.getTextBounds(message, 0, message.length(), bounds);
|
||||||
|
|
||||||
|
int width = bounds.right - bounds.left;
|
||||||
|
int height = bounds.bottom - bounds.top;
|
||||||
|
|
||||||
|
float x = pt.x + px_size / 2.0f;
|
||||||
|
float y = pt.y + px_size / 2.0f;
|
||||||
|
x = x - width / 2.0f;
|
||||||
|
y = y + height / 2.0f;
|
||||||
|
paint.setColor(0xff000000);
|
||||||
|
canvas.drawText(message, 0, message.length(), x, y, paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapTile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
|
||||||
|
super(cache, upper_left, center, zoom, maptype, px_size, scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public AltosMapTile new_tile(AltosMapCache cache, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
|
||||||
|
return new MapTile(cache, upper_left, center, zoom, maptype, px_size, scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AltosMapPath new_path() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AltosMapLine new_line() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MapImage implements AltosImage {
|
||||||
|
public Bitmap bitmap;
|
||||||
|
|
||||||
|
public void flush() {
|
||||||
|
if (bitmap != null) {
|
||||||
|
bitmap.recycle();
|
||||||
|
bitmap = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapImage(File file) {
|
||||||
|
bitmap = BitmapFactory.decodeFile(file.getPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AltosImage load_image(File file) throws Exception {
|
||||||
|
return new MapImage(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
class MapMark extends AltosMapMark {
|
||||||
|
public void paint(AltosMapTransform t) {
|
||||||
|
}
|
||||||
|
|
||||||
|
MapMark(double lat, double lon, int state) {
|
||||||
|
super(lat, lon, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
MapMark(double lat, double lon, int state, String label) {
|
||||||
|
super(lat, lon, state, label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AltosMapMark new_mark(double lat, double lon, int state) {
|
||||||
|
return new MapMark(lat, lon, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AltosMapMark new_mark(double lat, double lon, int state, String label) {
|
||||||
|
return new MapMark(lat, lon, state, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int width() {
|
||||||
|
return getWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int height() {
|
||||||
|
return getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void repaint() {
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void repaint(AltosRectangle damage) {
|
||||||
|
postInvalidate(damage.x, damage.y, damage.x + damage.width, damage.y + damage.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set_zoom_label(String label) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void select_object(AltosLatLon latlon) {
|
||||||
|
if (map.transform == null)
|
||||||
|
return;
|
||||||
|
ArrayList<Integer> near = new ArrayList<Integer>();
|
||||||
|
|
||||||
|
for (Rocket rocket : sorted_rockets()) {
|
||||||
|
if (rocket.position == null) {
|
||||||
|
debug("rocket %d has no position\n", rocket.serial);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
double distance = map.transform.hypot(latlon, rocket.position);
|
||||||
|
debug("check select %d distance %g width %d\n", rocket.serial, distance, rocket_bitmap.getWidth());
|
||||||
|
if (distance < rocket_bitmap.getWidth() * 2.0) {
|
||||||
|
debug("selecting %d\n", rocket.serial);
|
||||||
|
near.add(rocket.serial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (near.size() != 0)
|
||||||
|
altos_droid.touch_trackers(near.toArray(new Integer[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
class Line {
|
||||||
|
AltosLatLon a, b;
|
||||||
|
|
||||||
|
void paint() {
|
||||||
|
if (a != null && b != null) {
|
||||||
|
AltosPointDouble a_screen = map.transform.screen(a);
|
||||||
|
AltosPointDouble b_screen = map.transform.screen(b);
|
||||||
|
paint.setColor(0xff8080ff);
|
||||||
|
canvas.drawLine((float) a_screen.x, (float) a_screen.y,
|
||||||
|
(float) b_screen.x, (float) b_screen.y,
|
||||||
|
paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_a(AltosLatLon a) {
|
||||||
|
this.a = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_b(AltosLatLon b) {
|
||||||
|
this.b = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
Line() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Line line = new Line();
|
||||||
|
|
||||||
|
int stroke_width = 20;
|
||||||
|
|
||||||
|
void draw_text(AltosLatLon lat_lon, String text, int off_x, int off_y) {
|
||||||
|
if (lat_lon != null && map != null && map.transform != null) {
|
||||||
|
AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon));
|
||||||
|
|
||||||
|
Rect bounds = new Rect();
|
||||||
|
paint.getTextBounds(text, 0, text.length(), bounds);
|
||||||
|
|
||||||
|
int width = bounds.right - bounds.left;
|
||||||
|
int height = bounds.bottom - bounds.top;
|
||||||
|
|
||||||
|
float x = pt.x;
|
||||||
|
float y = pt.y;
|
||||||
|
x = x - width / 2.0f - off_x;
|
||||||
|
y = y + height / 2.0f - off_y;
|
||||||
|
paint.setColor(0xff000000);
|
||||||
|
canvas.drawText(text, 0, text.length(), x, y, paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<Integer,Rocket> rockets = new HashMap<Integer,Rocket>();
|
||||||
|
|
||||||
|
void draw_bitmap(AltosLatLon lat_lon, Bitmap bitmap, int off_x, int off_y) {
|
||||||
|
if (lat_lon != null && map != null && map.transform != null) {
|
||||||
|
AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon));
|
||||||
|
|
||||||
|
canvas.drawBitmap(bitmap, pt.x - off_x, pt.y - off_y, paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Rocket[] sorted_rockets() {
|
||||||
|
Rocket[] rocket_array = rockets.values().toArray(new Rocket[0]);
|
||||||
|
|
||||||
|
Arrays.sort(rocket_array);
|
||||||
|
return rocket_array;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void draw_positions() {
|
||||||
|
line.set_a(there);
|
||||||
|
line.set_b(here);
|
||||||
|
line.paint();
|
||||||
|
draw_bitmap(pad, pad_bitmap, pad_off_x, pad_off_y);
|
||||||
|
|
||||||
|
for (Rocket rocket : sorted_rockets())
|
||||||
|
rocket.paint();
|
||||||
|
draw_bitmap(here, here_bitmap, here_off_x, here_off_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas view_canvas) {
|
||||||
|
if (map == null) {
|
||||||
|
debug("MapView draw without map\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (map.transform == null) {
|
||||||
|
debug("MapView draw without transform\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
canvas = view_canvas;
|
||||||
|
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
paint.setStrokeWidth(stroke_width);
|
||||||
|
paint.setStrokeCap(Paint.Cap.ROUND);
|
||||||
|
paint.setStrokeJoin(Paint.Join.ROUND);
|
||||||
|
paint.setTextSize(40);
|
||||||
|
map.paint();
|
||||||
|
draw_positions();
|
||||||
|
canvas = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onScale(ScaleGestureDetector detector) {
|
||||||
|
float f = detector.getScaleFactor();
|
||||||
|
|
||||||
|
if (f <= 0.8) {
|
||||||
|
map.set_zoom_centre(map.get_zoom() - 1, new AltosPointInt((int) detector.getFocusX(), (int) detector.getFocusY()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (f >= 1.2) {
|
||||||
|
map.set_zoom_centre(map.get_zoom() + 1, new AltosPointInt((int) detector.getFocusX(), (int) detector.getFocusY()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onScaleBegin(ScaleGestureDetector detector) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onScaleEnd(ScaleGestureDetector detector) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean dispatchTouchEvent(MotionEvent event) {
|
||||||
|
scale_detector.onTouchEvent(event);
|
||||||
|
|
||||||
|
if (scale_detector.isInProgress()) {
|
||||||
|
scaling = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scaling) {
|
||||||
|
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||||
|
scaling = false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||||
|
map.touch_start((int) event.getX(), (int) event.getY(), true);
|
||||||
|
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
|
||||||
|
map.touch_continue((int) event.getX(), (int) event.getY(), true);
|
||||||
|
} else if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||||
|
map.touch_stop((int) event.getX(), (int) event.getY(), true);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
double mapAccuracy;
|
||||||
|
|
||||||
|
public void center(double lat, double lon, double accuracy) {
|
||||||
|
if (mapAccuracy <= 0 || accuracy < mapAccuracy/10 || (map != null && !map.has_centre())) {
|
||||||
|
if (map != null)
|
||||||
|
map.maybe_centre(lat, lon);
|
||||||
|
mapAccuracy = accuracy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set_visible(boolean visible) {
|
||||||
|
if (visible)
|
||||||
|
setVisibility(VISIBLE);
|
||||||
|
else
|
||||||
|
setVisibility(GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
|
||||||
|
boolean changed = false;
|
||||||
|
|
||||||
|
if (state != null) {
|
||||||
|
map.show(state, null);
|
||||||
|
if (state.pad_lat != AltosLib.MISSING && pad == null)
|
||||||
|
pad = new AltosLatLon(state.pad_lat, state.pad_lon);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (telem_state != null) {
|
||||||
|
Integer[] old_serial = rockets.keySet().toArray(new Integer[0]);
|
||||||
|
Integer[] new_serial = telem_state.keySet().toArray(new Integer[0]);
|
||||||
|
|
||||||
|
/* remove deleted keys */
|
||||||
|
for (int serial : old_serial) {
|
||||||
|
if (!telem_state.containsKey(serial))
|
||||||
|
rockets.remove(serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set remaining keys */
|
||||||
|
|
||||||
|
for (int serial : new_serial) {
|
||||||
|
Rocket rocket;
|
||||||
|
AltosState t_state = telem_state.get(serial);
|
||||||
|
if (rockets.containsKey(serial))
|
||||||
|
rocket = rockets.get(serial);
|
||||||
|
else {
|
||||||
|
rocket = new Rocket(serial, this);
|
||||||
|
rockets.put(serial, rocket);
|
||||||
|
}
|
||||||
|
if (t_state.gps != null) {
|
||||||
|
AltosLatLon latlon = new AltosLatLon(t_state.gps.lat, t_state.gps.lon);
|
||||||
|
rocket.set_position(latlon, t_state.received_time);
|
||||||
|
if (state != null && state.cal_data().serial == serial)
|
||||||
|
there = latlon;
|
||||||
|
}
|
||||||
|
if (state != null)
|
||||||
|
rocket.set_active(state.cal_data().serial == serial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (receiver != null) {
|
||||||
|
AltosLatLon new_here = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude());
|
||||||
|
if (!new_here.equals(here)) {
|
||||||
|
here = new_here;
|
||||||
|
AltosDebug.debug("Location changed, redraw");
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCreateView(AltosDroid altos_droid) {
|
||||||
|
this.altos_droid = altos_droid;
|
||||||
|
map = new AltosMap(this, scale);
|
||||||
|
AltosPreferences.register_map_type_listener(this);
|
||||||
|
map.set_maptype(AltosPreferences.map_type());
|
||||||
|
|
||||||
|
pad_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pad);
|
||||||
|
/* arrow at the bottom of the launchpad image */
|
||||||
|
pad_off_x = pad_bitmap.getWidth() / 2;
|
||||||
|
pad_off_y = pad_bitmap.getHeight();
|
||||||
|
|
||||||
|
rocket_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.rocket);
|
||||||
|
/* arrow at the bottom of the rocket image */
|
||||||
|
rocket_off_x = rocket_bitmap.getWidth() / 2;
|
||||||
|
rocket_off_y = rocket_bitmap.getHeight();
|
||||||
|
|
||||||
|
here_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_maps_indicator_current_position);
|
||||||
|
/* Center of the dot */
|
||||||
|
here_off_x = here_bitmap.getWidth() / 2;
|
||||||
|
here_off_y = here_bitmap.getHeight() / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDestroyView() {
|
||||||
|
AltosPreferences.unregister_map_type_listener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void map_type_changed(int map_type) {
|
||||||
|
if (map != null)
|
||||||
|
map.set_maptype(map_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AltosMapOffline(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
this.altos_droid = altos_droid;
|
||||||
|
scale_detector = new ScaleGestureDetector(context, this);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,358 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
import com.google.android.gms.maps.*;
|
||||||
|
import com.google.android.gms.maps.model.*;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.*;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.content.*;
|
||||||
|
|
||||||
|
class RocketOnline implements Comparable {
|
||||||
|
Marker marker;
|
||||||
|
int serial;
|
||||||
|
long last_packet;
|
||||||
|
int size;
|
||||||
|
|
||||||
|
void set_position(AltosLatLon position, long last_packet) {
|
||||||
|
marker.setPosition(new LatLng(position.lat, position.lon));
|
||||||
|
this.last_packet = last_packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bitmap rocket_bitmap(Context context, String text) {
|
||||||
|
|
||||||
|
/* From: http://mapicons.nicolasmollet.com/markers/industry/military/missile-2/
|
||||||
|
*/
|
||||||
|
Bitmap orig_bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.rocket);
|
||||||
|
Bitmap bitmap = orig_bitmap.copy(Bitmap.Config.ARGB_8888, true);
|
||||||
|
|
||||||
|
Canvas canvas = new Canvas(bitmap);
|
||||||
|
Paint paint = new Paint();
|
||||||
|
paint.setTextSize(40);
|
||||||
|
paint.setColor(0xff000000);
|
||||||
|
|
||||||
|
Rect bounds = new Rect();
|
||||||
|
paint.getTextBounds(text, 0, text.length(), bounds);
|
||||||
|
|
||||||
|
int width = bounds.right - bounds.left;
|
||||||
|
int height = bounds.bottom - bounds.top;
|
||||||
|
|
||||||
|
float x = bitmap.getWidth() / 2.0f - width / 2.0f;
|
||||||
|
float y = bitmap.getHeight() / 2.0f - height / 2.0f;
|
||||||
|
|
||||||
|
size = bitmap.getWidth();
|
||||||
|
|
||||||
|
canvas.drawText(text, 0, text.length(), x, y, paint);
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove() {
|
||||||
|
marker.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compareTo(Object o) {
|
||||||
|
RocketOnline other = (RocketOnline) o;
|
||||||
|
|
||||||
|
long diff = last_packet - other.last_packet;
|
||||||
|
|
||||||
|
if (diff > 0)
|
||||||
|
return 1;
|
||||||
|
if (diff < 0)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
RocketOnline(Context context, int serial, GoogleMap map, double lat, double lon, long last_packet) {
|
||||||
|
this.serial = serial;
|
||||||
|
String name = String.format("%d", serial);
|
||||||
|
this.marker = map.addMarker(new MarkerOptions()
|
||||||
|
.icon(BitmapDescriptorFactory.fromBitmap(rocket_bitmap(context, name)))
|
||||||
|
.position(new LatLng(lat, lon))
|
||||||
|
.visible(true));
|
||||||
|
this.last_packet = last_packet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarkerClickListener, GoogleMap.OnMapClickListener, OnMapReadyCallback, AltosMapTypeListener {
|
||||||
|
public AltosOnlineMapFragment mMapFragment;
|
||||||
|
private GoogleMap mMap;
|
||||||
|
private boolean mapLoaded = false;
|
||||||
|
Context context;
|
||||||
|
|
||||||
|
private HashMap<Integer,RocketOnline> rockets = new HashMap<Integer,RocketOnline>();
|
||||||
|
private Marker mPadMarker;
|
||||||
|
private boolean pad_set;
|
||||||
|
private Polyline mPolyline;
|
||||||
|
|
||||||
|
private double mapAccuracy = -1;
|
||||||
|
|
||||||
|
private AltosLatLon my_position = null;
|
||||||
|
private AltosLatLon target_position = null;
|
||||||
|
|
||||||
|
private AltosDroid altos_droid;
|
||||||
|
|
||||||
|
public static class AltosOnlineMapFragment extends SupportMapFragment {
|
||||||
|
AltosMapOnline c;
|
||||||
|
View map_view;
|
||||||
|
|
||||||
|
public AltosOnlineMapFragment(AltosMapOnline c) {
|
||||||
|
this.c = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AltosOnlineMapFragment() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityCreated(Bundle savedInstanceState) {
|
||||||
|
super.onActivityCreated(savedInstanceState);
|
||||||
|
if (c != null)
|
||||||
|
getMapAsync(c);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
map_view = super.onCreateView(inflater, container, savedInstanceState);
|
||||||
|
return map_view;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
map_view = null;
|
||||||
|
}
|
||||||
|
public void set_visible(boolean visible) {
|
||||||
|
if (map_view == null)
|
||||||
|
return;
|
||||||
|
if (visible)
|
||||||
|
map_view.setVisibility(View.VISIBLE);
|
||||||
|
else
|
||||||
|
map_view.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCreateView(AltosDroid altos_droid) {
|
||||||
|
this.altos_droid = altos_droid;
|
||||||
|
AltosPreferences.register_map_type_listener(this);
|
||||||
|
mMapFragment = new AltosOnlineMapFragment(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDestroyView() {
|
||||||
|
AltosPreferences.unregister_map_type_listener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double pixel_distance(LatLng a, LatLng b) {
|
||||||
|
Projection projection = mMap.getProjection();
|
||||||
|
|
||||||
|
Point a_pt = projection.toScreenLocation(a);
|
||||||
|
Point b_pt = projection.toScreenLocation(b);
|
||||||
|
|
||||||
|
return Math.hypot((double) (a_pt.x - b_pt.x), (double) (a_pt.y - b_pt.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
private RocketOnline[] sorted_rockets() {
|
||||||
|
synchronized(rockets) {
|
||||||
|
RocketOnline[] rocket_array = rockets.values().toArray(new RocketOnline[0]);
|
||||||
|
|
||||||
|
Arrays.sort(rocket_array);
|
||||||
|
return rocket_array;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onMapClick(LatLng lat_lng) {
|
||||||
|
ArrayList<Integer> near = new ArrayList<Integer>();
|
||||||
|
|
||||||
|
for (RocketOnline rocket : sorted_rockets()) {
|
||||||
|
LatLng pos = rocket.marker.getPosition();
|
||||||
|
|
||||||
|
if (pos == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
double distance = pixel_distance(lat_lng, pos);
|
||||||
|
if (distance < rocket.size * 2)
|
||||||
|
near.add(rocket.serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (near.size() != 0)
|
||||||
|
altos_droid.touch_trackers(near.toArray(new Integer[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onMarkerClick(Marker marker) {
|
||||||
|
onMapClick(marker.getPosition());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
position_permission() {
|
||||||
|
if (mMap != null)
|
||||||
|
mMap.setMyLocationEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMapReady(GoogleMap googleMap) {
|
||||||
|
final int map_type = AltosPreferences.map_type();
|
||||||
|
mMap = googleMap;
|
||||||
|
if (mMap != null) {
|
||||||
|
map_type_changed(map_type);
|
||||||
|
if (altos_droid.have_location_permission)
|
||||||
|
mMap.setMyLocationEnabled(true);
|
||||||
|
else
|
||||||
|
altos_droid.tell_map_permission(this);
|
||||||
|
mMap.getUiSettings().setTiltGesturesEnabled(false);
|
||||||
|
mMap.getUiSettings().setZoomControlsEnabled(false);
|
||||||
|
mMap.setOnMarkerClickListener(this);
|
||||||
|
mMap.setOnMapClickListener(this);
|
||||||
|
|
||||||
|
mPadMarker = mMap.addMarker(
|
||||||
|
new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.pad))
|
||||||
|
.position(new LatLng(0,0))
|
||||||
|
.visible(false)
|
||||||
|
);
|
||||||
|
|
||||||
|
mPolyline = mMap.addPolyline(
|
||||||
|
new PolylineOptions().add(new LatLng(0,0), new LatLng(0,0))
|
||||||
|
.width(20)
|
||||||
|
.color(Color.BLUE)
|
||||||
|
.visible(false)
|
||||||
|
);
|
||||||
|
|
||||||
|
mapLoaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void center(double lat, double lon, double accuracy) {
|
||||||
|
if (mMap == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (mapAccuracy < 0 || accuracy < mapAccuracy/10) {
|
||||||
|
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lat, lon),14));
|
||||||
|
mapAccuracy = accuracy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void set_rocket(int serial, AltosState state) {
|
||||||
|
RocketOnline rocket;
|
||||||
|
|
||||||
|
if (state.gps == null || state.gps.lat == AltosLib.MISSING)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (mMap == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
synchronized(rockets) {
|
||||||
|
if (rockets.containsKey(serial)) {
|
||||||
|
rocket = rockets.get(serial);
|
||||||
|
rocket.set_position(new AltosLatLon(state.gps.lat, state.gps.lon), state.received_time);
|
||||||
|
} else {
|
||||||
|
rocket = new RocketOnline(context,
|
||||||
|
serial,
|
||||||
|
mMap, state.gps.lat, state.gps.lon,
|
||||||
|
state.received_time);
|
||||||
|
rockets.put(serial, rocket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void remove_rocket(int serial) {
|
||||||
|
synchronized(rockets) {
|
||||||
|
RocketOnline rocket = rockets.get(serial);
|
||||||
|
rocket.remove();
|
||||||
|
rockets.remove(serial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set_visible(boolean visible) {
|
||||||
|
if (mMapFragment != null)
|
||||||
|
mMapFragment.set_visible(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
|
||||||
|
|
||||||
|
if (telem_state != null) {
|
||||||
|
synchronized(rockets) {
|
||||||
|
for (int serial : rockets.keySet()) {
|
||||||
|
if (!telem_state.containsKey(serial))
|
||||||
|
remove_rocket(serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int serial : telem_state.keySet()) {
|
||||||
|
set_rocket(serial, telem_state.get(serial));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state != null) {
|
||||||
|
if (mapLoaded) {
|
||||||
|
if (!pad_set && state.pad_lat != AltosLib.MISSING) {
|
||||||
|
pad_set = true;
|
||||||
|
mPadMarker.setPosition(new LatLng(state.pad_lat, state.pad_lon));
|
||||||
|
mPadMarker.setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state.gps != null && state.gps.lat != AltosLib.MISSING) {
|
||||||
|
|
||||||
|
target_position = new AltosLatLon(state.gps.lat, state.gps.lon);
|
||||||
|
if (state.gps.locked && state.gps.nsat >= 4)
|
||||||
|
center (state.gps.lat, state.gps.lon, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receiver != null) {
|
||||||
|
double accuracy;
|
||||||
|
|
||||||
|
if (receiver.hasAccuracy())
|
||||||
|
accuracy = receiver.getAccuracy();
|
||||||
|
else
|
||||||
|
accuracy = 1000;
|
||||||
|
|
||||||
|
my_position = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude());
|
||||||
|
center (my_position.lat, my_position.lon, accuracy);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (my_position != null && target_position != null && mPolyline != null) {
|
||||||
|
mPolyline.setPoints(Arrays.asList(new LatLng(my_position.lat, my_position.lon), new LatLng(target_position.lat, target_position.lon)));
|
||||||
|
mPolyline.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void map_type_changed(int map_type) {
|
||||||
|
if (mMap != null) {
|
||||||
|
if (map_type == AltosMap.maptype_hybrid)
|
||||||
|
mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
|
||||||
|
else if (map_type == AltosMap.maptype_satellite)
|
||||||
|
mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
|
||||||
|
else if (map_type == AltosMap.maptype_terrain)
|
||||||
|
mMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
|
||||||
|
else
|
||||||
|
mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AltosMapOnline(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,233 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2015 Keith Packard <keithp@keithp.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.hardware.usb.*;
|
||||||
|
import android.app.*;
|
||||||
|
import android.os.Handler;
|
||||||
|
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
public class AltosUsb extends AltosDroidLink {
|
||||||
|
|
||||||
|
private Thread input_thread = null;
|
||||||
|
|
||||||
|
private Handler handler;
|
||||||
|
|
||||||
|
private UsbManager manager;
|
||||||
|
private UsbDevice device;
|
||||||
|
private UsbDeviceConnection connection;
|
||||||
|
private UsbInterface iface;
|
||||||
|
private UsbEndpoint in, out;
|
||||||
|
|
||||||
|
private InputStream input;
|
||||||
|
private OutputStream output;
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
public AltosUsb(Context context, UsbDevice device, Handler handler) {
|
||||||
|
super(handler);
|
||||||
|
// set_debug(D);
|
||||||
|
this.handler = handler;
|
||||||
|
|
||||||
|
iface = null;
|
||||||
|
in = null;
|
||||||
|
out = null;
|
||||||
|
|
||||||
|
int niface = device.getInterfaceCount();
|
||||||
|
|
||||||
|
for (int i = 0; i < niface; i++) {
|
||||||
|
|
||||||
|
iface = device.getInterface(i);
|
||||||
|
|
||||||
|
in = null;
|
||||||
|
out = null;
|
||||||
|
|
||||||
|
int nendpoints = iface.getEndpointCount();
|
||||||
|
|
||||||
|
for (int e = 0; e < nendpoints; e++) {
|
||||||
|
UsbEndpoint endpoint = iface.getEndpoint(e);
|
||||||
|
|
||||||
|
if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
|
||||||
|
switch (endpoint.getDirection()) {
|
||||||
|
case UsbConstants.USB_DIR_OUT:
|
||||||
|
out = endpoint;
|
||||||
|
break;
|
||||||
|
case UsbConstants.USB_DIR_IN:
|
||||||
|
in = endpoint;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in != null && out != null)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in != null && out != null) {
|
||||||
|
AltosDebug.debug("\tin %s out %s\n", in.toString(), out.toString());
|
||||||
|
|
||||||
|
manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
|
||||||
|
|
||||||
|
if (manager == null) {
|
||||||
|
AltosDebug.debug("USB_SERVICE failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
connection = manager.openDevice(device);
|
||||||
|
|
||||||
|
if (connection == null) {
|
||||||
|
AltosDebug.debug("openDevice failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.claimInterface(iface, true);
|
||||||
|
|
||||||
|
input_thread = new Thread(this);
|
||||||
|
input_thread.start();
|
||||||
|
|
||||||
|
// Configure the newly connected device for telemetry
|
||||||
|
print("~\nE 0\n");
|
||||||
|
set_monitor(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static private boolean isAltusMetrum(UsbDevice device) {
|
||||||
|
if (device.getVendorId() != AltosLib.vendor_altusmetrum)
|
||||||
|
return false;
|
||||||
|
if (device.getProductId() < AltosLib.product_altusmetrum_min)
|
||||||
|
return false;
|
||||||
|
if (device.getProductId() > AltosLib.product_altusmetrum_max)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean matchProduct(int want_product, UsbDevice device) {
|
||||||
|
|
||||||
|
if (!isAltusMetrum(device))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (want_product == AltosLib.product_any)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
int have_product = device.getProductId();
|
||||||
|
|
||||||
|
if (want_product == AltosLib.product_basestation)
|
||||||
|
return have_product == AltosLib.product_teledongle ||
|
||||||
|
have_product == AltosLib.product_telebt ||
|
||||||
|
have_product == AltosLib.product_megadongle;
|
||||||
|
|
||||||
|
if (want_product == AltosLib.product_altimeter)
|
||||||
|
return have_product == AltosLib.product_telemetrum ||
|
||||||
|
have_product == AltosLib.product_telemega ||
|
||||||
|
have_product == AltosLib.product_easymega ||
|
||||||
|
have_product == AltosLib.product_telegps ||
|
||||||
|
have_product == AltosLib.product_easymini ||
|
||||||
|
have_product == AltosLib.product_telemini ||
|
||||||
|
have_product == AltosLib.product_easytimer;
|
||||||
|
|
||||||
|
if (have_product == AltosLib.product_altusmetrum) /* old devices match any request */
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (want_product == have_product)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public boolean request_permission(Context context, UsbDevice device, PendingIntent pi) {
|
||||||
|
UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
|
||||||
|
|
||||||
|
// if (manager.hasPermission(device))
|
||||||
|
// return true;
|
||||||
|
|
||||||
|
AltosDebug.debug("request permission for USB device " + device.toString());
|
||||||
|
|
||||||
|
manager.requestPermission(device, pi);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public UsbDevice find_device(Context context, int match_product) {
|
||||||
|
UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
|
||||||
|
|
||||||
|
HashMap<String,UsbDevice> devices = manager.getDeviceList();
|
||||||
|
|
||||||
|
for (UsbDevice device : devices.values()) {
|
||||||
|
int vendor = device.getVendorId();
|
||||||
|
int product = device.getProductId();
|
||||||
|
|
||||||
|
if (matchProduct(match_product, device)) {
|
||||||
|
AltosDebug.debug("found USB device " + device.toString());
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disconnected() {
|
||||||
|
if (closed()) {
|
||||||
|
AltosDebug.debug("disconnected after closed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AltosDebug.debug("Sending disconnected message");
|
||||||
|
handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, this).sendToTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void close_device() {
|
||||||
|
UsbDeviceConnection tmp_connection;
|
||||||
|
|
||||||
|
synchronized(this) {
|
||||||
|
tmp_connection = connection;
|
||||||
|
connection = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmp_connection != null) {
|
||||||
|
AltosDebug.debug("Closing USB device");
|
||||||
|
tmp_connection.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int read(byte[] buffer, int len) {
|
||||||
|
if (connection == null)
|
||||||
|
return 0;
|
||||||
|
int ret = connection.bulkTransfer(in, buffer, len, -1);
|
||||||
|
AltosDebug.debug("read(%d) = %d\n", len, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int write(byte[] buffer, int len) {
|
||||||
|
if (connection == null)
|
||||||
|
return 0;
|
||||||
|
int ret = connection.bulkTransfer(out, buffer, len, -1);
|
||||||
|
AltosDebug.debug("write(%d) = %d\n", len, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stubs of required methods when extending AltosLink
|
||||||
|
public boolean can_cancel_reply() { return false; }
|
||||||
|
public boolean show_reply_timeout() { return true; }
|
||||||
|
public void hide_reply_timeout() { }
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import androidx.viewpager.widget.ViewPager;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
public class AltosViewPager extends ViewPager {
|
||||||
|
|
||||||
|
public AltosViewPager(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AltosViewPager(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
|
||||||
|
|
||||||
|
if (v.getClass() != null &&
|
||||||
|
v.getClass().getName() != null &&
|
||||||
|
v.getClass().getName().endsWith("MapOffline"))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if(v.getClass() != null &&
|
||||||
|
v.getClass().getPackage() != null &&
|
||||||
|
v.getClass().getPackage().getName() != null &&
|
||||||
|
v.getClass().getPackage().getName().startsWith("maps."))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return super.canScroll(v, checkV, dx, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,329 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2011 Keith Packard <keithp@keithp.com>
|
||||||
|
* Copyright © 2012 Mike Beattie <mike@ethernal.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import android.speech.tts.TextToSpeech;
|
||||||
|
import android.speech.tts.TextToSpeech.OnInitListener;
|
||||||
|
import android.location.Location;
|
||||||
|
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
public class AltosVoice {
|
||||||
|
|
||||||
|
private TextToSpeech tts = null;
|
||||||
|
private boolean tts_enabled = false;
|
||||||
|
|
||||||
|
static final int TELL_MODE_NONE = 0;
|
||||||
|
static final int TELL_MODE_PAD = 1;
|
||||||
|
static final int TELL_MODE_FLIGHT = 2;
|
||||||
|
static final int TELL_MODE_RECOVER = 3;
|
||||||
|
|
||||||
|
static final int TELL_FLIGHT_NONE = 0;
|
||||||
|
static final int TELL_FLIGHT_STATE = 1;
|
||||||
|
static final int TELL_FLIGHT_SPEED = 2;
|
||||||
|
static final int TELL_FLIGHT_HEIGHT = 3;
|
||||||
|
static final int TELL_FLIGHT_TRACK = 4;
|
||||||
|
|
||||||
|
private int last_tell_mode;
|
||||||
|
private int last_tell_serial = AltosLib.MISSING;
|
||||||
|
private int last_state;
|
||||||
|
private AltosGPS last_gps;
|
||||||
|
private double last_height = AltosLib.MISSING;
|
||||||
|
private Location last_receiver;
|
||||||
|
private long last_speak_time;
|
||||||
|
private int last_flight_tell = TELL_FLIGHT_NONE;
|
||||||
|
private boolean quiet = false;
|
||||||
|
|
||||||
|
private long now() {
|
||||||
|
return System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reset_last() {
|
||||||
|
last_tell_mode = TELL_MODE_NONE;
|
||||||
|
last_speak_time = now() - 100 * 1000;
|
||||||
|
last_gps = null;
|
||||||
|
last_height = AltosLib.MISSING;
|
||||||
|
last_receiver = null;
|
||||||
|
last_state = AltosLib.ao_flight_invalid;
|
||||||
|
last_flight_tell = TELL_FLIGHT_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AltosVoice(AltosDroid a) {
|
||||||
|
tts = new TextToSpeech(a, new OnInitListener() {
|
||||||
|
public void onInit(int status) {
|
||||||
|
if (status == TextToSpeech.SUCCESS) tts_enabled = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
reset_last();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void set_enable(boolean enable) {
|
||||||
|
tts_enabled = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void speak(String s) {
|
||||||
|
if (!tts_enabled) return;
|
||||||
|
last_speak_time = now();
|
||||||
|
if (!quiet)
|
||||||
|
tts.speak(s, TextToSpeech.QUEUE_ADD, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized long time_since_speak() {
|
||||||
|
return now() - last_speak_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void speak(String format, Object ... arguments) {
|
||||||
|
speak(String.format(format, arguments));
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean is_speaking() {
|
||||||
|
return tts.isSpeaking();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
if (tts != null) {
|
||||||
|
tts.stop();
|
||||||
|
tts.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean last_apogee_good;
|
||||||
|
private boolean last_main_good;
|
||||||
|
private boolean last_gps_good;
|
||||||
|
|
||||||
|
private boolean tell_gonogo(String name,
|
||||||
|
boolean current,
|
||||||
|
boolean previous,
|
||||||
|
boolean new_mode) {
|
||||||
|
if (current != previous || new_mode)
|
||||||
|
speak("%s %s.", name, current ? "ready" : "not ready");
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tell_pad(TelemetryState telem_state, AltosState state,
|
||||||
|
AltosGreatCircle from_receiver, Location receiver) {
|
||||||
|
|
||||||
|
if (state == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (state.apogee_voltage != AltosLib.MISSING)
|
||||||
|
last_apogee_good = tell_gonogo("apogee",
|
||||||
|
state.apogee_voltage >= AltosLib.ao_igniter_good,
|
||||||
|
last_apogee_good,
|
||||||
|
last_tell_mode != TELL_MODE_PAD);
|
||||||
|
|
||||||
|
if (state.main_voltage != AltosLib.MISSING)
|
||||||
|
last_main_good = tell_gonogo("main",
|
||||||
|
state.main_voltage >= AltosLib.ao_igniter_good,
|
||||||
|
last_main_good,
|
||||||
|
last_tell_mode != TELL_MODE_PAD);
|
||||||
|
|
||||||
|
if (state.gps != null)
|
||||||
|
last_gps_good = tell_gonogo("G P S",
|
||||||
|
state.gps_ready,
|
||||||
|
last_gps_good,
|
||||||
|
last_tell_mode != TELL_MODE_PAD);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean descending(int state) {
|
||||||
|
return AltosLib.ao_flight_drogue <= state && state <= AltosLib.ao_flight_landed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean target_moved(AltosState state) {
|
||||||
|
if (last_gps != null && state != null && state.gps != null) {
|
||||||
|
AltosGreatCircle moved = new AltosGreatCircle(last_gps.lat, last_gps.lon, last_gps.alt,
|
||||||
|
state.gps.lat, state.gps.lon, state.gps.alt);
|
||||||
|
double height_change = 0;
|
||||||
|
double height = state.height();
|
||||||
|
|
||||||
|
if (height != AltosLib.MISSING && last_height != AltosLib.MISSING)
|
||||||
|
height_change = Math.abs(last_height - height);
|
||||||
|
|
||||||
|
if (moved.range < 10 && height_change < 10)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean receiver_moved(Location receiver) {
|
||||||
|
if (last_receiver != null && receiver != null) {
|
||||||
|
AltosGreatCircle moved = new AltosGreatCircle(last_receiver.getLatitude(),
|
||||||
|
last_receiver.getLongitude(),
|
||||||
|
last_receiver.getAltitude(),
|
||||||
|
receiver.getLatitude(),
|
||||||
|
receiver.getLongitude(),
|
||||||
|
receiver.getAltitude());
|
||||||
|
if (moved.range < 10)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tell_flight(TelemetryState telem_state, AltosState state,
|
||||||
|
AltosGreatCircle from_receiver, Location receiver) {
|
||||||
|
|
||||||
|
boolean spoken = false;
|
||||||
|
|
||||||
|
if (state == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (last_tell_mode != TELL_MODE_FLIGHT)
|
||||||
|
last_flight_tell = TELL_FLIGHT_NONE;
|
||||||
|
|
||||||
|
if (state.state() != last_state && AltosLib.ao_flight_boost <= state.state() && state.state() <= AltosLib.ao_flight_landed) {
|
||||||
|
speak(state.state_name());
|
||||||
|
if (descending(state.state()) && !descending(last_state)) {
|
||||||
|
if (state.max_height() != AltosLib.MISSING) {
|
||||||
|
speak("max height: %s.",
|
||||||
|
AltosConvert.height.say_units(state.max_height()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last_flight_tell = TELL_FLIGHT_STATE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_tell_mode == TELL_MODE_FLIGHT && last_flight_tell == TELL_FLIGHT_TRACK) {
|
||||||
|
if (time_since_speak() < 10 * 1000)
|
||||||
|
return false;
|
||||||
|
if (!target_moved(state) && !receiver_moved(receiver))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
double speed;
|
||||||
|
double height;
|
||||||
|
|
||||||
|
if (last_flight_tell == TELL_FLIGHT_NONE || last_flight_tell == TELL_FLIGHT_STATE || last_flight_tell == TELL_FLIGHT_TRACK) {
|
||||||
|
last_flight_tell = TELL_FLIGHT_SPEED;
|
||||||
|
|
||||||
|
if (state.state() <= AltosLib.ao_flight_coast) {
|
||||||
|
speed = state.speed();
|
||||||
|
} else {
|
||||||
|
speed = state.gps_speed();
|
||||||
|
if (speed == AltosLib.MISSING)
|
||||||
|
speed = state.speed();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (speed != AltosLib.MISSING) {
|
||||||
|
speak("speed: %s.", AltosConvert.speed.say_units(speed));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_flight_tell == TELL_FLIGHT_SPEED) {
|
||||||
|
last_flight_tell = TELL_FLIGHT_HEIGHT;
|
||||||
|
height = state.height();
|
||||||
|
|
||||||
|
if (height != AltosLib.MISSING) {
|
||||||
|
speak("height: %s.", AltosConvert.height.say_units(height));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_flight_tell == TELL_FLIGHT_HEIGHT) {
|
||||||
|
last_flight_tell = TELL_FLIGHT_TRACK;
|
||||||
|
if (from_receiver != null) {
|
||||||
|
speak("bearing %s %d, elevation %d, distance %s.",
|
||||||
|
from_receiver.bearing_words(
|
||||||
|
AltosGreatCircle.BEARING_VOICE),
|
||||||
|
(int) (from_receiver.bearing + 0.5),
|
||||||
|
(int) (from_receiver.elevation + 0.5),
|
||||||
|
AltosConvert.distance.say(from_receiver.distance));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return spoken;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tell_recover(TelemetryState telem_state, AltosState state,
|
||||||
|
AltosGreatCircle from_receiver, Location receiver) {
|
||||||
|
|
||||||
|
if (from_receiver == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (last_tell_mode == TELL_MODE_RECOVER) {
|
||||||
|
if (!target_moved(state) && !receiver_moved(receiver))
|
||||||
|
return false;
|
||||||
|
if (time_since_speak() <= 10 * 1000)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String direction = AltosDroid.direction(from_receiver, receiver);
|
||||||
|
if (direction == null)
|
||||||
|
direction = String.format("Bearing %d", (int) (from_receiver.bearing + 0.5));
|
||||||
|
|
||||||
|
speak("%s, distance %s.", direction,
|
||||||
|
AltosConvert.distance.say_units(from_receiver.distance));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tell(TelemetryState telem_state, AltosState state,
|
||||||
|
AltosGreatCircle from_receiver, Location receiver,
|
||||||
|
AltosDroidTab tab, boolean quiet) {
|
||||||
|
|
||||||
|
this.quiet = quiet;
|
||||||
|
|
||||||
|
boolean spoken = false;
|
||||||
|
|
||||||
|
if (!tts_enabled) return;
|
||||||
|
|
||||||
|
if (is_speaking()) return;
|
||||||
|
|
||||||
|
int tell_serial = last_tell_serial;
|
||||||
|
|
||||||
|
if (state != null)
|
||||||
|
tell_serial = state.cal_data().serial;
|
||||||
|
|
||||||
|
if (tell_serial != last_tell_serial)
|
||||||
|
reset_last();
|
||||||
|
|
||||||
|
int tell_mode = TELL_MODE_NONE;
|
||||||
|
|
||||||
|
if (tab.tab_name().equals(AltosDroid.tab_pad_name))
|
||||||
|
tell_mode = TELL_MODE_PAD;
|
||||||
|
else if (tab.tab_name().equals(AltosDroid.tab_flight_name))
|
||||||
|
tell_mode = TELL_MODE_FLIGHT;
|
||||||
|
else
|
||||||
|
tell_mode = TELL_MODE_RECOVER;
|
||||||
|
|
||||||
|
if (tell_mode == TELL_MODE_PAD)
|
||||||
|
spoken = tell_pad(telem_state, state, from_receiver, receiver);
|
||||||
|
else if (tell_mode == TELL_MODE_FLIGHT)
|
||||||
|
spoken = tell_flight(telem_state, state, from_receiver, receiver);
|
||||||
|
else
|
||||||
|
spoken = tell_recover(telem_state, state, from_receiver, receiver);
|
||||||
|
|
||||||
|
if (spoken) {
|
||||||
|
last_tell_mode = tell_mode;
|
||||||
|
last_tell_serial = tell_serial;
|
||||||
|
if (state != null) {
|
||||||
|
last_state = state.state();
|
||||||
|
last_height = state.height();
|
||||||
|
if (state.gps != null)
|
||||||
|
last_gps = state.gps;
|
||||||
|
}
|
||||||
|
if (receiver != null)
|
||||||
|
last_receiver = receiver;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2012 Mike Beattie <mike@ethernal.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
public class BuildInfo {
|
||||||
|
public static final String version = "@VERSION@";
|
||||||
|
public static final String git_describe = "@DESCRIBE@";
|
||||||
|
public static final String branch = "@BRANCH@";
|
||||||
|
public static final String commitnum = "@COMMITNUM@";
|
||||||
|
public static final String commithash = "@COMMITHASH@";
|
||||||
|
public static final String builddate = "@BUILDDATE@";
|
||||||
|
public static final String buildtime = "@BUILDTIME@";
|
||||||
|
public static final String buildtz = "@BUILDTZ@";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2015 Keith Packard <keithp@keithp.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
public class DeviceAddress {
|
||||||
|
public String address;
|
||||||
|
public String name;
|
||||||
|
|
||||||
|
public DeviceAddress(String address, String name) {
|
||||||
|
this.address = address;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,224 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.AdapterView.OnItemClickListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This Activity appears as a dialog. It lists any paired devices and
|
||||||
|
* devices detected in the area after discovery. When a device is chosen
|
||||||
|
* by the user, the MAC address of the device is sent back to the parent
|
||||||
|
* Activity in the result Intent.
|
||||||
|
*/
|
||||||
|
public class DeviceListActivity extends Activity {
|
||||||
|
|
||||||
|
// Return Intent extra
|
||||||
|
public static final String EXTRA_DEVICE_ADDRESS = "device_address";
|
||||||
|
public static final String EXTRA_DEVICE_NAME = "device_name";
|
||||||
|
|
||||||
|
// Member fields
|
||||||
|
private BluetoothAdapter mBtAdapter;
|
||||||
|
private ArrayAdapter<String> mPairedDevicesArrayAdapter;
|
||||||
|
private ArrayAdapter<String> mNewDevicesArrayAdapter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
setTheme(AltosDroid.dialog_themes[AltosDroidPreferences.font_size()]);
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// Setup the window
|
||||||
|
setContentView(R.layout.device_list);
|
||||||
|
|
||||||
|
// Set result CANCELED incase the user backs out
|
||||||
|
setResult(Activity.RESULT_CANCELED);
|
||||||
|
|
||||||
|
// Initialize the button to perform device discovery
|
||||||
|
Button scanButton = (Button) findViewById(R.id.button_scan);
|
||||||
|
scanButton.setOnClickListener(new OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
doDiscovery();
|
||||||
|
v.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize array adapters. One for already paired devices and
|
||||||
|
// one for newly discovered devices
|
||||||
|
mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
|
||||||
|
mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
|
||||||
|
|
||||||
|
// Find and set up the ListView for paired devices
|
||||||
|
ListView pairedListView = (ListView) findViewById(R.id.paired_devices);
|
||||||
|
pairedListView.setAdapter(mPairedDevicesArrayAdapter);
|
||||||
|
pairedListView.setOnItemClickListener(mDeviceClickListener);
|
||||||
|
|
||||||
|
// Find and set up the ListView for newly discovered devices
|
||||||
|
ListView newDevicesListView = (ListView) findViewById(R.id.new_devices);
|
||||||
|
newDevicesListView.setAdapter(mNewDevicesArrayAdapter);
|
||||||
|
newDevicesListView.setOnItemClickListener(mDeviceClickListener);
|
||||||
|
|
||||||
|
// Register for broadcasts when a device is discovered
|
||||||
|
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
|
||||||
|
this.registerReceiver(mReceiver, filter);
|
||||||
|
|
||||||
|
// Register for broadcasts when discovery has finished
|
||||||
|
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
|
||||||
|
this.registerReceiver(mReceiver, filter);
|
||||||
|
|
||||||
|
// Get the local Bluetooth adapter
|
||||||
|
mBtAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||||
|
|
||||||
|
// Get a set of currently paired devices
|
||||||
|
Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();
|
||||||
|
|
||||||
|
// If there are paired devices, add each one to the ArrayAdapter
|
||||||
|
if (pairedDevices.size() > 0) {
|
||||||
|
findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
|
||||||
|
for (BluetoothDevice device : pairedDevices) {
|
||||||
|
String name = device.getName();
|
||||||
|
if (name != null && name.startsWith("TeleBT"))
|
||||||
|
mPairedDevicesArrayAdapter.add(name + "\n" + device.getAddress());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String noDevices = getResources().getText(R.string.none_paired).toString();
|
||||||
|
mPairedDevicesArrayAdapter.add(noDevices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
|
||||||
|
// Make sure we're not doing discovery anymore
|
||||||
|
if (mBtAdapter != null) {
|
||||||
|
mBtAdapter.cancelDiscovery();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unregister broadcast listeners
|
||||||
|
this.unregisterReceiver(mReceiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start device discover with the BluetoothAdapter
|
||||||
|
*/
|
||||||
|
private void doDiscovery() {
|
||||||
|
AltosDebug.debug("doDiscovery()");
|
||||||
|
|
||||||
|
// Indicate scanning in the title
|
||||||
|
setTitle(R.string.scanning);
|
||||||
|
|
||||||
|
// Turn on sub-title for new devices
|
||||||
|
findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
// If we're already discovering, stop it
|
||||||
|
if (mBtAdapter.isDiscovering()) {
|
||||||
|
mBtAdapter.cancelDiscovery();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request discover from BluetoothAdapter
|
||||||
|
mBtAdapter.startDiscovery();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The on-click listener for all devices in the ListViews
|
||||||
|
private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {
|
||||||
|
public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
|
||||||
|
// Get the device MAC address, which is the last 17 chars in the View
|
||||||
|
String info = ((TextView) v).getText().toString();
|
||||||
|
|
||||||
|
/* Ignore clicks on items that are too short */
|
||||||
|
if (info.length() <= 17)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Cancel discovery because it's costly and we're about to connect
|
||||||
|
mBtAdapter.cancelDiscovery();
|
||||||
|
|
||||||
|
String address = info.substring(info.length() - 17);
|
||||||
|
|
||||||
|
int newline = info.indexOf('\n');
|
||||||
|
|
||||||
|
String name = null;
|
||||||
|
if (newline > 0)
|
||||||
|
name = info.substring(0, newline);
|
||||||
|
else
|
||||||
|
name = info;
|
||||||
|
|
||||||
|
AltosDebug.debug("******* selected item '%s'", info);
|
||||||
|
|
||||||
|
// Create the result Intent and include the MAC address
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.putExtra(EXTRA_DEVICE_ADDRESS, address);
|
||||||
|
intent.putExtra(EXTRA_DEVICE_NAME, name);
|
||||||
|
|
||||||
|
// Set result and finish this Activity
|
||||||
|
setResult(Activity.RESULT_OK, intent);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The BroadcastReceiver that listens for discovered devices and
|
||||||
|
// changes the title when discovery is finished
|
||||||
|
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
String action = intent.getAction();
|
||||||
|
|
||||||
|
// When discovery finds a device
|
||||||
|
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
|
||||||
|
|
||||||
|
/* Get the BluetoothDevice object from the Intent
|
||||||
|
*/
|
||||||
|
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
|
|
||||||
|
/* If it's already paired, skip it, because it's been listed already
|
||||||
|
*/
|
||||||
|
if (device != null && device.getBondState() != BluetoothDevice.BOND_BONDED)
|
||||||
|
{
|
||||||
|
String name = device.getName();
|
||||||
|
if (name != null && name.startsWith("TeleBT"))
|
||||||
|
mNewDevicesArrayAdapter.add(name + "\n" + device.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When discovery is finished, change the Activity title
|
||||||
|
*/
|
||||||
|
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
|
||||||
|
setTitle(R.string.select_device);
|
||||||
|
if (mNewDevicesArrayAdapter.getCount() == 0) {
|
||||||
|
String noDevices = getResources().getText(R.string.none_found).toString();
|
||||||
|
mNewDevicesArrayAdapter.add(noDevices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,183 @@
|
|||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class Dumper {
|
||||||
|
private static Dumper instance = new Dumper();
|
||||||
|
|
||||||
|
protected static Dumper getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DumpContext {
|
||||||
|
int maxDepth = 0;
|
||||||
|
int maxArrayElements = 0;
|
||||||
|
int callCount = 0;
|
||||||
|
HashMap<String, String> ignoreList = new HashMap<String, String>();
|
||||||
|
HashMap<Object, Integer> visited = new HashMap<Object, Integer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String dump(Object o) {
|
||||||
|
return dump(o, 0, 0, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String dump(Object o, int maxDepth, int maxArrayElements, String[] ignoreList) {
|
||||||
|
DumpContext ctx = Dumper.getInstance().new DumpContext();
|
||||||
|
ctx.maxDepth = maxDepth;
|
||||||
|
ctx.maxArrayElements = maxArrayElements;
|
||||||
|
|
||||||
|
if (ignoreList != null) {
|
||||||
|
for (int i = 0; i < Array.getLength(ignoreList); i++) {
|
||||||
|
int colonIdx = ignoreList[i].indexOf(':');
|
||||||
|
if (colonIdx == -1)
|
||||||
|
ignoreList[i] = ignoreList[i] + ":";
|
||||||
|
ctx.ignoreList.put(ignoreList[i], ignoreList[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dump(o, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static String dump(Object o, DumpContext ctx) {
|
||||||
|
if (o == null) {
|
||||||
|
return "<null>";
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.callCount++;
|
||||||
|
StringBuffer tabs = new StringBuffer();
|
||||||
|
for (int k = 0; k < ctx.callCount; k++) {
|
||||||
|
tabs.append("\t");
|
||||||
|
}
|
||||||
|
StringBuffer buffer = new StringBuffer();
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
Class oClass = o.getClass();
|
||||||
|
|
||||||
|
String oSimpleName = getSimpleNameWithoutArrayQualifier(oClass);
|
||||||
|
|
||||||
|
if (ctx.ignoreList.get(oSimpleName + ":") != null)
|
||||||
|
return "<Ignored>";
|
||||||
|
|
||||||
|
if (oClass.isArray()) {
|
||||||
|
buffer.append("\n");
|
||||||
|
buffer.append(tabs.toString().substring(1));
|
||||||
|
buffer.append("[\n");
|
||||||
|
int rowCount = ctx.maxArrayElements == 0 ? Array.getLength(o) : Math.min(ctx.maxArrayElements, Array.getLength(o));
|
||||||
|
for (int i = 0; i < rowCount; i++) {
|
||||||
|
buffer.append(tabs.toString());
|
||||||
|
try {
|
||||||
|
Object value = Array.get(o, i);
|
||||||
|
buffer.append(dumpValue(value, ctx));
|
||||||
|
} catch (Exception e) {
|
||||||
|
buffer.append(e.getMessage());
|
||||||
|
}
|
||||||
|
if (i < Array.getLength(o) - 1)
|
||||||
|
buffer.append(",");
|
||||||
|
buffer.append("\n");
|
||||||
|
}
|
||||||
|
if (rowCount < Array.getLength(o)) {
|
||||||
|
buffer.append(tabs.toString());
|
||||||
|
buffer.append(Array.getLength(o) - rowCount + " more array elements...");
|
||||||
|
buffer.append("\n");
|
||||||
|
}
|
||||||
|
buffer.append(tabs.toString().substring(1));
|
||||||
|
buffer.append("]");
|
||||||
|
} else {
|
||||||
|
buffer.append("\n");
|
||||||
|
buffer.append(tabs.toString().substring(1));
|
||||||
|
buffer.append("{\n");
|
||||||
|
buffer.append(tabs.toString());
|
||||||
|
buffer.append("hashCode: " + o.hashCode());
|
||||||
|
buffer.append("\n");
|
||||||
|
while (oClass != null && oClass != Object.class) {
|
||||||
|
Field[] fields = oClass.getDeclaredFields();
|
||||||
|
|
||||||
|
if (ctx.ignoreList.get(oClass.getSimpleName()) == null) {
|
||||||
|
if (oClass != o.getClass()) {
|
||||||
|
buffer.append(tabs.toString().substring(1));
|
||||||
|
buffer.append(" Inherited from superclass " + oSimpleName + ":\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < fields.length; i++) {
|
||||||
|
|
||||||
|
String fSimpleName = getSimpleNameWithoutArrayQualifier(fields[i].getType());
|
||||||
|
String fName = fields[i].getName();
|
||||||
|
|
||||||
|
fields[i].setAccessible(true);
|
||||||
|
buffer.append(tabs.toString());
|
||||||
|
buffer.append(fName + "(" + fSimpleName + ")");
|
||||||
|
buffer.append("=");
|
||||||
|
|
||||||
|
if (ctx.ignoreList.get(":" + fName) == null &&
|
||||||
|
ctx.ignoreList.get(fSimpleName + ":" + fName) == null &&
|
||||||
|
ctx.ignoreList.get(fSimpleName + ":") == null) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
Object value = fields[i].get(o);
|
||||||
|
buffer.append(dumpValue(value, ctx));
|
||||||
|
} catch (Exception e) {
|
||||||
|
buffer.append(e.getMessage());
|
||||||
|
}
|
||||||
|
buffer.append("\n");
|
||||||
|
} else {
|
||||||
|
buffer.append("<Ignored>");
|
||||||
|
buffer.append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oClass = oClass.getSuperclass();
|
||||||
|
oSimpleName = oClass.getSimpleName();
|
||||||
|
} else {
|
||||||
|
oClass = null;
|
||||||
|
oSimpleName = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.append(tabs.toString().substring(1));
|
||||||
|
buffer.append("}");
|
||||||
|
}
|
||||||
|
ctx.callCount--;
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static String dumpValue(Object value, DumpContext ctx) {
|
||||||
|
if (value == null) {
|
||||||
|
return "<null>";
|
||||||
|
}
|
||||||
|
if (value.getClass().isPrimitive() ||
|
||||||
|
value.getClass() == java.lang.Short.class ||
|
||||||
|
value.getClass() == java.lang.Long.class ||
|
||||||
|
value.getClass() == java.lang.String.class ||
|
||||||
|
value.getClass() == java.lang.Integer.class ||
|
||||||
|
value.getClass() == java.lang.Float.class ||
|
||||||
|
value.getClass() == java.lang.Byte.class ||
|
||||||
|
value.getClass() == java.lang.Character.class ||
|
||||||
|
value.getClass() == java.lang.Double.class ||
|
||||||
|
value.getClass() == java.lang.Boolean.class) {
|
||||||
|
|
||||||
|
return value.toString();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
Integer visitedIndex = ctx.visited.get(value);
|
||||||
|
if (visitedIndex == null) {
|
||||||
|
ctx.visited.put(value, ctx.callCount);
|
||||||
|
if (ctx.maxDepth == 0 || ctx.callCount < ctx.maxDepth) {
|
||||||
|
return dump(value, ctx);
|
||||||
|
} else {
|
||||||
|
return "<Reached max recursion depth>";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return "<Previously visited - see hashCode " + value.hashCode() + ">";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static String getSimpleNameWithoutArrayQualifier(@SuppressWarnings("rawtypes") Class clazz) {
|
||||||
|
String simpleName = clazz.getSimpleName();
|
||||||
|
int indexOfBracket = simpleName.indexOf('[');
|
||||||
|
if (indexOfBracket != -1)
|
||||||
|
return simpleName.substring(0, indexOfBracket);
|
||||||
|
return simpleName;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
public class GoNoGoLights {
|
||||||
|
private Boolean state;
|
||||||
|
private Boolean missing;
|
||||||
|
private Boolean set;
|
||||||
|
|
||||||
|
private ImageView red;
|
||||||
|
private ImageView green;
|
||||||
|
|
||||||
|
private Drawable dRed;
|
||||||
|
private Drawable dGreen;
|
||||||
|
private Drawable dGray;
|
||||||
|
|
||||||
|
public GoNoGoLights(ImageView in_red, ImageView in_green, Resources r) {
|
||||||
|
red = in_red;
|
||||||
|
green = in_green;
|
||||||
|
state = false;
|
||||||
|
missing = true;
|
||||||
|
set = false;
|
||||||
|
|
||||||
|
dRed = in_red.getContext().getDrawable(R.drawable.redled);
|
||||||
|
dGreen = in_red.getContext().getDrawable(R.drawable.greenled);
|
||||||
|
dGray = in_red.getContext().getDrawable(R.drawable.grayled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(Boolean s, Boolean m) {
|
||||||
|
if (set && s == state && m == missing) return;
|
||||||
|
state = s;
|
||||||
|
missing = m;
|
||||||
|
set = true;
|
||||||
|
if (missing) {
|
||||||
|
red.setImageDrawable(dGray);
|
||||||
|
green.setImageDrawable(dGray);
|
||||||
|
} else if (state) {
|
||||||
|
red.setImageDrawable(dGray);
|
||||||
|
green.setImageDrawable(dGreen);
|
||||||
|
} else {
|
||||||
|
red.setImageDrawable(dRed);
|
||||||
|
green.setImageDrawable(dGray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2016 Keith Packard <keithp@keithp.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
|
import android.widget.*;
|
||||||
|
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
public class IdleModeActivity extends Activity {
|
||||||
|
private EditText callsignText;
|
||||||
|
private TextView frequencyView;
|
||||||
|
private Button connect;
|
||||||
|
private Button disconnect;
|
||||||
|
private Button reboot;
|
||||||
|
private Button igniters;
|
||||||
|
private double frequency;
|
||||||
|
|
||||||
|
public static final String EXTRA_IDLE_MODE = "idle_mode";
|
||||||
|
public static final String EXTRA_IDLE_RESULT = "idle_result";
|
||||||
|
|
||||||
|
public static final int IDLE_MODE_CONNECT = 1;
|
||||||
|
public static final int IDLE_MODE_REBOOT = 2;
|
||||||
|
public static final int IDLE_MODE_IGNITERS = 3;
|
||||||
|
public static final int IDLE_MODE_DISCONNECT = 4;
|
||||||
|
|
||||||
|
private void done(int type) {
|
||||||
|
AltosPreferences.set_callsign(callsign());
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.putExtra(EXTRA_IDLE_RESULT, type);
|
||||||
|
setResult(Activity.RESULT_OK, intent);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String callsign() {
|
||||||
|
return callsignText.getEditableText().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void connect_idle() {
|
||||||
|
done(IDLE_MODE_CONNECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnect_idle() {
|
||||||
|
AltosDebug.debug("Disconnect idle button pressed");
|
||||||
|
done(IDLE_MODE_DISCONNECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reboot_idle() {
|
||||||
|
done(IDLE_MODE_REBOOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void igniters_idle() {
|
||||||
|
done(IDLE_MODE_IGNITERS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
setTheme(AltosDroid.dialog_themes[AltosDroidPreferences.font_size()]);
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// Setup the window
|
||||||
|
setContentView(R.layout.idle_mode);
|
||||||
|
|
||||||
|
callsignText = (EditText) findViewById(R.id.set_callsign);
|
||||||
|
callsignText.setText(new StringBuffer(AltosPreferences.callsign()));
|
||||||
|
|
||||||
|
frequency = getIntent().getDoubleExtra(AltosDroid.EXTRA_FREQUENCY, 0.0);
|
||||||
|
frequencyView = (TextView) findViewById(R.id.frequency);
|
||||||
|
frequencyView.setText(String.format("Frequency: %7.3f MHz", frequency));
|
||||||
|
|
||||||
|
connect = (Button) findViewById(R.id.connect_idle);
|
||||||
|
connect.setOnClickListener(new OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
connect_idle();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
disconnect = (Button) findViewById(R.id.disconnect_idle);
|
||||||
|
disconnect.setOnClickListener(new OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
disconnect_idle();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
boolean idle_mode = getIntent().getBooleanExtra(AltosDroid.EXTRA_IDLE_MODE, false);
|
||||||
|
|
||||||
|
if (idle_mode)
|
||||||
|
connect.setVisibility(View.GONE);
|
||||||
|
else
|
||||||
|
disconnect.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
reboot = (Button) findViewById(R.id.reboot_idle);
|
||||||
|
reboot.setOnClickListener(new OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
reboot_idle();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
igniters = (Button) findViewById(R.id.igniters_idle);
|
||||||
|
igniters.setOnClickListener(new OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
igniters_idle();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set result CANCELED incase the user backs out
|
||||||
|
setResult(Activity.RESULT_CANCELED);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,411 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2016 Keith Packard <keithp@keithp.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.*;
|
||||||
|
import android.graphics.*;
|
||||||
|
import android.os.*;
|
||||||
|
import android.view.*;
|
||||||
|
import android.view.View.*;
|
||||||
|
import android.widget.*;
|
||||||
|
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
class IgniterItem {
|
||||||
|
public String name;
|
||||||
|
public String pretty;
|
||||||
|
public String status;
|
||||||
|
public LinearLayout igniter_view = null;
|
||||||
|
public TextView pretty_view = null;
|
||||||
|
public TextView status_view = null;
|
||||||
|
|
||||||
|
private void update() {
|
||||||
|
if (pretty_view != null)
|
||||||
|
pretty_view.setText(pretty);
|
||||||
|
if (status_view != null)
|
||||||
|
status_view.setText(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(String name, String pretty, String status) {
|
||||||
|
if (!name.equals(this.name) ||
|
||||||
|
!pretty.equals(this.pretty) ||
|
||||||
|
!status.equals(this.status))
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
this.pretty = pretty;
|
||||||
|
this.status = status;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void realize(LinearLayout igniter_view,
|
||||||
|
TextView pretty_view,
|
||||||
|
TextView status_view) {
|
||||||
|
if (igniter_view != this.igniter_view ||
|
||||||
|
pretty_view != this.pretty_view ||
|
||||||
|
status_view != this.status_view)
|
||||||
|
{
|
||||||
|
this.igniter_view = igniter_view;
|
||||||
|
this.pretty_view = pretty_view;
|
||||||
|
this.status_view = status_view;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IgniterItem() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class IgniterAdapter extends ArrayAdapter<IgniterItem> {
|
||||||
|
int resource;
|
||||||
|
int selected_item = -1;
|
||||||
|
|
||||||
|
public IgniterAdapter(Context context, int in_resource) {
|
||||||
|
super(context, in_resource);
|
||||||
|
resource = in_resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
|
IgniterItem item = getItem(position);
|
||||||
|
if (item.igniter_view == null) {
|
||||||
|
LinearLayout igniter_view = new LinearLayout(getContext());
|
||||||
|
String inflater = Context.LAYOUT_INFLATER_SERVICE;
|
||||||
|
LayoutInflater li = (LayoutInflater) getContext().getSystemService(inflater);
|
||||||
|
li.inflate(resource, igniter_view, true);
|
||||||
|
|
||||||
|
item.realize(igniter_view,
|
||||||
|
(TextView) igniter_view.findViewById(R.id.igniter_name),
|
||||||
|
(TextView) igniter_view.findViewById(R.id.igniter_status));
|
||||||
|
}
|
||||||
|
if (position == selected_item)
|
||||||
|
item.igniter_view.setBackgroundColor(Color.RED);
|
||||||
|
else
|
||||||
|
item.igniter_view.setBackgroundColor(0);
|
||||||
|
return item.igniter_view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class IgniterActivity extends Activity {
|
||||||
|
private ListView igniters_view;
|
||||||
|
private ToggleButton arm;
|
||||||
|
private Button fire;
|
||||||
|
|
||||||
|
private HashMap<String,IgniterItem> igniters = new HashMap<String,IgniterItem>();;
|
||||||
|
|
||||||
|
private IgniterAdapter igniters_adapter;
|
||||||
|
|
||||||
|
private boolean is_bound;
|
||||||
|
private Messenger service = null;
|
||||||
|
private final Messenger messenger = new Messenger(new IncomingHandler(this));
|
||||||
|
|
||||||
|
private Timer query_timer;
|
||||||
|
private boolean query_timer_running;
|
||||||
|
|
||||||
|
private Timer arm_timer;
|
||||||
|
private int arm_remaining;
|
||||||
|
|
||||||
|
public static final int IGNITER_QUERY = 1;
|
||||||
|
public static final int IGNITER_FIRE = 2;
|
||||||
|
|
||||||
|
// The Handler that gets information back from the Telemetry Service
|
||||||
|
static class IncomingHandler extends Handler {
|
||||||
|
private final WeakReference<IgniterActivity> igniter_activity;
|
||||||
|
IncomingHandler(IgniterActivity ia) { igniter_activity = new WeakReference<IgniterActivity>(ia); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
IgniterActivity ia = igniter_activity.get();
|
||||||
|
|
||||||
|
switch (msg.what) {
|
||||||
|
case AltosDroid.MSG_IGNITER_STATUS:
|
||||||
|
@SuppressWarnings("unchecked") HashMap<String,Integer> map = (HashMap <String,Integer>) msg.obj;
|
||||||
|
ia.igniter_status(map);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
private ServiceConnection connection = new ServiceConnection() {
|
||||||
|
public void onServiceConnected(ComponentName className, IBinder binder) {
|
||||||
|
service = new Messenger(binder);
|
||||||
|
query_timer_tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onServiceDisconnected(ComponentName className) {
|
||||||
|
// This is called when the connection with the service has been unexpectedly disconnected - process crashed.
|
||||||
|
service = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void doBindService() {
|
||||||
|
bindService(new Intent(this, TelemetryService.class), connection, Context.BIND_AUTO_CREATE);
|
||||||
|
is_bound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void doUnbindService() {
|
||||||
|
if (is_bound) {
|
||||||
|
// If we have received the service, and hence registered with it, then now is the time to unregister.
|
||||||
|
unbindService(connection);
|
||||||
|
is_bound = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void done() {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
setResult(Activity.RESULT_OK, intent);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
class FireThread extends Thread {
|
||||||
|
private final String igniter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Message msg = Message.obtain(null, TelemetryService.MSG_IGNITER_FIRE, igniter);
|
||||||
|
try {
|
||||||
|
service.send(msg);
|
||||||
|
} catch (RemoteException re) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FireThread(String igniter) {
|
||||||
|
this.igniter = igniter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fire_igniter() {
|
||||||
|
if (igniters_adapter.selected_item >= 0) {
|
||||||
|
IgniterItem item = igniters_adapter.getItem(igniters_adapter.selected_item);
|
||||||
|
FireThread ft = new FireThread(item.name);
|
||||||
|
ft.run();
|
||||||
|
arm.setChecked(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void arm_igniter(boolean is_checked) {
|
||||||
|
if (is_checked) {
|
||||||
|
arm_timer_stop();
|
||||||
|
arm_timer = new Timer();
|
||||||
|
arm_remaining = 10;
|
||||||
|
arm_set_text();
|
||||||
|
fire.setEnabled(true);
|
||||||
|
arm_timer.scheduleAtFixedRate(new TimerTask() {
|
||||||
|
public void run() {
|
||||||
|
arm_timer_tick();
|
||||||
|
}},
|
||||||
|
1000L, 1000L);
|
||||||
|
} else {
|
||||||
|
arm_timer_stop();
|
||||||
|
fire.setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void query_timer_tick() {
|
||||||
|
if (query_timer_running)
|
||||||
|
return;
|
||||||
|
if (service == null)
|
||||||
|
return;
|
||||||
|
query_timer_running = true;
|
||||||
|
Thread thread = new Thread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
Message msg = Message.obtain(null, TelemetryService.MSG_IGNITER_QUERY);
|
||||||
|
msg.replyTo = messenger;
|
||||||
|
if (service == null) {
|
||||||
|
synchronized(IgniterActivity.this) {
|
||||||
|
query_timer_running = false;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
service.send(msg);
|
||||||
|
} catch (RemoteException re) {
|
||||||
|
AltosDebug.debug("igniter query thread failed");
|
||||||
|
synchronized(IgniterActivity.this) {
|
||||||
|
query_timer_running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean set_igniter(HashMap <String,Integer> status, String name, String pretty) {
|
||||||
|
if (!status.containsKey(name))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
IgniterItem item;
|
||||||
|
if (!igniters.containsKey(name)) {
|
||||||
|
item = new IgniterItem();
|
||||||
|
igniters.put(name, item);
|
||||||
|
igniters_adapter.add(item);
|
||||||
|
} else
|
||||||
|
item = igniters.get(name);
|
||||||
|
|
||||||
|
item.set(name, pretty, AltosIgnite.status_string(status.get(name)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void igniter_status(HashMap <String,Integer> status) {
|
||||||
|
query_timer_running = false;
|
||||||
|
if (status == null) {
|
||||||
|
AltosDebug.debug("no igniter status");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
set_igniter(status, "drogue", "Apogee");
|
||||||
|
set_igniter(status, "main", "Main");
|
||||||
|
for (int extra = 0;; extra++) {
|
||||||
|
String name = String.format("%d", extra);
|
||||||
|
String pretty = String.format("%c", 'A' + extra);
|
||||||
|
if (!set_igniter(status, name, pretty))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void arm_timer_stop() {
|
||||||
|
if (arm_timer != null) {
|
||||||
|
arm_timer.cancel();
|
||||||
|
arm_timer = null;
|
||||||
|
}
|
||||||
|
arm_remaining = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void arm_set_text() {
|
||||||
|
String text = String.format("Armed %d", arm_remaining);
|
||||||
|
|
||||||
|
if (arm.isChecked())
|
||||||
|
arm.setText(text);
|
||||||
|
arm.setTextOn(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void arm_timer_tick() {
|
||||||
|
--arm_remaining;
|
||||||
|
if (arm_remaining <= 0) {
|
||||||
|
arm_timer_stop();
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
arm.setChecked(false);
|
||||||
|
fire.setEnabled(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
arm_set_text();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void select_item(int position) {
|
||||||
|
if (position != igniters_adapter.selected_item) {
|
||||||
|
if (igniters_adapter.selected_item >= 0)
|
||||||
|
igniters_view.setItemChecked(igniters_adapter.selected_item, false);
|
||||||
|
if (position >= 0) {
|
||||||
|
igniters_view.setItemChecked(position, true);
|
||||||
|
arm.setEnabled(true);
|
||||||
|
} else
|
||||||
|
arm.setEnabled(false);
|
||||||
|
igniters_adapter.selected_item = position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class IgniterItemClickListener implements ListView.OnItemClickListener {
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> av, View v, int position, long id) {
|
||||||
|
AltosDebug.debug("select %d\n", position);
|
||||||
|
select_item(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
setTheme(AltosDroid.dialog_themes[AltosDroidPreferences.font_size()]);
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// Setup the window
|
||||||
|
setContentView(R.layout.igniters);
|
||||||
|
|
||||||
|
igniters_view = (ListView) findViewById(R.id.igniters);
|
||||||
|
igniters_view.setClickable(true);
|
||||||
|
|
||||||
|
igniters_adapter = new IgniterAdapter(this, R.layout.igniter_status);
|
||||||
|
|
||||||
|
igniters_view.setAdapter(igniters_adapter);
|
||||||
|
igniters_view.setOnItemClickListener(new IgniterItemClickListener());
|
||||||
|
|
||||||
|
fire = (Button) findViewById(R.id.igniter_fire);
|
||||||
|
fire.setEnabled(false);
|
||||||
|
fire.setOnClickListener(new OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
fire_igniter();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
arm = (ToggleButton) findViewById(R.id.igniter_arm);
|
||||||
|
arm.setEnabled(false);
|
||||||
|
arm.setOnCheckedChangeListener(new ToggleButton.OnCheckedChangeListener() {
|
||||||
|
public void onCheckedChanged(CompoundButton v, boolean is_checked) {
|
||||||
|
arm_igniter(is_checked);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set result CANCELED incase the user backs out
|
||||||
|
setResult(Activity.RESULT_CANCELED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
doBindService();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
query_timer = new Timer(true);
|
||||||
|
query_timer.scheduleAtFixedRate(new TimerTask() {
|
||||||
|
public void run() {
|
||||||
|
query_timer_tick();
|
||||||
|
}},
|
||||||
|
0L, 5000L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
if (query_timer != null) {
|
||||||
|
query_timer.cancel();
|
||||||
|
query_timer = null;
|
||||||
|
}
|
||||||
|
arm_timer_stop();
|
||||||
|
arm.setChecked(false);
|
||||||
|
fire.setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
doUnbindService();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,304 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2016 Keith Packard <keithp@keithp.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.text.*;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.*;
|
||||||
|
import android.graphics.*;
|
||||||
|
import android.os.*;
|
||||||
|
import android.view.*;
|
||||||
|
import android.view.View.*;
|
||||||
|
import android.view.inputmethod.*;
|
||||||
|
import android.widget.*;
|
||||||
|
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
class FrequencyItem {
|
||||||
|
public AltosFrequency frequency;
|
||||||
|
public LinearLayout frequency_view = null;
|
||||||
|
public TextView pretty_view = null;
|
||||||
|
|
||||||
|
private void update() {
|
||||||
|
if (pretty_view != null && frequency != null)
|
||||||
|
pretty_view.setText(frequency.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void realize(LinearLayout frequency_view,
|
||||||
|
TextView pretty_view) {
|
||||||
|
if (frequency_view != this.frequency_view ||
|
||||||
|
pretty_view != this.pretty_view)
|
||||||
|
{
|
||||||
|
this.frequency_view = frequency_view;
|
||||||
|
this.pretty_view = pretty_view;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set_frequency(AltosFrequency frequency) {
|
||||||
|
this.frequency = frequency;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrequencyItem(AltosFrequency frequency) {
|
||||||
|
this.frequency = frequency;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FrequencyAdapter extends ArrayAdapter<FrequencyItem> {
|
||||||
|
int resource;
|
||||||
|
int selected_item = -1;
|
||||||
|
|
||||||
|
public FrequencyAdapter(Context context, int in_resource) {
|
||||||
|
super(context, in_resource);
|
||||||
|
resource = in_resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int count() {
|
||||||
|
int count;
|
||||||
|
|
||||||
|
for (count = 0;; count++) {
|
||||||
|
try {
|
||||||
|
getItem(count);
|
||||||
|
} catch (IndexOutOfBoundsException ie) {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
|
FrequencyItem item = getItem(position);
|
||||||
|
if (item.frequency_view == null) {
|
||||||
|
LinearLayout frequency_view = new LinearLayout(getContext());
|
||||||
|
String inflater = Context.LAYOUT_INFLATER_SERVICE;
|
||||||
|
LayoutInflater li = (LayoutInflater) getContext().getSystemService(inflater);
|
||||||
|
li.inflate(resource, frequency_view, true);
|
||||||
|
|
||||||
|
item.realize(frequency_view,
|
||||||
|
(TextView) frequency_view.findViewById(R.id.frequency));
|
||||||
|
}
|
||||||
|
if (position == selected_item)
|
||||||
|
item.frequency_view.setBackgroundColor(Color.RED);
|
||||||
|
else
|
||||||
|
item.frequency_view.setBackgroundColor(0);
|
||||||
|
return item.frequency_view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ManageFrequenciesActivity extends Activity {
|
||||||
|
private ListView frequencies_view;
|
||||||
|
|
||||||
|
private Button set;
|
||||||
|
private Button remove;
|
||||||
|
private Button done;
|
||||||
|
|
||||||
|
private EditText set_frequency;
|
||||||
|
private EditText set_description;
|
||||||
|
|
||||||
|
private HashMap<String,FrequencyItem> frequencies = new HashMap<String,FrequencyItem>();;
|
||||||
|
|
||||||
|
private FrequencyAdapter frequencies_adapter;
|
||||||
|
|
||||||
|
private boolean is_bound;
|
||||||
|
private boolean changed = false;
|
||||||
|
|
||||||
|
private void done() {
|
||||||
|
|
||||||
|
set();
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
AltosFrequency[] frequencies = new AltosFrequency[frequencies_adapter.count()];
|
||||||
|
for (int i = 0; i < frequencies.length; i++)
|
||||||
|
frequencies[i] = frequencies_adapter.getItem(i).frequency;
|
||||||
|
AltosPreferences.set_common_frequencies(frequencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
Intent intent = new Intent();
|
||||||
|
setResult(Activity.RESULT_OK, intent);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void load_item() {
|
||||||
|
if (frequencies_adapter.selected_item >= 0) {
|
||||||
|
FrequencyItem item = frequencies_adapter.getItem(frequencies_adapter.selected_item);
|
||||||
|
|
||||||
|
set_frequency.setText(item.frequency.frequency_string());
|
||||||
|
set_description.setText(item.frequency.description);
|
||||||
|
} else {
|
||||||
|
set_frequency.setText("");
|
||||||
|
set_description.setText("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void select_item(int position) {
|
||||||
|
if (position != frequencies_adapter.selected_item) {
|
||||||
|
if (frequencies_adapter.selected_item >= 0)
|
||||||
|
frequencies_view.setItemChecked(frequencies_adapter.selected_item, false);
|
||||||
|
if (position >= 0)
|
||||||
|
frequencies_view.setItemChecked(position, true);
|
||||||
|
frequencies_adapter.selected_item = position;
|
||||||
|
} else {
|
||||||
|
if (frequencies_adapter.selected_item >= 0)
|
||||||
|
frequencies_view.setItemChecked(frequencies_adapter.selected_item, false);
|
||||||
|
frequencies_adapter.selected_item = -1;
|
||||||
|
}
|
||||||
|
load_item();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int find(AltosFrequency frequency) {
|
||||||
|
for (int pos = 0; pos < frequencies_adapter.getCount(); pos++) {
|
||||||
|
FrequencyItem item = frequencies_adapter.getItem(pos);
|
||||||
|
if (item.frequency.frequency == frequency.frequency &&
|
||||||
|
item.frequency.description.equals(frequency.description))
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int insert_item(AltosFrequency frequency) {
|
||||||
|
FrequencyItem new_item = new FrequencyItem(frequency);
|
||||||
|
int pos;
|
||||||
|
for (pos = 0; pos < frequencies_adapter.getCount(); pos++) {
|
||||||
|
FrequencyItem item = frequencies_adapter.getItem(pos);
|
||||||
|
if (item.frequency.frequency == new_item.frequency.frequency) {
|
||||||
|
item.set_frequency(frequency);
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
if (item.frequency.frequency > new_item.frequency.frequency)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
frequencies_adapter.insert(new_item, pos);
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FrequencyItemClickListener implements ListView.OnItemClickListener {
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> av, View v, int position, long id) {
|
||||||
|
select_item(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hide_keyboard() {
|
||||||
|
InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
|
||||||
|
View view = getCurrentFocus();
|
||||||
|
if (view != null)
|
||||||
|
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void set() {
|
||||||
|
String frequency_text = set_frequency.getEditableText().toString();
|
||||||
|
String description_text = set_description.getEditableText().toString();
|
||||||
|
|
||||||
|
try {
|
||||||
|
double f = AltosParse.parse_double_locale(frequency_text);
|
||||||
|
AltosFrequency frequency = new AltosFrequency(f, description_text);
|
||||||
|
int pos;
|
||||||
|
|
||||||
|
pos = find(frequency);
|
||||||
|
if (pos < 0) {
|
||||||
|
pos = insert_item(frequency);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
frequencies_adapter.selected_item = -1;
|
||||||
|
select_item(pos);
|
||||||
|
} catch (ParseException pe) {
|
||||||
|
}
|
||||||
|
hide_keyboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void remove() {
|
||||||
|
if (frequencies_adapter.selected_item >= 0) {
|
||||||
|
frequencies_adapter.remove(frequencies_adapter.getItem(frequencies_adapter.selected_item));
|
||||||
|
select_item(-1);
|
||||||
|
frequencies_view.setAdapter(frequencies_adapter);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
setTheme(AltosDroid.dialog_themes[AltosDroidPreferences.font_size()]);
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// Setup the window
|
||||||
|
setContentView(R.layout.manage_frequencies);
|
||||||
|
|
||||||
|
frequencies_view = (ListView) findViewById(R.id.frequencies);
|
||||||
|
frequencies_view.setClickable(true);
|
||||||
|
|
||||||
|
frequencies_adapter = new FrequencyAdapter(this, R.layout.frequency);
|
||||||
|
|
||||||
|
frequencies_view.setAdapter(frequencies_adapter);
|
||||||
|
frequencies_view.setOnItemClickListener(new FrequencyItemClickListener());
|
||||||
|
|
||||||
|
AltosFrequency[] frequencies = AltosPreferences.common_frequencies();
|
||||||
|
for (AltosFrequency frequency : frequencies)
|
||||||
|
insert_item(frequency);
|
||||||
|
|
||||||
|
set_frequency = (EditText) findViewById(R.id.set_frequency);
|
||||||
|
set_description = (EditText) findViewById(R.id.set_description);
|
||||||
|
|
||||||
|
set = (Button) findViewById(R.id.set);
|
||||||
|
set.setOnClickListener(new OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
set();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
remove = (Button) findViewById(R.id.remove);
|
||||||
|
remove.setOnClickListener(new OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
done = (Button) findViewById(R.id.done);
|
||||||
|
done.setOnClickListener(new OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set result CANCELED incase the user backs out
|
||||||
|
setResult(Activity.RESULT_CANCELED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2015 Keith Packard <keithp@keithp.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.widget.*;
|
||||||
|
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
public class MapTypeActivity extends Activity {
|
||||||
|
private Button hybrid;
|
||||||
|
private Button satellite;
|
||||||
|
private Button roadmap;
|
||||||
|
private Button terrain;
|
||||||
|
private int selected_type;
|
||||||
|
|
||||||
|
public static final String EXTRA_MAP_TYPE = "map_type";
|
||||||
|
|
||||||
|
private void done(int type) {
|
||||||
|
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.putExtra(EXTRA_MAP_TYPE, type);
|
||||||
|
setResult(Activity.RESULT_OK, intent);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void selectType(View view) {
|
||||||
|
AltosDebug.debug("selectType %s", view.toString());
|
||||||
|
if (view == hybrid)
|
||||||
|
done(AltosMap.maptype_hybrid);
|
||||||
|
if (view == satellite)
|
||||||
|
done(AltosMap.maptype_satellite);
|
||||||
|
if (view == roadmap)
|
||||||
|
done(AltosMap.maptype_roadmap);
|
||||||
|
if (view == terrain)
|
||||||
|
done(AltosMap.maptype_terrain);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
setTheme(AltosDroid.dialog_themes[AltosDroidPreferences.font_size()]);
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// Setup the window
|
||||||
|
setContentView(R.layout.map_type);
|
||||||
|
|
||||||
|
hybrid = (Button) findViewById(R.id.map_type_hybrid);
|
||||||
|
satellite = (Button) findViewById(R.id.map_type_satellite);
|
||||||
|
roadmap = (Button) findViewById(R.id.map_type_roadmap);
|
||||||
|
terrain = (Button) findViewById(R.id.map_type_terrain);
|
||||||
|
|
||||||
|
// Set result CANCELED incase the user backs out
|
||||||
|
setResult(Activity.RESULT_CANCELED);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,378 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2015 Keith Packard <keithp@keithp.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.text.*;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
|
import android.widget.*;
|
||||||
|
import android.widget.AdapterView.*;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.location.LocationManager;
|
||||||
|
import android.location.LocationListener;
|
||||||
|
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This Activity appears as a dialog. It lists any paired devices and
|
||||||
|
* devices detected in the area after discovery. When a device is chosen
|
||||||
|
* by the user, the MAC address of the device is sent back to the parent
|
||||||
|
* Activity in the result Intent.
|
||||||
|
*/
|
||||||
|
public class PreloadMapActivity extends Activity implements AltosLaunchSiteListener, AltosMapLoaderListener, LocationListener {
|
||||||
|
|
||||||
|
private ArrayAdapter<AltosLaunchSite> known_sites_adapter;
|
||||||
|
|
||||||
|
/*
|
||||||
|
private CheckBox hybrid;
|
||||||
|
private CheckBox satellite;
|
||||||
|
private CheckBox roadmap;
|
||||||
|
private CheckBox terrain;
|
||||||
|
*/
|
||||||
|
|
||||||
|
private Spinner known_sites_spinner;
|
||||||
|
private Spinner min_zoom;
|
||||||
|
private Spinner max_zoom;
|
||||||
|
private TextView radius_label;
|
||||||
|
private Spinner radius;
|
||||||
|
|
||||||
|
private EditText latitude;
|
||||||
|
private EditText longitude;
|
||||||
|
|
||||||
|
private ProgressBar progress;
|
||||||
|
|
||||||
|
private AltosMapLoader loader;
|
||||||
|
|
||||||
|
long loader_notify_time;
|
||||||
|
|
||||||
|
/* AltosMapLoaderListener interfaces */
|
||||||
|
public void loader_start(final int max) {
|
||||||
|
loader_notify_time = System.currentTimeMillis();
|
||||||
|
|
||||||
|
this.runOnUiThread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
progress.setMax(max);
|
||||||
|
progress.setProgress(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loader_notify(final int cur, final int max, final String name) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
|
||||||
|
if (now - loader_notify_time < 100)
|
||||||
|
return;
|
||||||
|
|
||||||
|
loader_notify_time = now;
|
||||||
|
|
||||||
|
this.runOnUiThread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
progress.setProgress(cur);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loader_done(int max) {
|
||||||
|
loader = null;
|
||||||
|
this.runOnUiThread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
progress.setProgress(0);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void debug(String format, Object ... arguments) {
|
||||||
|
AltosDebug.debug(format, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AltosLaunchSiteListener interface */
|
||||||
|
|
||||||
|
public void notify_launch_sites(final List<AltosLaunchSite> sites) {
|
||||||
|
this.runOnUiThread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
for (AltosLaunchSite site : sites)
|
||||||
|
known_sites_adapter.add(site);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LocationProvider interface */
|
||||||
|
|
||||||
|
AltosLaunchSite current_location_site;
|
||||||
|
|
||||||
|
public void onLocationChanged(Location location) {
|
||||||
|
AltosDebug.debug("location changed");
|
||||||
|
if (current_location_site == null) {
|
||||||
|
AltosLaunchSite selected_item = (AltosLaunchSite) known_sites_spinner.getSelectedItem();
|
||||||
|
|
||||||
|
current_location_site = new AltosLaunchSite("Current Location", location.getLatitude(), location.getLongitude());
|
||||||
|
known_sites_adapter.insert(current_location_site, 0);
|
||||||
|
|
||||||
|
if (selected_item != null)
|
||||||
|
known_sites_spinner.setSelection(known_sites_adapter.getPosition(selected_item));
|
||||||
|
else {
|
||||||
|
latitude.setText(new StringBuffer(String.format("%12.6f", current_location_site.latitude)));
|
||||||
|
longitude.setText(new StringBuffer(String.format("%12.6f", current_location_site.longitude)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current_location_site.latitude = location.getLatitude();
|
||||||
|
current_location_site.longitude = location.getLongitude();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onProviderEnabled(String provider) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onProviderDisabled(String provider) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private double text(EditText view) throws ParseException {
|
||||||
|
return AltosParse.parse_double_locale(view.getEditableText().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private double latitude() throws ParseException {
|
||||||
|
return text(latitude);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double longitude() throws ParseException {
|
||||||
|
return text(longitude);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int value(Spinner spinner) {
|
||||||
|
return (Integer) spinner.getSelectedItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int min_z() {
|
||||||
|
return value(min_zoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int max_z() {
|
||||||
|
return value(max_zoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double value_distance(Spinner spinner) {
|
||||||
|
return (Double) spinner.getSelectedItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
private double radius() {
|
||||||
|
double r = value_distance(radius);
|
||||||
|
if (AltosPreferences.imperial_units())
|
||||||
|
r = AltosConvert.miles_to_meters(r);
|
||||||
|
else
|
||||||
|
r = r * 1000;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
private int bit(CheckBox box, int value) {
|
||||||
|
if (box.isChecked())
|
||||||
|
return 1 << value;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
private int types() {
|
||||||
|
/*
|
||||||
|
return (bit(hybrid, AltosMap.maptype_hybrid) |
|
||||||
|
bit(satellite, AltosMap.maptype_satellite) |
|
||||||
|
bit(roadmap, AltosMap.maptype_roadmap) |
|
||||||
|
bit(terrain, AltosMap.maptype_terrain));
|
||||||
|
*/
|
||||||
|
return 1 << AltosMap.maptype_hybrid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void load() {
|
||||||
|
if (loader != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
double lat = latitude();
|
||||||
|
double lon = longitude();
|
||||||
|
int min = min_z();
|
||||||
|
int max = max_z();
|
||||||
|
double r = radius();
|
||||||
|
int t = types();
|
||||||
|
|
||||||
|
AltosDebug.debug("PreloadMap load %f %f %d %d %f %d\n",
|
||||||
|
lat, lon, min, max, r, t);
|
||||||
|
loader = new AltosMapLoader(this, lat, lon, min, max, r, t, AltosMapOffline.scale);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
AltosDebug.debug("PreloadMap load raised exception %s", e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void add_numbers(Spinner spinner, int min, int max, int def) {
|
||||||
|
|
||||||
|
ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(this, android.R.layout.simple_spinner_item);
|
||||||
|
|
||||||
|
int spinner_def = 0;
|
||||||
|
int pos = 0;
|
||||||
|
|
||||||
|
for (int i = min; i <= max; i++) {
|
||||||
|
adapter.add(new Integer(i));
|
||||||
|
if (i == def)
|
||||||
|
spinner_def = pos;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
spinner.setAdapter(adapter);
|
||||||
|
spinner.setSelection(spinner_def);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void add_distance(Spinner spinner, double[] distances_km, double def_km, double[] distances_mi, double def_mi) {
|
||||||
|
|
||||||
|
ArrayAdapter<Double> adapter = new ArrayAdapter<Double>(this, android.R.layout.simple_spinner_item);
|
||||||
|
|
||||||
|
int spinner_def = 0;
|
||||||
|
int pos = 0;
|
||||||
|
|
||||||
|
double[] distances;
|
||||||
|
double def;
|
||||||
|
if (AltosPreferences.imperial_units()) {
|
||||||
|
distances = distances_mi;
|
||||||
|
def = def_mi;
|
||||||
|
} else {
|
||||||
|
distances = distances_km;
|
||||||
|
def = def_km;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < distances.length; i++) {
|
||||||
|
adapter.add(distances[i]);
|
||||||
|
if (distances[i] == def)
|
||||||
|
spinner_def = pos;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
spinner.setAdapter(adapter);
|
||||||
|
spinner.setSelection(spinner_def);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SiteListListener implements OnItemSelectedListener {
|
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
|
||||||
|
AltosLaunchSite site = (AltosLaunchSite) parent.getItemAtPosition(pos);
|
||||||
|
latitude.setText(new StringBuffer(String.format("%12.6f", site.latitude)));
|
||||||
|
longitude.setText(new StringBuffer(String.format("%12.6f", site.longitude)));
|
||||||
|
}
|
||||||
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public SiteListListener() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double[] radius_mi = { 1, 2, 5, 10, 20 };
|
||||||
|
double radius_def_mi = 2;
|
||||||
|
double[] radius_km = { 1, 2, 5, 10, 20, 30 };
|
||||||
|
double radius_def_km = 2;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
setTheme(AltosDroid.dialog_themes[AltosDroidPreferences.font_size()]);
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// Setup the window
|
||||||
|
setContentView(R.layout.map_preload);
|
||||||
|
|
||||||
|
// Set result CANCELED incase the user backs out
|
||||||
|
setResult(Activity.RESULT_CANCELED);
|
||||||
|
|
||||||
|
// Initialize the button to perform device discovery
|
||||||
|
Button loadButton = (Button) findViewById(R.id.preload_load);
|
||||||
|
loadButton.setOnClickListener(new OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
latitude = (EditText) findViewById(R.id.preload_latitude);
|
||||||
|
longitude = (EditText) findViewById(R.id.preload_longitude);
|
||||||
|
|
||||||
|
/*
|
||||||
|
hybrid = (CheckBox) findViewById(R.id.preload_hybrid);
|
||||||
|
satellite = (CheckBox) findViewById(R.id.preload_satellite);
|
||||||
|
roadmap = (CheckBox) findViewById(R.id.preload_roadmap);
|
||||||
|
terrain = (CheckBox) findViewById(R.id.preload_terrain);
|
||||||
|
|
||||||
|
hybrid.setChecked(true);
|
||||||
|
*/
|
||||||
|
|
||||||
|
min_zoom = (Spinner) findViewById(R.id.preload_min_zoom);
|
||||||
|
add_numbers(min_zoom,
|
||||||
|
AltosMap.min_zoom - AltosMap.default_zoom,
|
||||||
|
AltosMap.max_zoom - AltosMap.default_zoom, -2);
|
||||||
|
max_zoom = (Spinner) findViewById(R.id.preload_max_zoom);
|
||||||
|
add_numbers(max_zoom,
|
||||||
|
AltosMap.min_zoom - AltosMap.default_zoom,
|
||||||
|
AltosMap.max_zoom - AltosMap.default_zoom, 2);
|
||||||
|
radius_label = (TextView) findViewById(R.id.preload_radius_label);
|
||||||
|
radius = (Spinner) findViewById(R.id.preload_radius);
|
||||||
|
if (AltosPreferences.imperial_units())
|
||||||
|
radius_label.setText("Radius (miles)");
|
||||||
|
else
|
||||||
|
radius_label.setText("Radius (km)");
|
||||||
|
add_distance(radius, radius_km, radius_def_km, radius_mi, radius_def_mi);
|
||||||
|
|
||||||
|
progress = (ProgressBar) findViewById(R.id.preload_progress);
|
||||||
|
|
||||||
|
// Initialize array adapters. One for already paired devices and
|
||||||
|
// one for newly discovered devices
|
||||||
|
known_sites_spinner = (Spinner) findViewById(R.id.preload_site_list);
|
||||||
|
|
||||||
|
known_sites_adapter = new ArrayAdapter<AltosLaunchSite>(this, android.R.layout.simple_spinner_item);
|
||||||
|
|
||||||
|
known_sites_adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
|
||||||
|
known_sites_spinner.setAdapter(known_sites_adapter);
|
||||||
|
known_sites_spinner.setOnItemSelectedListener(new SiteListListener());
|
||||||
|
|
||||||
|
// Listen for GPS and Network position updates
|
||||||
|
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
|
||||||
|
try {
|
||||||
|
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, this);
|
||||||
|
} catch (Exception e) {
|
||||||
|
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000, 1, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
new AltosLaunchSites(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
|
||||||
|
if (loader != null)
|
||||||
|
loader.abort();
|
||||||
|
|
||||||
|
// Stop listening for location updates
|
||||||
|
((LocationManager) getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,297 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2020 Keith Packard <keithp@keithp.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.*;
|
||||||
|
import android.os.*;
|
||||||
|
import android.util.*;
|
||||||
|
import android.view.*;
|
||||||
|
import android.view.View.*;
|
||||||
|
import android.widget.*;
|
||||||
|
import android.graphics.*;
|
||||||
|
import android.graphics.drawable.*;
|
||||||
|
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||||
|
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
class TrackerComparatorCall implements Comparator<Tracker> {
|
||||||
|
public int compare(Tracker a, Tracker b) {
|
||||||
|
int v;
|
||||||
|
|
||||||
|
v = a.compareCall(b);
|
||||||
|
if (v != 0)
|
||||||
|
return v;
|
||||||
|
v = a.compareAge(b);
|
||||||
|
if (v != 0)
|
||||||
|
return v;
|
||||||
|
v = a.compareSerial(b);
|
||||||
|
if (v != 0)
|
||||||
|
return v;
|
||||||
|
return a.compareFrequency(b);
|
||||||
|
}
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o instanceof TrackerComparatorCall;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TrackerComparatorSerial implements Comparator<Tracker> {
|
||||||
|
public int compare(Tracker a, Tracker b) {
|
||||||
|
int v;
|
||||||
|
|
||||||
|
v = a.compareSerial(b);
|
||||||
|
if (v != 0)
|
||||||
|
return v;
|
||||||
|
v = a.compareAge(b);
|
||||||
|
if (v != 0)
|
||||||
|
return v;
|
||||||
|
v = a.compareCall(b);
|
||||||
|
if (v != 0)
|
||||||
|
return v;
|
||||||
|
return a.compareFrequency(b);
|
||||||
|
}
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o instanceof TrackerComparatorSerial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TrackerComparatorAge implements Comparator<Tracker> {
|
||||||
|
public int compare(Tracker a, Tracker b) {
|
||||||
|
int v;
|
||||||
|
|
||||||
|
v = a.compareAge(b);
|
||||||
|
if (v != 0)
|
||||||
|
return v;
|
||||||
|
v = a.compareCall(b);
|
||||||
|
if (v != 0)
|
||||||
|
return v;
|
||||||
|
v = a.compareSerial(b);
|
||||||
|
if (v != 0)
|
||||||
|
return v;
|
||||||
|
return a.compareFrequency(b);
|
||||||
|
}
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o instanceof TrackerComparatorAge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TrackerComparatorFrequency implements Comparator<Tracker> {
|
||||||
|
public int compare(Tracker a, Tracker b) {
|
||||||
|
int v;
|
||||||
|
|
||||||
|
v = a.compareFrequency(b);
|
||||||
|
if (v != 0)
|
||||||
|
return v;
|
||||||
|
v = a.compareAge(b);
|
||||||
|
if (v != 0)
|
||||||
|
return v;
|
||||||
|
v = a.compareCall(b);
|
||||||
|
if (v != 0)
|
||||||
|
return v;
|
||||||
|
return a.compareSerial(b);
|
||||||
|
}
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o instanceof TrackerComparatorFrequency;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SelectTrackerActivity extends Activity implements OnTouchListener {
|
||||||
|
// Return Intent extra
|
||||||
|
public static final String EXTRA_SERIAL_NUMBER = "serial_number";
|
||||||
|
public static final String EXTRA_FREQUENCY = "frequency";
|
||||||
|
|
||||||
|
private int button_ids[] = {
|
||||||
|
R.id.call_button,
|
||||||
|
R.id.serial_button,
|
||||||
|
R.id.frequency_button,
|
||||||
|
R.id.age_button
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final int call_button = 0;
|
||||||
|
private static final int serial_button = 1;
|
||||||
|
private static final int freq_button = 2;
|
||||||
|
private static final int age_button = 3;
|
||||||
|
private RadioButton radio_buttons[] = new RadioButton[4];
|
||||||
|
private TableLayout table;
|
||||||
|
|
||||||
|
private Tracker[] trackers;
|
||||||
|
|
||||||
|
private void set_sort(int id) {
|
||||||
|
AltosDroidPreferences.set_tracker_sort(id);
|
||||||
|
resort();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resort() {
|
||||||
|
Comparator<Tracker> compare;
|
||||||
|
int tracker_sort = AltosDroidPreferences.tracker_sort();
|
||||||
|
AltosDebug.debug("sort %d", tracker_sort);
|
||||||
|
switch (tracker_sort) {
|
||||||
|
case call_button:
|
||||||
|
default:
|
||||||
|
compare = new TrackerComparatorCall();
|
||||||
|
break;
|
||||||
|
case serial_button:
|
||||||
|
compare = new TrackerComparatorSerial();
|
||||||
|
break;
|
||||||
|
case freq_button:
|
||||||
|
compare = new TrackerComparatorFrequency();
|
||||||
|
break;
|
||||||
|
case age_button:
|
||||||
|
compare = new TrackerComparatorAge();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Arrays.sort(trackers, compare);
|
||||||
|
set_trackers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_button_state() {
|
||||||
|
int tracker_sort = AltosDroidPreferences.tracker_sort();
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
radio_buttons[i].setChecked(i == tracker_sort);
|
||||||
|
}
|
||||||
|
|
||||||
|
OnCheckedChangeListener button_listener = new OnCheckedChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||||
|
int id = buttonView.getId();
|
||||||
|
if (isChecked) {
|
||||||
|
int sort_id = -1;
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
if (id == button_ids[i])
|
||||||
|
sort_id = i;
|
||||||
|
else
|
||||||
|
radio_buttons[i].setChecked(false);
|
||||||
|
}
|
||||||
|
if (sort_id != -1)
|
||||||
|
set_sort(sort_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
long start_time;
|
||||||
|
|
||||||
|
private void
|
||||||
|
insert_tracker(Tracker tracker) {
|
||||||
|
TableRow row = (TableRow) getLayoutInflater().inflate(R.layout.tracker_ent, null);
|
||||||
|
|
||||||
|
((TextView) row.findViewById(R.id.call_view)).setText(tracker.call);
|
||||||
|
if (tracker.serial == 0)
|
||||||
|
((TextView) row.findViewById(R.id.serial_view)).setText("");
|
||||||
|
else
|
||||||
|
((TextView) row.findViewById(R.id.serial_view)).setText(String.format("%d", tracker.serial));
|
||||||
|
if (tracker.frequency == 0.0)
|
||||||
|
((TextView) row.findViewById(R.id.frequency_view)).setText("");
|
||||||
|
else if (tracker.frequency == AltosLib.MISSING)
|
||||||
|
((TextView) row.findViewById(R.id.frequency_view)).setText("");
|
||||||
|
else
|
||||||
|
((TextView) row.findViewById(R.id.frequency_view)).setText(String.format("%7.3f", tracker.frequency));
|
||||||
|
if (tracker.received_time != 0) {
|
||||||
|
int age = (int) ((start_time - tracker.received_time + 500) / 1000);
|
||||||
|
((TextView) row.findViewById(R.id.age_view)).setText(AltosDroid.age_string(age));
|
||||||
|
} else {
|
||||||
|
((TextView) row.findViewById(R.id.age_view)).setText("");
|
||||||
|
}
|
||||||
|
row.setClickable(true);
|
||||||
|
row.setOnTouchListener(this);
|
||||||
|
table.addView(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void set_trackers() {
|
||||||
|
for (int i = table.getChildCount() - 1; i >= 1; i--)
|
||||||
|
table.removeViewAt(i);
|
||||||
|
for (Tracker tracker : trackers)
|
||||||
|
insert_tracker(tracker);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void done(View v) {
|
||||||
|
int result = Activity.RESULT_CANCELED;
|
||||||
|
Intent intent = new Intent();
|
||||||
|
for (int i = 1; i < table.getChildCount(); i++) {
|
||||||
|
View child = table.getChildAt(i);
|
||||||
|
if (child == v) {
|
||||||
|
Tracker tracker = trackers[i - 1];
|
||||||
|
intent.putExtra(EXTRA_SERIAL_NUMBER, tracker.serial);
|
||||||
|
intent.putExtra(EXTRA_FREQUENCY, tracker.frequency);
|
||||||
|
result = Activity.RESULT_OK;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setResult(Activity.RESULT_OK, intent);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
int title_id = getIntent().getIntExtra(AltosDroid.EXTRA_TRACKERS_TITLE, R.id.select_tracker);
|
||||||
|
AltosDebug.debug("get title id 0x%x %s", title_id, getResources().getText(title_id));
|
||||||
|
setTitle(getResources().getText(title_id));
|
||||||
|
setTheme(AltosDroid.dialog_themes[AltosDroidPreferences.font_size()]);
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
setContentView(R.layout.tracker_list);
|
||||||
|
// Set result CANCELED incase the user backs out
|
||||||
|
setResult(Activity.RESULT_CANCELED);
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
radio_buttons[i] = (RadioButton) findViewById(button_ids[i]);
|
||||||
|
radio_buttons[i].setOnCheckedChangeListener(button_listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<Parcelable> tracker_array = (ArrayList<Parcelable>) getIntent().getParcelableArrayListExtra(AltosDroid.EXTRA_TRACKERS);
|
||||||
|
if (tracker_array != null) {
|
||||||
|
Object[] array = tracker_array.toArray();
|
||||||
|
trackers = new Tracker[array.length];
|
||||||
|
for (int i = 0; i < array.length; i++)
|
||||||
|
trackers[i] = (Tracker) array[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
start_time = System.currentTimeMillis();
|
||||||
|
|
||||||
|
table = (TableLayout) findViewById(R.id.tracker_list);
|
||||||
|
|
||||||
|
init_button_state();
|
||||||
|
|
||||||
|
resort();
|
||||||
|
|
||||||
|
set_trackers();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouch(View v, MotionEvent event) {
|
||||||
|
int action = event.getAction() & MotionEvent.ACTION_MASK;
|
||||||
|
switch (action) {
|
||||||
|
case MotionEvent.ACTION_UP:
|
||||||
|
case MotionEvent.ACTION_CANCEL:
|
||||||
|
case MotionEvent.ACTION_OUTSIDE:
|
||||||
|
v.setBackgroundColor(0);
|
||||||
|
v.invalidate();
|
||||||
|
break;
|
||||||
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
v.setBackgroundColor(Color.RED);
|
||||||
|
v.invalidate();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (action == MotionEvent.ACTION_UP) {
|
||||||
|
done(v);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,365 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2016 Keith Packard <keithp@keithp.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.*;
|
||||||
|
import android.os.*;
|
||||||
|
import android.view.*;
|
||||||
|
import android.view.View.*;
|
||||||
|
import android.widget.*;
|
||||||
|
import android.widget.AdapterView.*;
|
||||||
|
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
public class SetupActivity extends Activity {
|
||||||
|
private Spinner select_rate;
|
||||||
|
private Spinner set_units;
|
||||||
|
private Spinner font_size;
|
||||||
|
private Spinner map_type;
|
||||||
|
private Spinner map_source;
|
||||||
|
private Button manage_frequencies;
|
||||||
|
private Button preload_maps;
|
||||||
|
private Button done;
|
||||||
|
|
||||||
|
private boolean is_bound;
|
||||||
|
private Messenger service = null;
|
||||||
|
|
||||||
|
public final static String EXTRA_SETUP_CHANGES = "setup_changes";
|
||||||
|
|
||||||
|
private ServiceConnection connection = new ServiceConnection() {
|
||||||
|
public void onServiceConnected(ComponentName className, IBinder binder) {
|
||||||
|
service = new Messenger(binder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onServiceDisconnected(ComponentName className) {
|
||||||
|
// This is called when the connection with the service has been unexpectedly disconnected - process crashed.
|
||||||
|
service = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void doBindService() {
|
||||||
|
bindService(new Intent(this, TelemetryService.class), connection, Context.BIND_AUTO_CREATE);
|
||||||
|
is_bound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void doUnbindService() {
|
||||||
|
if (is_bound) {
|
||||||
|
// If we have received the service, and hence registered with it, then now is the time to unregister.
|
||||||
|
unbindService(connection);
|
||||||
|
is_bound = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static final String[] rates = {
|
||||||
|
"38400",
|
||||||
|
"9600",
|
||||||
|
"2400",
|
||||||
|
};
|
||||||
|
|
||||||
|
static final String[] sizes = {
|
||||||
|
"Small",
|
||||||
|
"Medium",
|
||||||
|
"Large",
|
||||||
|
"Extra"
|
||||||
|
};
|
||||||
|
|
||||||
|
static final String[] map_types = {
|
||||||
|
"Hybrid",
|
||||||
|
"Satellite",
|
||||||
|
"Roadmap",
|
||||||
|
"Terrain"
|
||||||
|
};
|
||||||
|
|
||||||
|
static final int[] map_type_values = {
|
||||||
|
AltosMap.maptype_hybrid,
|
||||||
|
AltosMap.maptype_satellite,
|
||||||
|
AltosMap.maptype_roadmap,
|
||||||
|
AltosMap.maptype_terrain,
|
||||||
|
};
|
||||||
|
|
||||||
|
static final String[] map_sources = {
|
||||||
|
"Online",
|
||||||
|
"Offline"
|
||||||
|
};
|
||||||
|
|
||||||
|
private int set_telemetry_rate;
|
||||||
|
private int set_map_source;
|
||||||
|
private int set_map_type;
|
||||||
|
private boolean set_imperial_units;
|
||||||
|
private int set_font_size;
|
||||||
|
|
||||||
|
private void done() {
|
||||||
|
int changes = 0;
|
||||||
|
Intent intent = new Intent();
|
||||||
|
|
||||||
|
if (set_telemetry_rate != AltosPreferences.telemetry_rate(1)) {
|
||||||
|
changes |= AltosDroid.SETUP_BAUD;
|
||||||
|
AltosPreferences.set_telemetry_rate(1, set_telemetry_rate);
|
||||||
|
}
|
||||||
|
if (set_imperial_units != AltosPreferences.imperial_units()) {
|
||||||
|
changes |= AltosDroid.SETUP_UNITS;
|
||||||
|
AltosPreferences.set_imperial_units(set_imperial_units);
|
||||||
|
}
|
||||||
|
if (set_map_source != AltosDroidPreferences.map_source()) {
|
||||||
|
changes |= AltosDroid.SETUP_MAP_SOURCE;
|
||||||
|
AltosDroidPreferences.set_map_source(set_map_source);
|
||||||
|
}
|
||||||
|
if (set_map_type != AltosPreferences.map_type()) {
|
||||||
|
changes |= AltosDroid.SETUP_MAP_TYPE;
|
||||||
|
AltosPreferences.set_map_type(set_map_type);
|
||||||
|
}
|
||||||
|
if (set_font_size != AltosDroidPreferences.font_size()) {
|
||||||
|
changes |= AltosDroid.SETUP_FONT_SIZE;
|
||||||
|
AltosDroidPreferences.set_font_size(set_font_size);
|
||||||
|
}
|
||||||
|
intent.putExtra(EXTRA_SETUP_CHANGES, changes);
|
||||||
|
setResult(Activity.RESULT_OK, intent);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void add_strings(Spinner spinner, String[] strings, int def) {
|
||||||
|
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
|
||||||
|
for (int i = 0; i < strings.length; i++)
|
||||||
|
adapter.add(strings[i]);
|
||||||
|
|
||||||
|
spinner.setAdapter(adapter);
|
||||||
|
if (def >= 0)
|
||||||
|
spinner.setSelection(def);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int default_rate_pos() {
|
||||||
|
int default_rate = AltosPreferences.telemetry_rate(1);
|
||||||
|
|
||||||
|
for (int pos = 0; pos < rates.length; pos++) {
|
||||||
|
if (string_to_rate(rates[pos]) == default_rate)
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setBaud(int baud) {
|
||||||
|
set_telemetry_rate = baud;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int string_to_rate(String baud) {
|
||||||
|
int rate = AltosLib.ao_telemetry_rate_38400;
|
||||||
|
try {
|
||||||
|
int value = Integer.parseInt(baud);
|
||||||
|
switch (value) {
|
||||||
|
case 2400:
|
||||||
|
rate = AltosLib.ao_telemetry_rate_2400;
|
||||||
|
break;
|
||||||
|
case 9600:
|
||||||
|
rate = AltosLib.ao_telemetry_rate_9600;
|
||||||
|
break;
|
||||||
|
case 38400:
|
||||||
|
rate = AltosLib.ao_telemetry_rate_38400;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
}
|
||||||
|
return rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setBaud(String baud) {
|
||||||
|
setBaud(string_to_rate(baud));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void select_rate(int pos) {
|
||||||
|
setBaud(rates[pos]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static final String[] units = {
|
||||||
|
"Metric",
|
||||||
|
"Imperial"
|
||||||
|
};
|
||||||
|
|
||||||
|
private int default_units_pos() {
|
||||||
|
boolean imperial = AltosPreferences.imperial_units();
|
||||||
|
|
||||||
|
if (imperial)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void set_units(int pos) {
|
||||||
|
switch (pos) {
|
||||||
|
default:
|
||||||
|
set_imperial_units = false;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
set_imperial_units = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void set_font_size(int pos) {
|
||||||
|
set_font_size = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int default_map_type_pos() {
|
||||||
|
int default_map_type = AltosPreferences.map_type();
|
||||||
|
|
||||||
|
for (int pos = 0; pos < map_types.length; pos++)
|
||||||
|
if (map_type_values[pos] == default_map_type)
|
||||||
|
return pos;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void select_map_type(int pos) {
|
||||||
|
set_map_type = map_type_values[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
private int default_map_source_pos() {
|
||||||
|
int default_source = AltosDroidPreferences.map_source();
|
||||||
|
|
||||||
|
switch (default_source) {
|
||||||
|
case AltosDroidPreferences.MAP_SOURCE_OFFLINE:
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void select_map_source(int pos) {
|
||||||
|
switch (pos) {
|
||||||
|
default:
|
||||||
|
set_map_source = AltosDroidPreferences.MAP_SOURCE_ONLINE;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
set_map_source = AltosDroidPreferences.MAP_SOURCE_OFFLINE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void manage_frequencies(){
|
||||||
|
Intent intent = new Intent(this, ManageFrequenciesActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void preload_maps(){
|
||||||
|
Intent intent = new Intent(this, PreloadMapActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
setTheme(AltosDroid.dialog_themes[AltosDroidPreferences.font_size()]);
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
AltosDebug.init(this);
|
||||||
|
AltosDebug.debug("+++ ON CREATE +++");
|
||||||
|
|
||||||
|
// Initialise preferences
|
||||||
|
AltosDroidPreferences.init(this);
|
||||||
|
|
||||||
|
// Setup the window
|
||||||
|
setContentView(R.layout.setup);
|
||||||
|
|
||||||
|
select_rate = (Spinner) findViewById(R.id.select_rate);
|
||||||
|
add_strings(select_rate, rates, default_rate_pos());
|
||||||
|
select_rate.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
|
||||||
|
AltosDebug.debug("rate selected pos %d id %d", pos, id);
|
||||||
|
select_rate(pos);
|
||||||
|
}
|
||||||
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
set_units = (Spinner) findViewById(R.id.set_units);
|
||||||
|
add_strings(set_units, units, default_units_pos());
|
||||||
|
set_units.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
|
||||||
|
set_units(pos);
|
||||||
|
}
|
||||||
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
font_size = (Spinner) findViewById(R.id.font_size);
|
||||||
|
add_strings(font_size, sizes, AltosDroidPreferences.font_size());
|
||||||
|
font_size.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
|
||||||
|
set_font_size(pos);
|
||||||
|
}
|
||||||
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
map_type = (Spinner) findViewById(R.id.map_type);
|
||||||
|
add_strings(map_type, map_types, default_map_type_pos());
|
||||||
|
map_type.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
|
||||||
|
select_map_type(pos);
|
||||||
|
}
|
||||||
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
map_source = (Spinner) findViewById(R.id.map_source);
|
||||||
|
add_strings(map_source, map_sources, default_map_source_pos());
|
||||||
|
map_source.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
|
||||||
|
select_map_source(pos);
|
||||||
|
}
|
||||||
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
manage_frequencies = (Button) findViewById(R.id.manage_frequencies);
|
||||||
|
manage_frequencies.setOnClickListener(new OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
manage_frequencies();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
preload_maps = (Button) findViewById(R.id.preload_maps);
|
||||||
|
preload_maps.setOnClickListener(new OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
preload_maps();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
done = (Button) findViewById(R.id.done);
|
||||||
|
done.setOnClickListener(new OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set result for when the user backs out
|
||||||
|
setResult(Activity.RESULT_CANCELED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
doBindService();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
doUnbindService();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.*;
|
||||||
|
import android.widget.*;
|
||||||
|
import android.location.Location;
|
||||||
|
|
||||||
|
public class TabFlight extends AltosDroidTab {
|
||||||
|
private TextView speed_view;
|
||||||
|
private TextView height_view;
|
||||||
|
private TextView altitude_view;
|
||||||
|
private View tilt_view;
|
||||||
|
private TextView tilt_value;
|
||||||
|
private TextView max_speed_view;
|
||||||
|
private TextView max_height_view;
|
||||||
|
private TextView max_altitude_view;
|
||||||
|
private TextView elevation_view;
|
||||||
|
private TextView range_view;
|
||||||
|
private TextView bearing_view;
|
||||||
|
private TextView compass_view;
|
||||||
|
private TextView distance_view;
|
||||||
|
private TextView latitude_view;
|
||||||
|
private TextView longitude_view;
|
||||||
|
private View apogee_view;
|
||||||
|
private TextView apogee_voltage_view;
|
||||||
|
private TextView apogee_voltage_label;
|
||||||
|
private GoNoGoLights apogee_lights;
|
||||||
|
private View main_view;
|
||||||
|
private TextView main_voltage_view;
|
||||||
|
private TextView main_voltage_label;
|
||||||
|
private GoNoGoLights main_lights;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
View v = inflater.inflate(R.layout.tab_flight, container, false);
|
||||||
|
|
||||||
|
speed_view = (TextView) v.findViewById(R.id.speed_value);
|
||||||
|
height_view = (TextView) v.findViewById(R.id.height_value);
|
||||||
|
altitude_view = (TextView) v.findViewById(R.id.altitude_value);
|
||||||
|
tilt_view = (View) v.findViewById(R.id.tilt_view);
|
||||||
|
tilt_value = (TextView) v.findViewById(R.id.tilt_value);
|
||||||
|
max_speed_view = (TextView) v.findViewById(R.id.max_speed_value);
|
||||||
|
max_height_view = (TextView) v.findViewById(R.id.max_height_value);
|
||||||
|
max_altitude_view= (TextView) v.findViewById(R.id.max_altitude_value);
|
||||||
|
elevation_view = (TextView) v.findViewById(R.id.elevation_value);
|
||||||
|
range_view = (TextView) v.findViewById(R.id.range_value);
|
||||||
|
bearing_view = (TextView) v.findViewById(R.id.bearing_value);
|
||||||
|
compass_view = (TextView) v.findViewById(R.id.compass_value);
|
||||||
|
distance_view = (TextView) v.findViewById(R.id.distance_value);
|
||||||
|
latitude_view = (TextView) v.findViewById(R.id.lat_value);
|
||||||
|
longitude_view = (TextView) v.findViewById(R.id.lon_value);
|
||||||
|
|
||||||
|
apogee_view = v.findViewById(R.id.apogee_view);
|
||||||
|
apogee_voltage_view = (TextView) v.findViewById(R.id.apogee_voltage_value);
|
||||||
|
apogee_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled),
|
||||||
|
(ImageView) v.findViewById(R.id.apogee_greenled),
|
||||||
|
getResources());
|
||||||
|
apogee_voltage_label = (TextView) v.findViewById(R.id.apogee_voltage_label);
|
||||||
|
|
||||||
|
main_view = v.findViewById(R.id.main_view);
|
||||||
|
main_voltage_view = (TextView) v.findViewById(R.id.main_voltage_value);
|
||||||
|
main_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled),
|
||||||
|
(ImageView) v.findViewById(R.id.main_greenled),
|
||||||
|
getResources());
|
||||||
|
main_voltage_label = (TextView) v.findViewById(R.id.main_voltage_label);
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String tab_name() { return AltosDroid.tab_flight_name; }
|
||||||
|
|
||||||
|
public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
|
||||||
|
if (state != null) {
|
||||||
|
set_value(speed_view, AltosConvert.speed, 1, state.speed());
|
||||||
|
set_value(height_view, AltosConvert.height, 1, state.height());
|
||||||
|
set_value(altitude_view, AltosConvert.height, 1, state.altitude());
|
||||||
|
double orient = state.orient();
|
||||||
|
if (orient == AltosLib.MISSING) {
|
||||||
|
tilt_view.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
tilt_value.setText(AltosDroid.number("%1.0f°", orient));
|
||||||
|
tilt_view.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
set_value(max_speed_view, AltosConvert.speed, 1, state.max_speed());
|
||||||
|
set_value(max_height_view, AltosConvert.height, 1, state.max_height());
|
||||||
|
set_value(max_altitude_view, AltosConvert.height, 1, state.max_altitude());
|
||||||
|
if (from_receiver != null) {
|
||||||
|
elevation_view.setText(AltosDroid.number("%1.0f°", from_receiver.elevation));
|
||||||
|
set_value(range_view, AltosConvert.distance, 1, from_receiver.range);
|
||||||
|
bearing_view.setText(AltosDroid.number("%1.0f°", from_receiver.bearing));
|
||||||
|
compass_view.setText(from_receiver.bearing_words(AltosGreatCircle.BEARING_LONG));
|
||||||
|
set_value(distance_view, AltosConvert.distance, 1, from_receiver.distance);
|
||||||
|
} else {
|
||||||
|
elevation_view.setText("<unknown>");
|
||||||
|
range_view.setText("<unknown>");
|
||||||
|
bearing_view.setText("<unknown>");
|
||||||
|
compass_view.setText("<unknown>");
|
||||||
|
distance_view.setText("<unknown>");
|
||||||
|
}
|
||||||
|
if (state.gps != null) {
|
||||||
|
latitude_view.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
|
||||||
|
longitude_view.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.apogee_voltage == AltosLib.MISSING) {
|
||||||
|
apogee_view.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
apogee_voltage_view.setText(AltosDroid.number("%1.2f V", state.apogee_voltage));
|
||||||
|
apogee_lights.set(state.apogee_voltage > 3.2, state.apogee_voltage == AltosLib.MISSING);
|
||||||
|
apogee_view.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.main_voltage == AltosLib.MISSING) {
|
||||||
|
main_view.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
main_voltage_view.setText(AltosDroid.number("%1.2f V", state.main_voltage));
|
||||||
|
main_lights.set(state.main_voltage > 3.2, state.main_voltage == AltosLib.MISSING);
|
||||||
|
main_view.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.*;
|
||||||
|
import android.widget.*;
|
||||||
|
import android.location.Location;
|
||||||
|
|
||||||
|
public class TabMap extends AltosDroidTab implements AltosDroidMapSourceListener {
|
||||||
|
|
||||||
|
AltosLatLon here;
|
||||||
|
|
||||||
|
private TextView mDistanceView;
|
||||||
|
private TextView mBearingLabel;
|
||||||
|
private TextView mBearingView;
|
||||||
|
private TextView mTargetLatitudeView;
|
||||||
|
private TextView mTargetLongitudeView;
|
||||||
|
private TextView mReceiverLatitudeView;
|
||||||
|
private TextView mReceiverLongitudeView;
|
||||||
|
private AltosMapOffline map_offline;
|
||||||
|
private AltosMapOnline map_online;
|
||||||
|
private View view;
|
||||||
|
private int map_source;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
view = inflater.inflate(R.layout.tab_map, container, false);
|
||||||
|
int map_source = AltosDroidPreferences.map_source();
|
||||||
|
|
||||||
|
mDistanceView = (TextView)view.findViewById(R.id.distance_value);
|
||||||
|
mBearingLabel = (TextView)view.findViewById(R.id.bearing_label);
|
||||||
|
mBearingView = (TextView)view.findViewById(R.id.bearing_value);
|
||||||
|
mTargetLatitudeView = (TextView)view.findViewById(R.id.target_lat_value);
|
||||||
|
mTargetLongitudeView = (TextView)view.findViewById(R.id.target_lon_value);
|
||||||
|
mReceiverLatitudeView = (TextView)view.findViewById(R.id.receiver_lat_value);
|
||||||
|
mReceiverLongitudeView = (TextView)view.findViewById(R.id.receiver_lon_value);
|
||||||
|
map_offline = (AltosMapOffline)view.findViewById(R.id.map_offline);
|
||||||
|
map_offline.onCreateView(altos_droid);
|
||||||
|
map_online = new AltosMapOnline(view.getContext());
|
||||||
|
map_online.onCreateView(altos_droid);
|
||||||
|
map_source_changed(AltosDroidPreferences.map_source());
|
||||||
|
AltosDroidPreferences.register_map_source_listener(this);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityCreated(Bundle savedInstanceState) {
|
||||||
|
super.onActivityCreated(savedInstanceState);
|
||||||
|
if (map_online != null)
|
||||||
|
getChildFragmentManager().beginTransaction().add(R.id.map_online, map_online.mMapFragment).commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
map_offline.onDestroyView();
|
||||||
|
map_online.onDestroyView();
|
||||||
|
AltosDroidPreferences.unregister_map_source_listener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String tab_name() { return AltosDroid.tab_map_name; }
|
||||||
|
|
||||||
|
private void center(double lat, double lon, double accuracy) {
|
||||||
|
if (map_offline != null)
|
||||||
|
map_offline.center(lat, lon, accuracy);
|
||||||
|
if (map_online != null)
|
||||||
|
map_online.center(lat, lon, accuracy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
|
||||||
|
if (from_receiver != null) {
|
||||||
|
String direction = AltosDroid.direction(from_receiver, receiver);
|
||||||
|
if (direction != null) {
|
||||||
|
mBearingLabel.setText("Direction");
|
||||||
|
mBearingView.setText(direction);
|
||||||
|
} else {
|
||||||
|
mBearingLabel.setText("Bearing");
|
||||||
|
mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
|
||||||
|
}
|
||||||
|
set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
|
||||||
|
} else {
|
||||||
|
mBearingLabel.setText("Bearing");
|
||||||
|
mBearingView.setText("");
|
||||||
|
set_value(mDistanceView, AltosConvert.distance, 6, AltosLib.MISSING);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state != null) {
|
||||||
|
if (state.gps != null) {
|
||||||
|
mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
|
||||||
|
mTargetLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receiver != null) {
|
||||||
|
double accuracy;
|
||||||
|
|
||||||
|
here = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude());
|
||||||
|
if (receiver.hasAccuracy())
|
||||||
|
accuracy = receiver.getAccuracy();
|
||||||
|
else
|
||||||
|
accuracy = 1000;
|
||||||
|
mReceiverLatitudeView.setText(AltosDroid.pos(here.lat, "N", "S"));
|
||||||
|
mReceiverLongitudeView.setText(AltosDroid.pos(here.lon, "E", "W"));
|
||||||
|
center (here.lat, here.lon, accuracy);
|
||||||
|
}
|
||||||
|
if (map_source == AltosDroidPreferences.MAP_SOURCE_OFFLINE) {
|
||||||
|
if (map_offline != null)
|
||||||
|
map_offline.show(telem_state, state, from_receiver, receiver);
|
||||||
|
} else {
|
||||||
|
if (map_online != null)
|
||||||
|
map_online.show(telem_state, state, from_receiver, receiver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void map_source_changed(int map_source) {
|
||||||
|
this.map_source = map_source;
|
||||||
|
if (map_source == AltosDroidPreferences.MAP_SOURCE_OFFLINE) {
|
||||||
|
if (map_online != null)
|
||||||
|
map_online.set_visible(false);
|
||||||
|
if (map_offline != null) {
|
||||||
|
map_offline.set_visible(true);
|
||||||
|
map_offline.show(last_telem_state, last_state, last_from_receiver, last_receiver);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (map_offline != null)
|
||||||
|
map_offline.set_visible(false);
|
||||||
|
if (map_online != null) {
|
||||||
|
map_online.set_visible(true);
|
||||||
|
map_online.show(last_telem_state, last_state, last_from_receiver, last_receiver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TabMap() {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,259 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.*;
|
||||||
|
import android.widget.*;
|
||||||
|
import android.location.Location;
|
||||||
|
|
||||||
|
public class TabPad extends AltosDroidTab {
|
||||||
|
private TextView battery_voltage_view;
|
||||||
|
private GoNoGoLights battery_lights;
|
||||||
|
|
||||||
|
private TableRow receiver_row;
|
||||||
|
private TextView receiver_voltage_view;
|
||||||
|
private TextView receiver_voltage_label;
|
||||||
|
private GoNoGoLights receiver_voltage_lights;
|
||||||
|
|
||||||
|
private TableRow apogee_row;
|
||||||
|
private TextView apogee_voltage_view;
|
||||||
|
private TextView apogee_voltage_label;
|
||||||
|
private GoNoGoLights apogee_lights;
|
||||||
|
|
||||||
|
private TableRow main_row;
|
||||||
|
private TextView main_voltage_view;
|
||||||
|
private TextView main_voltage_label;
|
||||||
|
private GoNoGoLights main_lights;
|
||||||
|
|
||||||
|
private TextView data_logging_view;
|
||||||
|
private GoNoGoLights data_logging_lights;
|
||||||
|
|
||||||
|
private TextView gps_locked_view;
|
||||||
|
private GoNoGoLights gps_locked_lights;
|
||||||
|
|
||||||
|
private TextView gps_ready_view;
|
||||||
|
private GoNoGoLights gps_ready_lights;
|
||||||
|
|
||||||
|
private TextView receiver_latitude_view;
|
||||||
|
private TextView receiver_longitude_view;
|
||||||
|
private TextView receiver_altitude_view;
|
||||||
|
|
||||||
|
private TableRow[] ignite_row = new TableRow[4];
|
||||||
|
private TextView[] ignite_voltage_view = new TextView[4];
|
||||||
|
private TextView[] ignite_voltage_label = new TextView[4];
|
||||||
|
private GoNoGoLights[] ignite_lights = new GoNoGoLights[4];
|
||||||
|
|
||||||
|
private View tilt_view;
|
||||||
|
private TextView tilt_value;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
AltosDebug.debug("TabPad onCreateView\n");
|
||||||
|
View v = inflater.inflate(R.layout.tab_pad, container, false);
|
||||||
|
battery_voltage_view = (TextView) v.findViewById(R.id.battery_voltage_value);
|
||||||
|
battery_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.battery_redled),
|
||||||
|
(ImageView) v.findViewById(R.id.battery_greenled),
|
||||||
|
getResources());
|
||||||
|
|
||||||
|
receiver_row = (TableRow) v.findViewById(R.id.receiver_row);
|
||||||
|
receiver_voltage_view = (TextView) v.findViewById(R.id.receiver_voltage_value);
|
||||||
|
receiver_voltage_label = (TextView) v.findViewById(R.id.receiver_voltage_label);
|
||||||
|
receiver_voltage_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.receiver_redled),
|
||||||
|
(ImageView) v.findViewById(R.id.receiver_greenled),
|
||||||
|
getResources());
|
||||||
|
|
||||||
|
apogee_row = (TableRow) v.findViewById(R.id.apogee_row);
|
||||||
|
apogee_voltage_view = (TextView) v.findViewById(R.id.apogee_voltage_value);
|
||||||
|
apogee_voltage_label = (TextView) v.findViewById(R.id.apogee_voltage_label);
|
||||||
|
apogee_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled),
|
||||||
|
(ImageView) v.findViewById(R.id.apogee_greenled),
|
||||||
|
getResources());
|
||||||
|
|
||||||
|
main_row = (TableRow) v.findViewById(R.id.main_row);
|
||||||
|
main_voltage_view = (TextView) v.findViewById(R.id.main_voltage_value);
|
||||||
|
main_voltage_label = (TextView) v.findViewById(R.id.main_voltage_label);
|
||||||
|
main_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled),
|
||||||
|
(ImageView) v.findViewById(R.id.main_greenled),
|
||||||
|
getResources());
|
||||||
|
|
||||||
|
data_logging_view = (TextView) v.findViewById(R.id.logging_value);
|
||||||
|
data_logging_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.logging_redled),
|
||||||
|
(ImageView) v.findViewById(R.id.logging_greenled),
|
||||||
|
getResources());
|
||||||
|
|
||||||
|
gps_locked_view = (TextView) v.findViewById(R.id.gps_locked_value);
|
||||||
|
gps_locked_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.gps_locked_redled),
|
||||||
|
(ImageView) v.findViewById(R.id.gps_locked_greenled),
|
||||||
|
getResources());
|
||||||
|
|
||||||
|
gps_ready_view = (TextView) v.findViewById(R.id.gps_ready_value);
|
||||||
|
gps_ready_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.gps_ready_redled),
|
||||||
|
(ImageView) v.findViewById(R.id.gps_ready_greenled),
|
||||||
|
getResources());
|
||||||
|
|
||||||
|
tilt_view = (View) v.findViewById(R.id.tilt_view);
|
||||||
|
tilt_value = (TextView) v.findViewById(R.id.tilt_value);
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
int row_id, view_id, label_id, lights_id;
|
||||||
|
int red_id, green_id;
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
default:
|
||||||
|
row_id = R.id.ignite_a_row;
|
||||||
|
view_id = R.id.ignite_a_voltage_value;
|
||||||
|
label_id = R.id.ignite_a_voltage_label;
|
||||||
|
red_id = R.id.ignite_a_redled;
|
||||||
|
green_id = R.id.ignite_a_greenled;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
row_id = R.id.ignite_b_row;
|
||||||
|
view_id = R.id.ignite_b_voltage_value;
|
||||||
|
label_id = R.id.ignite_b_voltage_label;
|
||||||
|
red_id = R.id.ignite_b_redled;
|
||||||
|
green_id = R.id.ignite_b_greenled;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
row_id = R.id.ignite_c_row;
|
||||||
|
view_id = R.id.ignite_c_voltage_value;
|
||||||
|
label_id = R.id.ignite_c_voltage_label;
|
||||||
|
red_id = R.id.ignite_c_redled;
|
||||||
|
green_id = R.id.ignite_c_greenled;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
row_id = R.id.ignite_d_row;
|
||||||
|
view_id = R.id.ignite_d_voltage_value;
|
||||||
|
label_id = R.id.ignite_d_voltage_label;
|
||||||
|
red_id = R.id.ignite_d_redled;
|
||||||
|
green_id = R.id.ignite_d_greenled;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ignite_row[i] = (TableRow) v.findViewById(row_id);
|
||||||
|
ignite_voltage_view[i] = (TextView) v.findViewById(view_id);
|
||||||
|
ignite_voltage_label[i] = (TextView) v.findViewById(label_id);
|
||||||
|
ignite_lights[i] = new GoNoGoLights((ImageView) v.findViewById(red_id),
|
||||||
|
(ImageView) v.findViewById(green_id),
|
||||||
|
getResources());
|
||||||
|
}
|
||||||
|
|
||||||
|
receiver_latitude_view = (TextView) v.findViewById(R.id.receiver_lat_value);
|
||||||
|
receiver_longitude_view = (TextView) v.findViewById(R.id.receiver_lon_value);
|
||||||
|
receiver_altitude_view = (TextView) v.findViewById(R.id.receiver_alt_value);
|
||||||
|
|
||||||
|
AltosDebug.debug("TabPad onCreateView done battery_voltage_view %s\n", battery_voltage_view);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String tab_name() { return AltosDroid.tab_pad_name; }
|
||||||
|
|
||||||
|
public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
|
||||||
|
AltosDebug.debug("pad show state %b bvv %s\n", state != null, battery_voltage_view);
|
||||||
|
if (state != null) {
|
||||||
|
battery_voltage_view.setText(AltosDroid.number("%1.2f V", state.battery_voltage));
|
||||||
|
battery_lights.set(state.battery_voltage >= AltosLib.ao_battery_good, state.battery_voltage == AltosLib.MISSING);
|
||||||
|
if (state.apogee_voltage == AltosLib.MISSING) {
|
||||||
|
apogee_row.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
apogee_voltage_view.setText(AltosDroid.number("%1.2f V", state.apogee_voltage));
|
||||||
|
apogee_row.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
apogee_lights.set(state.apogee_voltage >= AltosLib.ao_igniter_good, state.apogee_voltage == AltosLib.MISSING);
|
||||||
|
if (state.main_voltage == AltosLib.MISSING) {
|
||||||
|
main_row.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
main_voltage_view.setText(AltosDroid.number("%1.2f V", state.main_voltage));
|
||||||
|
main_row.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
main_lights.set(state.main_voltage >= AltosLib.ao_igniter_good, state.main_voltage == AltosLib.MISSING);
|
||||||
|
|
||||||
|
int num_igniter = state.igniter_voltage == null ? 0 : state.igniter_voltage.length;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
double voltage = i >= num_igniter ? AltosLib.MISSING : state.igniter_voltage[i];
|
||||||
|
if (voltage == AltosLib.MISSING) {
|
||||||
|
ignite_row[i].setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
ignite_voltage_view[i].setText(AltosDroid.number("%1.2f V", voltage));
|
||||||
|
ignite_row[i].setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
ignite_lights[i].set(voltage >= AltosLib.ao_igniter_good, voltage == AltosLib.MISSING);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.cal_data().flight != 0) {
|
||||||
|
if (state.state() <= AltosLib.ao_flight_pad)
|
||||||
|
data_logging_view.setText("Ready to record");
|
||||||
|
else if (state.state() < AltosLib.ao_flight_landed)
|
||||||
|
data_logging_view.setText("Recording data");
|
||||||
|
else
|
||||||
|
data_logging_view.setText("Recorded data");
|
||||||
|
} else {
|
||||||
|
data_logging_view.setText("Storage full");
|
||||||
|
}
|
||||||
|
data_logging_lights.set(state.cal_data().flight != 0, state.cal_data().flight == AltosLib.MISSING);
|
||||||
|
|
||||||
|
if (state.gps != null) {
|
||||||
|
int soln = state.gps.nsat;
|
||||||
|
int nsat = state.gps.cc_gps_sat != null ? state.gps.cc_gps_sat.length : 0;
|
||||||
|
gps_locked_view.setText(String.format("%d in soln, %d in view", soln, nsat));
|
||||||
|
gps_locked_lights.set(state.gps.locked && state.gps.nsat >= 4, false);
|
||||||
|
if (state.gps_ready)
|
||||||
|
gps_ready_view.setText("Ready");
|
||||||
|
else
|
||||||
|
gps_ready_view.setText(AltosDroid.integer("Waiting %d", state.gps_waiting));
|
||||||
|
} else
|
||||||
|
gps_locked_lights.set(false, true);
|
||||||
|
gps_ready_lights.set(state.gps_ready, state.gps == null);
|
||||||
|
|
||||||
|
double orient = state.orient();
|
||||||
|
|
||||||
|
if (orient == AltosLib.MISSING) {
|
||||||
|
tilt_view.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
tilt_value.setText(AltosDroid.number("%1.0f°", orient));
|
||||||
|
tilt_view.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (telem_state != null) {
|
||||||
|
if (telem_state.receiver_battery == AltosLib.MISSING) {
|
||||||
|
receiver_row.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
receiver_voltage_view.setText(AltosDroid.number("%1.2f V", telem_state.receiver_battery));
|
||||||
|
receiver_row.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
receiver_voltage_lights.set(telem_state.receiver_battery >= AltosLib.ao_battery_good, telem_state.receiver_battery == AltosLib.MISSING);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receiver != null) {
|
||||||
|
double altitude = AltosLib.MISSING;
|
||||||
|
if (receiver.hasAltitude())
|
||||||
|
altitude = receiver.getAltitude();
|
||||||
|
String lat_text = AltosDroid.pos(receiver.getLatitude(), "N", "S");
|
||||||
|
String lon_text = AltosDroid.pos(receiver.getLongitude(), "E", "W");
|
||||||
|
AltosDebug.debug("lat %s lon %s\n", lat_text, lon_text);
|
||||||
|
receiver_latitude_view.setText(lat_text);
|
||||||
|
receiver_longitude_view.setText(lon_text);
|
||||||
|
set_value(receiver_altitude_view, AltosConvert.height, 1, altitude);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.location.Location;
|
||||||
|
|
||||||
|
public class TabRecover extends AltosDroidTab {
|
||||||
|
private TextView mBearingView;
|
||||||
|
private TextView mDirectionView;
|
||||||
|
private TextView mDistanceView;
|
||||||
|
private TextView mTargetLatitudeView;
|
||||||
|
private TextView mTargetLongitudeView;
|
||||||
|
private TextView mReceiverLatitudeView;
|
||||||
|
private TextView mReceiverLongitudeView;
|
||||||
|
private TextView mMaxHeightView;
|
||||||
|
private TextView mMaxSpeedView;
|
||||||
|
private TextView mMaxAccelView;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
View v = inflater.inflate(R.layout.tab_recover, container, false);
|
||||||
|
|
||||||
|
mBearingView = (TextView) v.findViewById(R.id.bearing_value);
|
||||||
|
mDirectionView = (TextView) v.findViewById(R.id.direction_value);
|
||||||
|
mDistanceView = (TextView) v.findViewById(R.id.distance_value);
|
||||||
|
mTargetLatitudeView = (TextView) v.findViewById(R.id.target_lat_value);
|
||||||
|
mTargetLongitudeView = (TextView) v.findViewById(R.id.target_lon_value);
|
||||||
|
mReceiverLatitudeView = (TextView) v.findViewById(R.id.receiver_lat_value);
|
||||||
|
mReceiverLongitudeView = (TextView) v.findViewById(R.id.receiver_lon_value);
|
||||||
|
mMaxHeightView = (TextView) v.findViewById(R.id.max_height_value);
|
||||||
|
mMaxSpeedView = (TextView) v.findViewById(R.id.max_speed_value);
|
||||||
|
mMaxAccelView = (TextView) v.findViewById(R.id.max_accel_value);
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String tab_name() { return AltosDroid.tab_recover_name; }
|
||||||
|
|
||||||
|
public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
|
||||||
|
if (from_receiver != null) {
|
||||||
|
mBearingView.setText(String.format("%1.0f°", from_receiver.bearing));
|
||||||
|
set_value(mDistanceView, AltosConvert.distance, 1, from_receiver.distance);
|
||||||
|
String direction = AltosDroid.direction(from_receiver, receiver);
|
||||||
|
if (direction == null)
|
||||||
|
mDirectionView.setText("");
|
||||||
|
else
|
||||||
|
mDirectionView.setText(direction);
|
||||||
|
}
|
||||||
|
if (state != null && state.gps != null) {
|
||||||
|
mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
|
||||||
|
mTargetLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receiver != null) {
|
||||||
|
mReceiverLatitudeView.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
|
||||||
|
mReceiverLongitudeView.setText(AltosDroid.pos(receiver.getLongitude(), "E", "W"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state != null) {
|
||||||
|
set_value(mMaxHeightView, AltosConvert.height, 1, state.max_height());
|
||||||
|
set_value(mMaxAccelView, AltosConvert.accel, 1, state.max_acceleration());
|
||||||
|
set_value(mMaxSpeedView, AltosConvert.speed, 1, state.max_speed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import androidx.fragment.app.FragmentPagerAdapter;
|
||||||
|
import androidx.viewpager.widget.ViewPager;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TabHost;
|
||||||
|
import android.widget.TabWidget;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a helper class that implements the management of tabs and all
|
||||||
|
* details of connecting a ViewPager with associated TabHost. It relies on a
|
||||||
|
* trick. Normally a tab host has a simple API for supplying a View or
|
||||||
|
* Intent that each tab will show. This is not sufficient for switching
|
||||||
|
* between pages. So instead we make the content part of the tab host
|
||||||
|
* 0dp high (it is not shown) and the TabsAdapter supplies its own dummy
|
||||||
|
* view to show as the tab content. It listens to changes in tabs, and takes
|
||||||
|
* care of switch to the correct paged in the ViewPager whenever the selected
|
||||||
|
* tab changes.
|
||||||
|
*/
|
||||||
|
public class TabsAdapter extends FragmentPagerAdapter
|
||||||
|
implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {
|
||||||
|
private final Context mContext;
|
||||||
|
private final TabHost mTabHost;
|
||||||
|
private final ViewPager mViewPager;
|
||||||
|
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
|
||||||
|
private int position;
|
||||||
|
|
||||||
|
static class TabInfo {
|
||||||
|
private final String tag;
|
||||||
|
private final Class<?> clss;
|
||||||
|
private final Bundle args;
|
||||||
|
private Fragment fragment;
|
||||||
|
|
||||||
|
TabInfo(String _tag, Class<?> _class, Bundle _args, Fragment _fragment) {
|
||||||
|
tag = _tag;
|
||||||
|
clss = _class;
|
||||||
|
args = _args;
|
||||||
|
fragment = _fragment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DummyTabFactory implements TabHost.TabContentFactory {
|
||||||
|
private final Context mContext;
|
||||||
|
|
||||||
|
public DummyTabFactory(Context context) {
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public View createTabContent(String tag) {
|
||||||
|
View v = new View(mContext);
|
||||||
|
v.setMinimumWidth(0);
|
||||||
|
v.setMinimumHeight(0);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager pager) {
|
||||||
|
super(activity.getSupportFragmentManager());
|
||||||
|
mContext = activity;
|
||||||
|
mTabHost = tabHost;
|
||||||
|
mViewPager = pager;
|
||||||
|
mTabHost.setOnTabChangedListener(this);
|
||||||
|
mViewPager.setAdapter(this);
|
||||||
|
mViewPager.addOnPageChangeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args, Fragment fragment) {
|
||||||
|
tabSpec.setContent(new DummyTabFactory(mContext));
|
||||||
|
String tag = tabSpec.getTag();
|
||||||
|
|
||||||
|
TabInfo info = new TabInfo(tag, clss, args, fragment);
|
||||||
|
mTabs.add(info);
|
||||||
|
mTabHost.addTab(tabSpec);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return mTabs.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Fragment getItem(int position) {
|
||||||
|
TabInfo info = mTabs.get(position);
|
||||||
|
AltosDebug.debug("TabsAdapter.getItem(%d)", position);
|
||||||
|
if (info.fragment == null)
|
||||||
|
info.fragment = Fragment.instantiate(mContext, info.clss.getName(), info.args);
|
||||||
|
return info.fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Fragment currentItem() {
|
||||||
|
TabInfo info = mTabs.get(position);
|
||||||
|
return info.fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onTabChanged(String tabId) {
|
||||||
|
AltosDroidTab prev_frag = (AltosDroidTab) mTabs.get(position).fragment;
|
||||||
|
|
||||||
|
position = mTabHost.getCurrentTab();
|
||||||
|
|
||||||
|
AltosDroidTab cur_frag = (AltosDroidTab) mTabs.get(position).fragment;
|
||||||
|
|
||||||
|
AltosDebug.debug("TabsAdapter.onTabChanged(%s) = %d cur %s prev %s", tabId, position, cur_frag, prev_frag);
|
||||||
|
|
||||||
|
if (prev_frag != cur_frag) {
|
||||||
|
if (prev_frag != null) {
|
||||||
|
prev_frag.set_visible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This happens when the tab is selected before any of them
|
||||||
|
* have been created, like during rotation
|
||||||
|
*/
|
||||||
|
if (cur_frag != null)
|
||||||
|
cur_frag.set_visible(true);
|
||||||
|
mViewPager.setCurrentItem(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPageSelected(int position) {
|
||||||
|
// Unfortunately when TabHost changes the current tab, it kindly
|
||||||
|
// also takes care of putting focus on it when not in touch mode.
|
||||||
|
// The jerk.
|
||||||
|
// This hack tries to prevent this from pulling focus out of our
|
||||||
|
// ViewPager.
|
||||||
|
TabWidget widget = mTabHost.getTabWidget();
|
||||||
|
int oldFocusability = widget.getDescendantFocusability();
|
||||||
|
widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
|
||||||
|
mTabHost.setCurrentTab(position);
|
||||||
|
widget.setDescendantFocusability(oldFocusability);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPageScrollStateChanged(int state) {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2021 Keith Packard <keithp@keithp.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.os.Environment;
|
||||||
|
|
||||||
|
public class TelemetryLogger implements AltosLogTrace {
|
||||||
|
private TelemetryService service = null;
|
||||||
|
private AltosLink link = null;
|
||||||
|
private AltosLog logger = null;
|
||||||
|
|
||||||
|
private BroadcastReceiver mExternalStorageReceiver;
|
||||||
|
|
||||||
|
/* AltosLogTrace interface */
|
||||||
|
public void trace(String format, Object ... arguments) {
|
||||||
|
AltosDebug.debug(format, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void open_failed(File file) {
|
||||||
|
service.send_file_failed_to_clients(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TelemetryLogger(TelemetryService in_service, AltosLink in_link) {
|
||||||
|
service = in_service;
|
||||||
|
link = in_link;
|
||||||
|
|
||||||
|
startWatchingExternalStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
stopWatchingExternalStorage();
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void close() {
|
||||||
|
if (logger != null) {
|
||||||
|
AltosDebug.debug("Shutting down Telemetry Logging");
|
||||||
|
logger.close();
|
||||||
|
logger = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleExternalStorageState() {
|
||||||
|
String state = Environment.getExternalStorageState();
|
||||||
|
if (Environment.MEDIA_MOUNTED.equals(state)) {
|
||||||
|
if (logger == null) {
|
||||||
|
AltosDebug.debug("Starting up Telemetry Logging");
|
||||||
|
logger = new AltosLog(link,this);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
AltosDebug.debug("External Storage not present - stopping");
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void startWatchingExternalStorage() {
|
||||||
|
mExternalStorageReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
handleExternalStorageState();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
|
||||||
|
filter.addAction(Intent.ACTION_MEDIA_REMOVED);
|
||||||
|
service.registerReceiver(mExternalStorageReceiver, filter);
|
||||||
|
handleExternalStorageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopWatchingExternalStorage() {
|
||||||
|
service.unregisterReceiver(mExternalStorageReceiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2011 Keith Packard <keithp@keithp.com>
|
||||||
|
* Copyright © 2012 Mike Beattie <mike@ethernal.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import java.text.*;
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
import android.os.Handler;
|
||||||
|
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
|
||||||
|
public class TelemetryReader extends Thread {
|
||||||
|
|
||||||
|
int crc_errors;
|
||||||
|
|
||||||
|
Handler handler;
|
||||||
|
|
||||||
|
AltosLink link;
|
||||||
|
|
||||||
|
LinkedBlockingQueue<AltosLine> telemQueue;
|
||||||
|
|
||||||
|
public AltosTelemetry read() throws ParseException, AltosCRCException, InterruptedException, IOException {
|
||||||
|
AltosLine l = telemQueue.take();
|
||||||
|
if (l.line == null)
|
||||||
|
throw new IOException("IO error");
|
||||||
|
AltosTelemetry telem = AltosTelemetryLegacy.parse(l.line);
|
||||||
|
return telem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
link.remove_monitor(telemQueue);
|
||||||
|
link = null;
|
||||||
|
telemQueue.clear();
|
||||||
|
telemQueue = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
AltosDebug.debug("starting loop");
|
||||||
|
while (telemQueue != null) {
|
||||||
|
try {
|
||||||
|
AltosTelemetry telem = read();
|
||||||
|
telem.set_frequency(link.frequency);
|
||||||
|
handler.obtainMessage(TelemetryService.MSG_TELEMETRY, telem).sendToTarget();
|
||||||
|
} catch (ParseException pp) {
|
||||||
|
AltosDebug.error("Parse error: %d \"%s\"", pp.getErrorOffset(), pp.getMessage());
|
||||||
|
} catch (AltosCRCException ce) {
|
||||||
|
++crc_errors;
|
||||||
|
handler.obtainMessage(TelemetryService.MSG_CRC_ERROR, new Integer(crc_errors)).sendToTarget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ee) {
|
||||||
|
} catch (IOException ie) {
|
||||||
|
AltosDebug.error("IO exception in telemetry reader");
|
||||||
|
handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, link).sendToTarget();
|
||||||
|
} finally {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TelemetryReader (AltosLink in_link, Handler in_handler) {
|
||||||
|
AltosDebug.debug("connected TelemetryReader create started");
|
||||||
|
link = in_link;
|
||||||
|
handler = in_handler;
|
||||||
|
|
||||||
|
telemQueue = new LinkedBlockingQueue<AltosLine>();
|
||||||
|
link.add_monitor(telemQueue);
|
||||||
|
link.set_telemetry(AltosLib.ao_telemetry_standard);
|
||||||
|
|
||||||
|
AltosDebug.debug("connected TelemetryReader created");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,748 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2012 Mike Beattie <mike@ethernal.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import android.app.*;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.hardware.usb.*;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.*;
|
||||||
|
import android.os.Build.*;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
public class TelemetryService extends Service implements AltosIdleMonitorListener {
|
||||||
|
|
||||||
|
static final int MSG_REGISTER_CLIENT = 1;
|
||||||
|
static final int MSG_UNREGISTER_CLIENT = 2;
|
||||||
|
static final int MSG_CONNECT = 3;
|
||||||
|
static final int MSG_OPEN_USB = 4;
|
||||||
|
static final int MSG_CONNECTED = 5;
|
||||||
|
static final int MSG_CONNECT_FAILED = 6;
|
||||||
|
static final int MSG_DISCONNECTED = 7;
|
||||||
|
static final int MSG_TELEMETRY = 8;
|
||||||
|
static final int MSG_SETFREQUENCY = 9;
|
||||||
|
static final int MSG_CRC_ERROR = 10;
|
||||||
|
static final int MSG_SETBAUD = 11;
|
||||||
|
static final int MSG_DISCONNECT = 12;
|
||||||
|
static final int MSG_DELETE_SERIAL = 13;
|
||||||
|
static final int MSG_BLUETOOTH_ENABLED = 14;
|
||||||
|
static final int MSG_MONITOR_IDLE_START= 15;
|
||||||
|
static final int MSG_MONITOR_IDLE_STOP = 16;
|
||||||
|
static final int MSG_REBOOT = 17;
|
||||||
|
static final int MSG_IGNITER_QUERY = 18;
|
||||||
|
static final int MSG_IGNITER_FIRE = 19;
|
||||||
|
|
||||||
|
// Unique Identification Number for the Notification.
|
||||||
|
// We use it on Notification start, and to cancel it.
|
||||||
|
private int NOTIFICATION = R.string.telemetry_service_label;
|
||||||
|
//private NotificationManager mNM;
|
||||||
|
|
||||||
|
ArrayList<Messenger> clients = new ArrayList<Messenger>(); // Keeps track of all current registered clients.
|
||||||
|
final Handler handler = new IncomingHandler(this);
|
||||||
|
final Messenger messenger = new Messenger(handler); // Target we publish for clients to send messages to IncomingHandler.
|
||||||
|
|
||||||
|
// Name of the connected device
|
||||||
|
DeviceAddress address;
|
||||||
|
private AltosDroidLink altos_link = null;
|
||||||
|
private TelemetryReader telemetry_reader = null;
|
||||||
|
private TelemetryLogger telemetry_logger = null;
|
||||||
|
|
||||||
|
// Local Bluetooth adapter
|
||||||
|
private BluetoothAdapter bluetooth_adapter = null;
|
||||||
|
|
||||||
|
// Last data seen; send to UI when it starts
|
||||||
|
private TelemetryState telemetry_state;
|
||||||
|
|
||||||
|
// Idle monitor if active
|
||||||
|
AltosIdleMonitor idle_monitor = null;
|
||||||
|
|
||||||
|
// Igniter bits
|
||||||
|
AltosIgnite ignite = null;
|
||||||
|
boolean ignite_running;
|
||||||
|
|
||||||
|
// Handler of incoming messages from clients.
|
||||||
|
static class IncomingHandler extends Handler {
|
||||||
|
private final WeakReference<TelemetryService> service;
|
||||||
|
IncomingHandler(TelemetryService s) { service = new WeakReference<TelemetryService>(s); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
DeviceAddress address;
|
||||||
|
|
||||||
|
TelemetryService s = service.get();
|
||||||
|
AltosDroidLink bt = null;
|
||||||
|
if (s == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (msg.what) {
|
||||||
|
|
||||||
|
/* Messages from application */
|
||||||
|
case MSG_REGISTER_CLIENT:
|
||||||
|
s.add_client(msg.replyTo);
|
||||||
|
break;
|
||||||
|
case MSG_UNREGISTER_CLIENT:
|
||||||
|
s.remove_client(msg.replyTo);
|
||||||
|
break;
|
||||||
|
case MSG_CONNECT:
|
||||||
|
AltosDebug.debug("Connect command received");
|
||||||
|
address = (DeviceAddress) msg.obj;
|
||||||
|
AltosDroidPreferences.set_active_device(address);
|
||||||
|
s.start_altos_bluetooth(address, false);
|
||||||
|
break;
|
||||||
|
case MSG_OPEN_USB:
|
||||||
|
AltosDebug.debug("Open USB command received");
|
||||||
|
UsbDevice device = (UsbDevice) msg.obj;
|
||||||
|
s.start_usb(device);
|
||||||
|
break;
|
||||||
|
case MSG_DISCONNECT:
|
||||||
|
AltosDebug.debug("Disconnect command received");
|
||||||
|
s.address = null;
|
||||||
|
if (!(Boolean) msg.obj)
|
||||||
|
AltosDroidPreferences.set_active_device(null);
|
||||||
|
s.disconnect(true);
|
||||||
|
break;
|
||||||
|
case MSG_DELETE_SERIAL:
|
||||||
|
AltosDebug.debug("Delete Serial command received");
|
||||||
|
s.delete_serial((Integer) msg.obj);
|
||||||
|
break;
|
||||||
|
case MSG_SETFREQUENCY:
|
||||||
|
AltosDebug.debug("MSG_SETFREQUENCY");
|
||||||
|
s.telemetry_state.frequency = (Double) msg.obj;
|
||||||
|
if (s.idle_monitor != null) {
|
||||||
|
s.idle_monitor.set_frequency(s.telemetry_state.frequency);
|
||||||
|
} else if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) {
|
||||||
|
try {
|
||||||
|
s.altos_link.set_radio_frequency(s.telemetry_state.frequency);
|
||||||
|
s.altos_link.save_frequency();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.send_to_clients();
|
||||||
|
break;
|
||||||
|
case MSG_SETBAUD:
|
||||||
|
AltosDebug.debug("MSG_SETBAUD");
|
||||||
|
s.telemetry_state.telemetry_rate = (Integer) msg.obj;
|
||||||
|
if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) {
|
||||||
|
s.altos_link.set_telemetry_rate(s.telemetry_state.telemetry_rate);
|
||||||
|
s.altos_link.save_telemetry_rate();
|
||||||
|
}
|
||||||
|
s.send_to_clients();
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
*Messages from AltosBluetooth
|
||||||
|
*/
|
||||||
|
case MSG_CONNECTED:
|
||||||
|
AltosDebug.debug("MSG_CONNECTED");
|
||||||
|
bt = (AltosDroidLink) msg.obj;
|
||||||
|
|
||||||
|
if (bt != s.altos_link) {
|
||||||
|
AltosDebug.debug("Stale message");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
AltosDebug.debug("Connected to device");
|
||||||
|
try {
|
||||||
|
s.connected();
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MSG_CONNECT_FAILED:
|
||||||
|
AltosDebug.debug("MSG_CONNECT_FAILED");
|
||||||
|
bt = (AltosDroidLink) msg.obj;
|
||||||
|
|
||||||
|
if (bt != s.altos_link) {
|
||||||
|
AltosDebug.debug("Stale message");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (s.address != null) {
|
||||||
|
AltosDebug.debug("Connection failed... retrying");
|
||||||
|
s.start_altos_bluetooth(s.address, true);
|
||||||
|
} else {
|
||||||
|
s.disconnect(true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MSG_DISCONNECTED:
|
||||||
|
|
||||||
|
/* This can be sent by either AltosDroidLink or TelemetryReader */
|
||||||
|
AltosDebug.debug("MSG_DISCONNECTED");
|
||||||
|
bt = (AltosDroidLink) msg.obj;
|
||||||
|
|
||||||
|
if (bt != s.altos_link) {
|
||||||
|
AltosDebug.debug("Stale message");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (s.address != null) {
|
||||||
|
AltosDebug.debug("Connection lost... retrying");
|
||||||
|
s.start_altos_bluetooth(s.address, true);
|
||||||
|
} else {
|
||||||
|
s.disconnect(true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Messages from TelemetryReader
|
||||||
|
*/
|
||||||
|
case MSG_TELEMETRY:
|
||||||
|
s.telemetry((AltosTelemetry) msg.obj);
|
||||||
|
break;
|
||||||
|
case MSG_CRC_ERROR:
|
||||||
|
// forward crc error messages
|
||||||
|
s.telemetry_state.crc_errors = (Integer) msg.obj;
|
||||||
|
s.send_to_clients();
|
||||||
|
break;
|
||||||
|
case MSG_BLUETOOTH_ENABLED:
|
||||||
|
AltosDebug.debug("TelemetryService notes that BT is now enabled");
|
||||||
|
address = AltosDroidPreferences.active_device();
|
||||||
|
if (address != null && !address.address.startsWith("USB"))
|
||||||
|
s.start_altos_bluetooth(address, false);
|
||||||
|
break;
|
||||||
|
case MSG_MONITOR_IDLE_START:
|
||||||
|
AltosDebug.debug("start monitor idle");
|
||||||
|
s.start_idle_monitor();
|
||||||
|
break;
|
||||||
|
case MSG_MONITOR_IDLE_STOP:
|
||||||
|
AltosDebug.debug("stop monitor idle");
|
||||||
|
s.stop_idle_monitor();
|
||||||
|
break;
|
||||||
|
case MSG_REBOOT:
|
||||||
|
AltosDebug.debug("reboot");
|
||||||
|
s.reboot_remote();
|
||||||
|
break;
|
||||||
|
case MSG_IGNITER_QUERY:
|
||||||
|
AltosDebug.debug("igniter query");
|
||||||
|
s.igniter_query(msg.replyTo);
|
||||||
|
break;
|
||||||
|
case MSG_IGNITER_FIRE:
|
||||||
|
AltosDebug.debug("igniter fire");
|
||||||
|
s.igniter_fire((String) msg.obj);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
super.handleMessage(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle telemetry packet
|
||||||
|
*/
|
||||||
|
private void telemetry(AltosTelemetry telem) {
|
||||||
|
AltosState state;
|
||||||
|
|
||||||
|
state = telemetry_state.get(telem.serial());
|
||||||
|
if (state == null)
|
||||||
|
state = new AltosState(new AltosCalData());
|
||||||
|
telem.provide_data(state);
|
||||||
|
telemetry_state.put(telem.serial(), state);
|
||||||
|
telemetry_state.quiet = false;
|
||||||
|
if (state != null) {
|
||||||
|
AltosPreferences.set_state(state,telem.serial());
|
||||||
|
}
|
||||||
|
send_to_clients();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Construct the message to deliver to clients
|
||||||
|
*/
|
||||||
|
private Message message() {
|
||||||
|
if (telemetry_state == null)
|
||||||
|
AltosDebug.debug("telemetry_state null!");
|
||||||
|
return Message.obtain(null, AltosDroid.MSG_STATE, telemetry_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A new friend has connected
|
||||||
|
*/
|
||||||
|
private void add_client(Messenger client) {
|
||||||
|
|
||||||
|
clients.add(client);
|
||||||
|
AltosDebug.debug("Client bound to service");
|
||||||
|
|
||||||
|
/* On connect, send the current state to the new client
|
||||||
|
*/
|
||||||
|
send_to_client(client);
|
||||||
|
send_idle_mode_to_client(client);
|
||||||
|
|
||||||
|
/* If we've got an address from a previous session, then
|
||||||
|
* go ahead and try to reconnect to the device
|
||||||
|
*/
|
||||||
|
if (address != null && telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED) {
|
||||||
|
AltosDebug.debug("Reconnecting now...");
|
||||||
|
start_altos_bluetooth(address, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A client has disconnected, clean up
|
||||||
|
*/
|
||||||
|
private void remove_client(Messenger client) {
|
||||||
|
clients.remove(client);
|
||||||
|
AltosDebug.debug("Client unbound from service");
|
||||||
|
|
||||||
|
/* When the list of clients is empty, stop the service if
|
||||||
|
* we have no current telemetry source
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (clients.isEmpty() && telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED) {
|
||||||
|
AltosDebug.debug("No clients, no connection. Stopping\n");
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void send_to_client(Messenger client) {
|
||||||
|
Message m = message();
|
||||||
|
try {
|
||||||
|
client.send(m);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
AltosDebug.error("Client %s disappeared", client.toString());
|
||||||
|
remove_client(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void send_to_clients() {
|
||||||
|
for (Messenger client : clients)
|
||||||
|
send_to_client(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void send_idle_mode_to_client(Messenger client) {
|
||||||
|
Message m = Message.obtain(null, AltosDroid.MSG_IDLE_MODE, idle_monitor != null);
|
||||||
|
try {
|
||||||
|
client.send(m);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
AltosDebug.error("Client %s disappeared", client.toString());
|
||||||
|
remove_client(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void send_idle_mode_to_clients() {
|
||||||
|
for (Messenger client : clients)
|
||||||
|
send_idle_mode_to_client(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void send_file_failed_to_client(Messenger client, File f) {
|
||||||
|
Message m = Message.obtain(null, AltosDroid.MSG_FILE_FAILED, f);
|
||||||
|
try {
|
||||||
|
client.send(m);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
AltosDebug.error("Client %s disappeared", client.toString());
|
||||||
|
remove_client(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send_file_failed_to_clients(File f) {
|
||||||
|
for (Messenger client : clients)
|
||||||
|
send_file_failed_to_client(client, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void telemetry_start() {
|
||||||
|
if (telemetry_reader == null && idle_monitor == null && !ignite_running) {
|
||||||
|
telemetry_reader = new TelemetryReader(altos_link, handler);
|
||||||
|
telemetry_reader.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void telemetry_stop() {
|
||||||
|
if (telemetry_reader != null) {
|
||||||
|
AltosDebug.debug("disconnect(): stopping TelemetryReader");
|
||||||
|
telemetry_reader.interrupt();
|
||||||
|
try {
|
||||||
|
telemetry_reader.join();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
telemetry_reader = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disconnect(boolean notify) {
|
||||||
|
AltosDebug.debug("disconnect(): begin");
|
||||||
|
|
||||||
|
telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED;
|
||||||
|
telemetry_state.address = null;
|
||||||
|
|
||||||
|
if (idle_monitor != null)
|
||||||
|
stop_idle_monitor();
|
||||||
|
|
||||||
|
if (altos_link != null)
|
||||||
|
altos_link.closing();
|
||||||
|
|
||||||
|
stop_receiver_voltage_timer();
|
||||||
|
|
||||||
|
telemetry_stop();
|
||||||
|
if (telemetry_logger != null) {
|
||||||
|
AltosDebug.debug("disconnect(): stopping TelemetryLogger");
|
||||||
|
telemetry_logger.stop();
|
||||||
|
telemetry_logger = null;
|
||||||
|
}
|
||||||
|
if (altos_link != null) {
|
||||||
|
AltosDebug.debug("disconnect(): stopping AltosDroidLink");
|
||||||
|
altos_link.close();
|
||||||
|
altos_link = null;
|
||||||
|
ignite = null;
|
||||||
|
}
|
||||||
|
telemetry_state.config = null;
|
||||||
|
if (notify) {
|
||||||
|
AltosDebug.debug("disconnect(): send message to clients");
|
||||||
|
send_to_clients();
|
||||||
|
if (clients.isEmpty()) {
|
||||||
|
AltosDebug.debug("disconnect(): no clients, terminating");
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void start_usb(UsbDevice device) {
|
||||||
|
AltosUsb d = new AltosUsb(this, device, handler);
|
||||||
|
|
||||||
|
if (d != null) {
|
||||||
|
disconnect(false);
|
||||||
|
altos_link = d;
|
||||||
|
try {
|
||||||
|
connected();
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void delete_serial(int serial) {
|
||||||
|
telemetry_state.remove(serial);
|
||||||
|
AltosPreferences.remove_state(serial);
|
||||||
|
send_to_clients();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void start_altos_bluetooth(DeviceAddress address, boolean pause) {
|
||||||
|
if (bluetooth_adapter == null || !bluetooth_adapter.isEnabled() || address.address == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
disconnect(false);
|
||||||
|
|
||||||
|
// Get the BluetoothDevice object
|
||||||
|
BluetoothDevice device = bluetooth_adapter.getRemoteDevice(address.address);
|
||||||
|
|
||||||
|
this.address = address;
|
||||||
|
AltosDebug.debug("start_altos_bluetooth(): Connecting to %s (%s)", device.getName(), device.getAddress());
|
||||||
|
altos_link = new AltosBluetooth(device, handler, pause);
|
||||||
|
telemetry_state.connect = TelemetryState.CONNECT_CONNECTING;
|
||||||
|
telemetry_state.address = address;
|
||||||
|
send_to_clients();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void start_idle_monitor() {
|
||||||
|
if (altos_link != null && idle_monitor == null) {
|
||||||
|
telemetry_stop();
|
||||||
|
idle_monitor = new AltosIdleMonitor(this, altos_link, true, false);
|
||||||
|
idle_monitor.set_callsign(AltosPreferences.callsign());
|
||||||
|
idle_monitor.set_frequency(telemetry_state.frequency);
|
||||||
|
telemetry_state.idle_mode = true;
|
||||||
|
idle_monitor.start();
|
||||||
|
send_idle_mode_to_clients();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stop_idle_monitor() {
|
||||||
|
if (idle_monitor != null) {
|
||||||
|
try {
|
||||||
|
idle_monitor.abort();
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
}
|
||||||
|
idle_monitor = null;
|
||||||
|
telemetry_state.idle_mode = false;
|
||||||
|
telemetry_start();
|
||||||
|
send_idle_mode_to_clients();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reboot_remote() {
|
||||||
|
if (altos_link != null) {
|
||||||
|
stop_idle_monitor();
|
||||||
|
try {
|
||||||
|
altos_link.start_remote();
|
||||||
|
altos_link.printf("r eboot\n");
|
||||||
|
altos_link.flush_output();
|
||||||
|
} catch (TimeoutException te) {
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
altos_link.stop_remote();
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensure_ignite() {
|
||||||
|
if (ignite == null)
|
||||||
|
ignite = new AltosIgnite(altos_link, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void igniter_query(Messenger client) {
|
||||||
|
ensure_ignite();
|
||||||
|
HashMap<String,Integer> status_map = null;
|
||||||
|
ignite_running = true;
|
||||||
|
try {
|
||||||
|
stop_idle_monitor();
|
||||||
|
try {
|
||||||
|
status_map = ignite.status();
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
AltosDebug.debug("ignite.status interrupted");
|
||||||
|
} catch (TimeoutException te) {
|
||||||
|
AltosDebug.debug("ignite.status timeout");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
ignite_running = false;
|
||||||
|
}
|
||||||
|
Message m = Message.obtain(null, AltosDroid.MSG_IGNITER_STATUS, status_map);
|
||||||
|
try {
|
||||||
|
client.send(m);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void igniter_fire(String igniter) {
|
||||||
|
ensure_ignite();
|
||||||
|
ignite_running = true;
|
||||||
|
stop_idle_monitor();
|
||||||
|
try {
|
||||||
|
ignite.fire(igniter);
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
} finally {
|
||||||
|
ignite_running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timer for receiver battery voltage monitoring
|
||||||
|
Timer receiver_voltage_timer;
|
||||||
|
|
||||||
|
private void update_receiver_voltage() {
|
||||||
|
if (altos_link != null && idle_monitor == null && !ignite_running) {
|
||||||
|
try {
|
||||||
|
double voltage = altos_link.monitor_battery();
|
||||||
|
telemetry_state.receiver_battery = voltage;
|
||||||
|
send_to_clients();
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stop_receiver_voltage_timer() {
|
||||||
|
if (receiver_voltage_timer != null) {
|
||||||
|
receiver_voltage_timer.cancel();
|
||||||
|
receiver_voltage_timer.purge();
|
||||||
|
receiver_voltage_timer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void start_receiver_voltage_timer() {
|
||||||
|
if (receiver_voltage_timer == null && altos_link.has_monitor_battery()) {
|
||||||
|
receiver_voltage_timer = new Timer();
|
||||||
|
receiver_voltage_timer.scheduleAtFixedRate(new TimerTask() { public void run() {update_receiver_voltage();}}, 1000L, 10000L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void connected() throws InterruptedException {
|
||||||
|
AltosDebug.debug("connected top");
|
||||||
|
AltosDebug.check_ui("connected\n");
|
||||||
|
try {
|
||||||
|
if (altos_link == null)
|
||||||
|
throw new InterruptedException("no bluetooth");
|
||||||
|
telemetry_state.config = altos_link.config_data();
|
||||||
|
altos_link.set_radio_frequency(telemetry_state.frequency);
|
||||||
|
altos_link.set_telemetry_rate(telemetry_state.telemetry_rate);
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
// If this timed out, then we really want to retry it, but
|
||||||
|
// probably safer to just retry the connection from scratch.
|
||||||
|
AltosDebug.debug("connected timeout");
|
||||||
|
if (address != null) {
|
||||||
|
AltosDebug.debug("connected timeout, retrying");
|
||||||
|
start_altos_bluetooth(address, true);
|
||||||
|
} else {
|
||||||
|
handler.obtainMessage(MSG_CONNECT_FAILED).sendToTarget();
|
||||||
|
disconnect(true);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AltosDebug.debug("connected bluetooth configured");
|
||||||
|
telemetry_state.connect = TelemetryState.CONNECT_CONNECTED;
|
||||||
|
telemetry_state.address = address;
|
||||||
|
|
||||||
|
telemetry_start();
|
||||||
|
|
||||||
|
AltosDebug.debug("connected TelemetryReader started");
|
||||||
|
|
||||||
|
telemetry_logger = new TelemetryLogger(this, altos_link);
|
||||||
|
|
||||||
|
start_receiver_voltage_timer();
|
||||||
|
|
||||||
|
AltosDebug.debug("Notify UI of connection");
|
||||||
|
|
||||||
|
send_to_clients();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
|
||||||
|
AltosDebug.init(this);
|
||||||
|
|
||||||
|
// Initialise preferences
|
||||||
|
AltosDroidPreferences.init(this);
|
||||||
|
|
||||||
|
// Get local Bluetooth adapter
|
||||||
|
bluetooth_adapter = BluetoothAdapter.getDefaultAdapter();
|
||||||
|
|
||||||
|
telemetry_state = new TelemetryState();
|
||||||
|
|
||||||
|
// Create a reference to the NotificationManager so that we can update our notifcation text later
|
||||||
|
//mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
|
||||||
|
|
||||||
|
telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED;
|
||||||
|
telemetry_state.address = null;
|
||||||
|
|
||||||
|
/* Pull the saved state information out of the preferences database
|
||||||
|
*/
|
||||||
|
ArrayList<Integer> serials = AltosPreferences.list_states();
|
||||||
|
|
||||||
|
telemetry_state.latest_serial = AltosPreferences.latest_state();
|
||||||
|
|
||||||
|
telemetry_state.quiet = true;
|
||||||
|
|
||||||
|
AltosDebug.debug("latest serial %d\n", telemetry_state.latest_serial);
|
||||||
|
|
||||||
|
for (int serial : serials) {
|
||||||
|
AltosState saved_state = AltosPreferences.state(serial);
|
||||||
|
if (saved_state != null) {
|
||||||
|
AltosDebug.debug("recovered old state serial %d flight %d",
|
||||||
|
serial,
|
||||||
|
saved_state.cal_data().flight);
|
||||||
|
if (saved_state.gps != null)
|
||||||
|
AltosDebug.debug("\tposition %f,%f",
|
||||||
|
saved_state.gps.lat,
|
||||||
|
saved_state.gps.lon);
|
||||||
|
telemetry_state.put(serial, saved_state);
|
||||||
|
} else {
|
||||||
|
AltosDebug.debug("Failed to recover state for %d", serial);
|
||||||
|
AltosPreferences.remove_state(serial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String createNotificationChannel(String channelId, String channelName) {
|
||||||
|
NotificationChannel chan = new NotificationChannel(
|
||||||
|
channelId, channelName, NotificationManager.IMPORTANCE_NONE);
|
||||||
|
chan.setLightColor(Color.BLUE);
|
||||||
|
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
|
||||||
|
NotificationManager service = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
|
service.createNotificationChannel(chan);
|
||||||
|
return channelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
AltosDebug.debug("Received start id %d: %s", startId, intent);
|
||||||
|
int flag;
|
||||||
|
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= 31) // android.os.Build.VERSION_CODES.S
|
||||||
|
flag = 33554432; // PendingIntent.FLAG_MUTABLE
|
||||||
|
else
|
||||||
|
flag = 0;
|
||||||
|
|
||||||
|
// The PendingIntent to launch our activity if the user selects this notification
|
||||||
|
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
|
||||||
|
new Intent(this, AltosDroid.class), flag);
|
||||||
|
|
||||||
|
String channelId =
|
||||||
|
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||||
|
? createNotificationChannel("altosdroid_telemetry", "AltosDroid Telemetry Service")
|
||||||
|
: "";
|
||||||
|
|
||||||
|
// Create notification to be displayed while the service runs
|
||||||
|
Notification notification = new NotificationCompat.Builder(this, channelId)
|
||||||
|
.setContentTitle(getText(R.string.telemetry_service_label))
|
||||||
|
.setContentText(getText(R.string.telemetry_service_started))
|
||||||
|
.setContentIntent(contentIntent)
|
||||||
|
.setWhen(System.currentTimeMillis())
|
||||||
|
.setOngoing(true)
|
||||||
|
.setSmallIcon(R.drawable.am_status_c)
|
||||||
|
// .setLargeIcon(R.drawable.am_status_c)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Move us into the foreground.
|
||||||
|
startForeground(NOTIFICATION, notification);
|
||||||
|
|
||||||
|
/* Start bluetooth if we don't have a connection already */
|
||||||
|
if (intent != null &&
|
||||||
|
(telemetry_state.connect == TelemetryState.CONNECT_NONE ||
|
||||||
|
telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED))
|
||||||
|
{
|
||||||
|
String action = intent.getAction();
|
||||||
|
|
||||||
|
if (action.equals(AltosDroid.ACTION_BLUETOOTH)) {
|
||||||
|
DeviceAddress address = AltosDroidPreferences.active_device();
|
||||||
|
if (address != null && !address.address.startsWith("USB"))
|
||||||
|
start_altos_bluetooth(address, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want this service to continue running until it is explicitly
|
||||||
|
// stopped, so return sticky.
|
||||||
|
return START_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
|
||||||
|
// Stop the bluetooth Comms threads
|
||||||
|
disconnect(true);
|
||||||
|
|
||||||
|
// Demote us from the foreground, and cancel the persistent notification.
|
||||||
|
stopForeground(true);
|
||||||
|
|
||||||
|
// Tell the user we stopped.
|
||||||
|
Toast.makeText(this, R.string.telemetry_service_stopped, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return messenger.getBinder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AltosIdleMonitorListener */
|
||||||
|
public void update(AltosState state, AltosListenerState listener_state) {
|
||||||
|
if (state != null)
|
||||||
|
AltosDebug.debug("update call %s freq %7.3f", state.cal_data().callsign, state.frequency);
|
||||||
|
telemetry_state.put(state.cal_data().serial, state);
|
||||||
|
telemetry_state.receiver_battery = listener_state.battery;
|
||||||
|
send_to_clients();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void failed() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void error(String reason) {
|
||||||
|
stop_idle_monitor();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2012 Mike Beattie <mike@ethernal.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
public class TelemetryState {
|
||||||
|
public static final int CONNECT_NONE = 0;
|
||||||
|
public static final int CONNECT_DISCONNECTED = 1;
|
||||||
|
public static final int CONNECT_CONNECTING = 2;
|
||||||
|
public static final int CONNECT_CONNECTED = 3;
|
||||||
|
|
||||||
|
int connect;
|
||||||
|
DeviceAddress address;
|
||||||
|
AltosConfigData config;
|
||||||
|
int crc_errors;
|
||||||
|
double receiver_battery;
|
||||||
|
double frequency;
|
||||||
|
int telemetry_rate;
|
||||||
|
|
||||||
|
boolean idle_mode;
|
||||||
|
boolean quiet;
|
||||||
|
|
||||||
|
private HashMap<Integer,AltosState> states;
|
||||||
|
|
||||||
|
int latest_serial;
|
||||||
|
long latest_received_time;
|
||||||
|
|
||||||
|
public void put(int serial, AltosState state) {
|
||||||
|
long received_time = state.received_time;
|
||||||
|
if (received_time > latest_received_time || latest_serial == 0) {
|
||||||
|
latest_serial = serial;
|
||||||
|
latest_received_time = received_time;
|
||||||
|
}
|
||||||
|
states.put(serial, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AltosState get(int serial) {
|
||||||
|
if (states.containsKey(serial))
|
||||||
|
return states.get(serial);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(int serial) {
|
||||||
|
states.remove((Integer) serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Integer> keySet() {
|
||||||
|
return states.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<AltosState> values() {
|
||||||
|
return states.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsKey(int serial) {
|
||||||
|
return states.containsKey(serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TelemetryState() {
|
||||||
|
connect = CONNECT_NONE;
|
||||||
|
config = null;
|
||||||
|
states = new HashMap<Integer,AltosState>();
|
||||||
|
crc_errors = 0;
|
||||||
|
receiver_battery = AltosLib.MISSING;
|
||||||
|
frequency = AltosPreferences.frequency(0);
|
||||||
|
telemetry_rate = AltosPreferences.telemetry_rate(0);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,201 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2020 Keith Packard <keithp@keithp.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.altusmetrum.AltosDroid;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.os.Messenger;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import android.view.*;
|
||||||
|
import android.widget.*;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.location.LocationManager;
|
||||||
|
import android.location.LocationListener;
|
||||||
|
import android.hardware.usb.*;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
|
import org.altusmetrum.altoslib_14.*;
|
||||||
|
|
||||||
|
public class Tracker implements CharSequence, Comparable, Parcelable {
|
||||||
|
int serial;
|
||||||
|
String call;
|
||||||
|
double frequency;
|
||||||
|
long received_time;
|
||||||
|
String display;
|
||||||
|
|
||||||
|
private void make_display() {
|
||||||
|
if (frequency == 0.0)
|
||||||
|
display = "Auto";
|
||||||
|
else if (frequency == AltosLib.MISSING) {
|
||||||
|
display = String.format("%-8.8s %6d", call, serial);
|
||||||
|
} else {
|
||||||
|
display = String.format("%-8.8s %7.3f %6d", call, frequency, serial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tracker(int serial, String call, double frequency, long received_time) {
|
||||||
|
if (call == null)
|
||||||
|
call = "none";
|
||||||
|
|
||||||
|
this.serial = serial;
|
||||||
|
this.call = call;
|
||||||
|
this.frequency = frequency;
|
||||||
|
this.received_time = received_time;
|
||||||
|
make_display();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tracker(int serial, String call, double frequency) {
|
||||||
|
this(serial, call, frequency, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tracker(AltosState s) {
|
||||||
|
this(s == null ? 0 : s.cal_data().serial,
|
||||||
|
s == null ? null : s.cal_data().callsign,
|
||||||
|
s == null ? 0.0 : s.frequency,
|
||||||
|
s == null ? 0 : s.received_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CharSequence */
|
||||||
|
public char charAt(int index) {
|
||||||
|
return display.charAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int length() {
|
||||||
|
return display.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CharSequence subSequence(int start, int end) throws IndexOutOfBoundsException {
|
||||||
|
return display.subSequence(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return display.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Comparable */
|
||||||
|
public int compareTo (Object other) {
|
||||||
|
Tracker o = (Tracker) other;
|
||||||
|
if (frequency == 0.0) {
|
||||||
|
if (o.frequency == 0.0)
|
||||||
|
return 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (o.frequency == 0.0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
int a = serial - o.serial;
|
||||||
|
int b = call.compareTo(o.call);
|
||||||
|
int c = (int) Math.signum(frequency - o.frequency);
|
||||||
|
|
||||||
|
if (b != 0)
|
||||||
|
return b;
|
||||||
|
if (c != 0)
|
||||||
|
return c;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parcelable */
|
||||||
|
|
||||||
|
public int describeContents() {
|
||||||
|
AltosDebug.debug("describe contents %d", serial);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeToParcel(Parcel out, int flags) {
|
||||||
|
AltosDebug.debug("write to parcel %s", display);
|
||||||
|
out.writeInt(serial);
|
||||||
|
out.writeString(call);
|
||||||
|
out.writeDouble(frequency);
|
||||||
|
out.writeLong(received_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Parcelable.Creator<Tracker> CREATOR
|
||||||
|
= new Parcelable.Creator<Tracker>() {
|
||||||
|
public Tracker createFromParcel(Parcel in) {
|
||||||
|
AltosDebug.debug("createFromParcel");
|
||||||
|
return new Tracker(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tracker[] newArray(int size) {
|
||||||
|
AltosDebug.debug("newArray %d", size);
|
||||||
|
return new Tracker[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* newer (-1), same (0), older(1) */
|
||||||
|
public int compareAge(Tracker o) {
|
||||||
|
if (received_time == o.received_time)
|
||||||
|
return 0;
|
||||||
|
if (received_time == 0)
|
||||||
|
return -1;
|
||||||
|
if (o.received_time == 0)
|
||||||
|
return 1;
|
||||||
|
if (received_time > o.received_time)
|
||||||
|
return -1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compareCall(Tracker o) {
|
||||||
|
int v = call.compareTo(o.call);
|
||||||
|
if (v == 0)
|
||||||
|
return v;
|
||||||
|
if (call.equals("auto"))
|
||||||
|
return -1;
|
||||||
|
if (o.call.equals("auto"))
|
||||||
|
return 1;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compareSerial(Tracker o) {
|
||||||
|
return serial - o.serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compareFrequency(Tracker o) {
|
||||||
|
return (int) Math.signum(frequency - o.frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tracker(Parcel in) {
|
||||||
|
serial = in.readInt();
|
||||||
|
call = in.readString();
|
||||||
|
frequency = in.readDouble();
|
||||||
|
received_time = in.readLong();
|
||||||
|
make_display();
|
||||||
|
AltosDebug.debug("Create from parcel %s", display);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
|||||||
|
all:
|
||||||
|
make -C ../../../../../../.. $@
|
||||||
|
|
||||||
|
%:
|
||||||
|
make -C ../../../../../../.. $@
|
BIN
altosdroid/app/src/main/res/drawable-hdpi/am_status_c.png
Normal file
After Width: | Height: | Size: 994 B |
BIN
altosdroid/app/src/main/res/drawable-hdpi/am_status_g.png
Normal file
After Width: | Height: | Size: 804 B |
BIN
altosdroid/app/src/main/res/drawable-hdpi/app_icon.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 2.0 KiB |
BIN
altosdroid/app/src/main/res/drawable-mdpi/am_status_c.png
Normal file
After Width: | Height: | Size: 703 B |
BIN
altosdroid/app/src/main/res/drawable-mdpi/am_status_g.png
Normal file
After Width: | Height: | Size: 595 B |
After Width: | Height: | Size: 1.2 KiB |
BIN
altosdroid/app/src/main/res/drawable/app_icon.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
altosdroid/app/src/main/res/drawable/pad.png
Normal file
After Width: | Height: | Size: 584 B |
BIN
altosdroid/app/src/main/res/drawable/rocket.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
199
altosdroid/app/src/main/res/layout/altosdroid.xml
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2012-2013 Mike Beattie <mike@ethernal.org>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:orientation="vertical" >
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:baselineAligned="true"
|
||||||
|
android:orientation="horizontal" >
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/callsign_container"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/callsign_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/callsign_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/callsign_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/callsign_label"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/serial_container"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/serial_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/serial_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/serial_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/serial_label"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/flight_container"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/flight_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/flight_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/flight_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/flight_label"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/state_container"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/state_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/state_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/state_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/state_label"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/rssi_container"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/rssi_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/rssi_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/rssi_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/rssi_label"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/age_container"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/age_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/age_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/age_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/age_label"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</RelativeLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TabHost
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@android:id/tabhost"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1" >
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical" >
|
||||||
|
|
||||||
|
<TabWidget
|
||||||
|
android:id="@android:id/tabs"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:orientation="horizontal" />
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@android:id/tabcontent"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="0" />
|
||||||
|
|
||||||
|
<org.altusmetrum.AltosDroid.AltosViewPager
|
||||||
|
android:id="@+id/pager"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
</LinearLayout>
|
||||||
|
</TabHost>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/version"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="10dip"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:gravity="bottom|right"
|
||||||
|
android:textSize="7sp"
|
||||||
|
android:typeface="monospace" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
39
altosdroid/app/src/main/res/layout/custom_title.xml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2009 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
>
|
||||||
|
<TextView android:id="@+id/title_left_text"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:singleLine="true"
|
||||||
|
style="?android:attr/windowTitleStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
/>
|
||||||
|
<TextView android:id="@+id/title_right_text"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:textColor="#fff"
|
||||||
|
android:layout_weight="1"
|
||||||
|
/>
|
||||||
|
</RelativeLayout>
|
63
altosdroid/app/src/main/res/layout/device_list.xml
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2016 Keith Packard <keithp@keithp.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
>
|
||||||
|
<Button android:id="@+id/button_scan"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/button_scan"
|
||||||
|
/>
|
||||||
|
<TextView android:id="@+id/title_new_devices"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/title_other_devices"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:background="#666"
|
||||||
|
android:textColor="#fff"
|
||||||
|
android:paddingLeft="5dp"
|
||||||
|
/>
|
||||||
|
<ListView android:id="@+id/new_devices"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:fadeScrollbars="false"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
/>
|
||||||
|
<TextView android:id="@+id/title_paired_devices"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/title_paired_devices"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:background="#666"
|
||||||
|
android:textColor="#fff"
|
||||||
|
android:paddingLeft="5dp"
|
||||||
|
/>
|
||||||
|
<ListView android:id="@+id/paired_devices"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:fadeScrollbars="false"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
20
altosdroid/app/src/main/res/layout/device_name.xml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2009 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="5dp"
|
||||||
|
/>
|
34
altosdroid/app/src/main/res/layout/frequency.xml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2016 Keith Packard <keithp@keithp.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/frequency"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
67
altosdroid/app/src/main/res/layout/idle_mode.xml
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2016 Keith Packard <keithp@keithp.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
>
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
<TextView android:id="@+id/set_callsign_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/set_callsign_label"
|
||||||
|
/>
|
||||||
|
<EditText android:id="@+id/set_callsign"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:hint="@string/set_callsign_label"/>
|
||||||
|
</LinearLayout>
|
||||||
|
<TextView android:id="@+id/frequency"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text=""
|
||||||
|
/>
|
||||||
|
<Button android:id="@+id/connect_idle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/connect_idle"
|
||||||
|
/>
|
||||||
|
<Button android:id="@+id/disconnect_idle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/disconnect_idle"
|
||||||
|
/>
|
||||||
|
<Button android:id="@+id/reboot_idle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/reboot_idle"
|
||||||
|
/>
|
||||||
|
<Button android:id="@+id/igniters_idle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/igniters_idle"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
39
altosdroid/app/src/main/res/layout/igniter_status.xml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2016 Keith Packard <keithp@keithp.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<RelativeLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/igniter_status"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/igniter_name"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:layout_alignParentLeft="@+id/igniter_status"
|
||||||
|
/>
|
||||||
|
</RelativeLayout>
|
49
altosdroid/app/src/main/res/layout/igniters.xml
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2016 Keith Packard <keithp@keithp.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
>
|
||||||
|
|
||||||
|
<ListView android:id="@+id/igniters"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:fadeScrollbars="false"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
android:choiceMode="singleChoice"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ToggleButton android:id="@+id/igniter_arm"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textOn="@string/igniter_armed"
|
||||||
|
android:textOff="@string/igniter_arm"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button android:id="@+id/igniter_fire"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/igniter_fire"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
97
altosdroid/app/src/main/res/layout/manage_frequencies.xml
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2016 Keith Packard <keithp@keithp.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/set_layout"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
>
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/set_frequency"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:hint="@string/frequency"
|
||||||
|
android:inputType="number|numberDecimal"/>
|
||||||
|
/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/mhz"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:text="@string/mhz"
|
||||||
|
/>
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/set_description"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:padding="10dp"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:hint="@string/description"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
<Button android:id="@+id/set"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/set"
|
||||||
|
android:layout_weight="1"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button android:id="@+id/remove"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/remove"
|
||||||
|
android:layout_weight="1"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button android:id="@+id/done"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/done"
|
||||||
|
android:layout_weight="1"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ListView android:id="@+id/frequencies"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:fadeScrollbars="false"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
android:choiceMode="singleChoice"
|
||||||
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
132
altosdroid/app/src/main/res/layout/map_preload.xml
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright © 2015 Keith Packard <keithp@keithp.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
>
|
||||||
|
<ScrollView android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<LinearLayout android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
<TextView android:id="@+id/preload_site_label"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/preload_site_label"
|
||||||
|
/>
|
||||||
|
<Spinner android:id="@+id/preload_site_list"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:prompt="@string/preload_site_label"
|
||||||
|
android:spinnerMode="dropdown"
|
||||||
|
/>
|
||||||
|
<TextView android:id="@+id/preload_latitude_label"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/preload_latitude_label"
|
||||||
|
/>
|
||||||
|
<EditText android:id="@+id/preload_latitude"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/preload_latitude_label"
|
||||||
|
android:inputType="number|numberSigned|numberDecimal"/>
|
||||||
|
<TextView android:id="@+id/preload_longitude_label"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/preload_longitude_label"
|
||||||
|
/>
|
||||||
|
<EditText android:id="@+id/preload_longitude"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/preload_longitude_label"
|
||||||
|
android:inputType="number|numberSigned|numberDecimal"/>
|
||||||
|
<TextView android:id="@+id/preload_types"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/preload_types"
|
||||||
|
/>
|
||||||
|
<!--
|
||||||
|
<CheckBox android:id="@+id/preload_hybrid"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/preload_hybrid"
|
||||||
|
/>
|
||||||
|
<CheckBox android:id="@+id/preload_satellite"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/preload_satellite"
|
||||||
|
/>
|
||||||
|
<CheckBox android:id="@+id/preload_roadmap"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/preload_roadmap"
|
||||||
|
/>
|
||||||
|
<CheckBox android:id="@+id/preload_terrain"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/preload_terrain"
|
||||||
|
/>
|
||||||
|
-->
|
||||||
|
<TextView android:id="@+id/preload_min_zoom_label"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/preload_min_zoom"
|
||||||
|
/>
|
||||||
|
<Spinner android:id="@+id/preload_min_zoom"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:prompt="@string/preload_min_zoom"
|
||||||
|
android:spinnerMode="dropdown"
|
||||||
|
/>
|
||||||
|
<TextView android:id="@+id/preload_max_zoom_label"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/preload_max_zoom"
|
||||||
|
/>
|
||||||
|
<Spinner android:id="@+id/preload_max_zoom"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:prompt="@string/preload_max_zoom"
|
||||||
|
android:spinnerMode="dropdown"
|
||||||
|
/>
|
||||||
|
<TextView android:id="@+id/preload_radius_label"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/preload_radius"
|
||||||
|
/>
|
||||||
|
<Spinner android:id="@+id/preload_radius"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:prompt="@string/preload_radius"
|
||||||
|
android:spinnerMode="dropdown"
|
||||||
|
/>
|
||||||
|
<Button android:id="@+id/preload_load"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/preload_load"
|
||||||
|
/>
|
||||||
|
<ProgressBar android:id="@+id/preload_progress"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@android:style/Widget.ProgressBar.Horizontal"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
|
</LinearLayout>
|
48
altosdroid/app/src/main/res/layout/map_type.xml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright © 2015 Keith Packard <keithp@keithp.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
>
|
||||||
|
<Button android:id="@+id/map_type_hybrid"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/preload_hybrid"
|
||||||
|
android:onClick="selectType"
|
||||||
|
/>
|
||||||
|
<Button android:id="@+id/map_type_satellite"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/preload_satellite"
|
||||||
|
android:onClick="selectType"
|
||||||
|
/>
|
||||||
|
<Button android:id="@+id/map_type_roadmap"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/preload_roadmap"
|
||||||
|
android:onClick="selectType"
|
||||||
|
/>
|
||||||
|
<Button android:id="@+id/map_type_terrain"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/preload_terrain"
|
||||||
|
android:onClick="selectType"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
144
altosdroid/app/src/main/res/layout/setup.xml
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2016 Keith Packard <keithp@keithp.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
>
|
||||||
|
<TableLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:stretchColumns="2,3"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/select_rate_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/telemetry_rate"
|
||||||
|
/>
|
||||||
|
<Spinner android:id="@+id/select_rate"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:prompt="@string/telemetry_rate"
|
||||||
|
android:spinnerMode="dropdown"
|
||||||
|
/>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/set_units_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/set_units"
|
||||||
|
/>
|
||||||
|
<Spinner android:id="@+id/set_units"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:prompt="@string/set_units"
|
||||||
|
android:spinnerMode="dropdown"
|
||||||
|
/>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/font_size_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/font_size"
|
||||||
|
/>
|
||||||
|
<Spinner android:id="@+id/font_size"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:prompt="@string/font_size"
|
||||||
|
android:spinnerMode="dropdown"
|
||||||
|
/>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/map_type_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/map_type"
|
||||||
|
/>
|
||||||
|
<Spinner android:id="@+id/map_type"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:prompt="@string/map_type"
|
||||||
|
android:spinnerMode="dropdown"
|
||||||
|
/>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/map_source_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/map_source"
|
||||||
|
/>
|
||||||
|
<Spinner android:id="@+id/map_source"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:prompt="@string/map_source"
|
||||||
|
android:spinnerMode="dropdown"
|
||||||
|
/>
|
||||||
|
</TableRow>
|
||||||
|
</TableLayout>
|
||||||
|
<Button android:id="@+id/preload_maps"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/preload_maps"
|
||||||
|
/>
|
||||||
|
<Button android:id="@+id/manage_frequencies"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/manage_frequencies"
|
||||||
|
/>
|
||||||
|
<Button android:id="@+id/done"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/done"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
430
altosdroid/app/src/main/res/layout/tab_flight.xml
Normal file
@ -0,0 +1,430 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
-->
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical" >
|
||||||
|
|
||||||
|
<TableLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:stretchColumns="2,3"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/speed_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_column="2"
|
||||||
|
android:text="@string/speed_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/speed_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/height_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_column="2"
|
||||||
|
android:text="@string/height_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/height_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/altitude_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_column="2"
|
||||||
|
android:text="@string/altitude_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/altitude_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:id="@+id/tilt_view"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tilt_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_column="2"
|
||||||
|
android:text="@string/tilt_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tilt_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/max_speed_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_column="2"
|
||||||
|
android:text="@string/max_speed_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/max_speed_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/max_height_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_column="2"
|
||||||
|
android:text="@string/max_height_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/max_height_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/max_altitude_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_column="2"
|
||||||
|
android:text="@string/max_altitude_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/max_altitude_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/elevation_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_column="2"
|
||||||
|
android:text="@string/elevation_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/elevation_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/range_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_column="2"
|
||||||
|
android:text="@string/range_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/range_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/bearing_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_column="2"
|
||||||
|
android:text="@string/bearing_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/bearing_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/compass_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_column="3"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/distance_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_column="2"
|
||||||
|
android:text="@string/gnd_distance_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/distance_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/lat_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_column="2"
|
||||||
|
android:text="@string/latitude_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/lat_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/lon_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_column="2"
|
||||||
|
android:text="@string/longitude_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/lon_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:id="@+id/apogee_view"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/apogee_redled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:contentDescription="@string/apogee_voltage_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/apogee_greenled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:contentDescription="@string/apogee_voltage_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/apogee_voltage_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_toRightOf="@id/apogee_greenled"
|
||||||
|
android:text="@string/apogee_voltage_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/apogee_voltage_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:id="@+id/main_view"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/main_redled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/main_voltage_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/main_greenled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:contentDescription="@string/main_voltage_label"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/main_voltage_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_toRightOf="@id/main_greenled"
|
||||||
|
android:text="@string/main_voltage_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/main_voltage_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
</TableLayout>
|
||||||
|
</LinearLayout>
|
15
altosdroid/app/src/main/res/layout/tab_layout.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/customTabLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tabLabel"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:textColor="#ffffff"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:background="#808080"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_centerHorizontal="true" />
|
||||||
|
</RelativeLayout>
|
151
altosdroid/app/src/main/res/layout/tab_map.xml
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical" >
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:baselineAligned="true"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_weight="1">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/map_online"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:layout_weight="1">
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<org.altusmetrum.AltosDroid.AltosMapOffline
|
||||||
|
android:id="@+id/map_offline"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"/>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<TableLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:stretchColumns="1,3"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:baselineAligned="true"
|
||||||
|
android:orientation="horizontal" >
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/distance_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingRight="4dp"
|
||||||
|
android:text="@string/distance_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/distance_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/bearing_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingRight="4dp"
|
||||||
|
android:text="@string/bearing_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/bearing_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="" />
|
||||||
|
</TableRow>
|
||||||
|
</TableLayout>
|
||||||
|
|
||||||
|
<TableLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:stretchColumns="1,2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:baselineAligned="true"
|
||||||
|
android:orientation="horizontal" >
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/target_pos_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingRight="4dp"
|
||||||
|
android:text="@string/target_pos_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/target_lat_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/target_lon_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/receiver_pos_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingRight="4dp"
|
||||||
|
android:text="@string/receiver_pos_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/receiver_lat_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/receiver_lon_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="" />
|
||||||
|
</TableRow>
|
||||||
|
</TableLayout>
|
||||||
|
</LinearLayout>
|
565
altosdroid/app/src/main/res/layout/tab_pad.xml
Normal file
@ -0,0 +1,565 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TableLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:stretchColumns="2,3"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/battery_redled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/battery_voltage_label"
|
||||||
|
android:src="@drawable/grayled"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/battery_greenled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:contentDescription="@string/battery_voltage_label"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/battery_voltage_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/battery_voltage_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/battery_voltage_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
/>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:id="@+id/receiver_row"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/receiver_redled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/receiver_voltage_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/receiver_greenled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/receiver_voltage_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/receiver_voltage_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/receiver_voltage_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/receiver_voltage_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" >
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/logging_redled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/logging_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/logging_greenled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/logging_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/logging_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/logging_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/logging_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/logging_label"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" >
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/gps_locked_redled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/gps_locked_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/gps_locked_greenled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/gps_locked_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/gps_locked_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/gps_locked_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/gps_locked_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/gps_locked_label"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" >
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/gps_ready_redled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/gps_ready_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/gps_ready_greenled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/gps_ready_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/gps_ready_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/gps_ready_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/gps_ready_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/gps_ready_label"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:id="@+id/apogee_row"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/apogee_redled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/apogee_voltage_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/apogee_greenled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/apogee_voltage_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/apogee_voltage_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/apogee_voltage_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/apogee_voltage_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:id="@+id/main_row"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" >
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/main_redled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/main_voltage_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/main_greenled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/main_voltage_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/main_voltage_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/main_voltage_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/main_voltage_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:id="@+id/ignite_a_row"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" >
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ignite_a_redled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/ignite_a_voltage_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ignite_a_greenled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/ignite_a_voltage_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/ignite_a_voltage_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/ignite_a_voltage_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/ignite_a_voltage_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:id="@+id/ignite_b_row"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" >
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ignite_b_redled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/ignite_b_voltage_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ignite_b_greenled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/ignite_b_voltage_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/ignite_b_voltage_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/ignite_b_voltage_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/ignite_b_voltage_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:id="@+id/ignite_c_row"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" >
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ignite_c_redled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/ignite_c_voltage_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ignite_c_greenled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/ignite_c_voltage_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/ignite_c_voltage_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/ignite_c_voltage_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/ignite_c_voltage_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:id="@+id/ignite_d_row"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" >
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ignite_d_redled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/ignite_d_voltage_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ignite_d_greenled"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:contentDescription="@string/ignite_d_voltage_label"
|
||||||
|
android:src="@drawable/grayled" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/ignite_d_voltage_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/ignite_d_voltage_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/ignite_d_voltage_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:id="@+id/tilt_view"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tilt_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_column="2"
|
||||||
|
android:text="@string/tilt_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tilt_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/receiver_lat_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_column="2"
|
||||||
|
android:text="@string/receiver_latitude_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/receiver_lat_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/receiver_lon_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_column="2"
|
||||||
|
android:text="@string/receiver_longitude_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/receiver_lon_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/receiver_alt_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_column="2"
|
||||||
|
android:text="@string/receiver_altitude_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/receiver_alt_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text=""
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
|
</TableRow>
|
||||||
|
</TableLayout>
|
||||||
|
</LinearLayout>
|
244
altosdroid/app/src/main/res/layout/tab_recover.xml
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright © 2013 Mike Beattie <mike@ethernal.org>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
-->
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical" >
|
||||||
|
|
||||||
|
<TableLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:stretchColumns="0,1"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/bearing_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/bearing_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/bearing_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/direction_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/direction_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/direction_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/distance_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/distance_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/distance_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/target_lat_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/target_latitude_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/target_lat_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/target_lon_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/target_longitude_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/target_lon_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="" />
|
||||||
|
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/receiver_lat_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/receiver_latitude_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/receiver_lat_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/receiver_lon_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/receiver_longitude_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/receiver_lon_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text=""/>
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/max_height_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/max_height_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/max_height_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/max_speed_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/max_speed_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/max_speed_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="" />
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/max_accel_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/max_accel_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/max_accel_value"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="" />
|
||||||
|
</TableRow>
|
||||||
|
</TableLayout>
|
||||||
|
</LinearLayout>
|
52
altosdroid/app/src/main/res/layout/tracker_ent.xml
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2020 Keith Packard <keithp@keithp.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<TableRow
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/call_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingRight="3dp"
|
||||||
|
android:text="" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/serial_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingRight="3dp"
|
||||||
|
android:text="" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/frequency_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingRight="3dp"
|
||||||
|
android:text="" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/age_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="" />
|
||||||
|
</TableRow>
|
68
altosdroid/app/src/main/res/layout/tracker_list.xml
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright © 2020 Keith Packard <keithp@keithp.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
>
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
>
|
||||||
|
<TableLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/tracker_list"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:stretchColumns="1,2,3,4">
|
||||||
|
<TableRow
|
||||||
|
android:id="@+id/tracker_row"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
>
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/call_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/callsign_label" />
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/serial_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/serial_label" />
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/frequency_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/freq_label" />
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/age_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/age_label" />
|
||||||
|
</TableRow>
|
||||||
|
</TableLayout>
|
||||||
|
</ScrollView>
|
||||||
|
</LinearLayout>
|
47
altosdroid/app/src/main/res/menu/option_menu.xml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright © 2015 Keith Packard <keithp@keithp.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
-->
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:id="@+id/connect_scan"
|
||||||
|
android:icon="@android:drawable/ic_menu_search"
|
||||||
|
android:title="@string/connect_device" />
|
||||||
|
<item android:id="@+id/disconnect"
|
||||||
|
android:icon="@android:drawable/ic_notification_clear_all"
|
||||||
|
android:title="@string/disconnect_device" />
|
||||||
|
<item android:id="@+id/select_freq"
|
||||||
|
android:icon="@android:drawable/ic_menu_preferences"
|
||||||
|
android:title="@string/select_freq" />
|
||||||
|
|
||||||
|
<item android:id="@+id/select_tracker"
|
||||||
|
android:icon="@android:drawable/ic_menu_view"
|
||||||
|
android:title="@string/select_tracker"/>
|
||||||
|
<item android:id="@+id/delete_track"
|
||||||
|
android:icon="@android:drawable/ic_notification_clear_all"
|
||||||
|
android:title="@string/delete_track"/>
|
||||||
|
|
||||||
|
<item android:id="@+id/setup"
|
||||||
|
android:icon="@android:drawable/ic_menu_preferences"
|
||||||
|
android:title="@string/setup" />
|
||||||
|
<item android:id="@+id/idle_mode"
|
||||||
|
android:icon="@android:drawable/ic_menu_preferences"
|
||||||
|
android:title="@string/idle_mode" />
|
||||||
|
|
||||||
|
<item android:id="@+id/quit"
|
||||||
|
android:icon="@android:drawable/ic_menu_close_clear_cancel"
|
||||||
|
android:title="@string/quit" />
|
||||||
|
</menu>
|
21
altosdroid/app/src/main/res/values/Colors.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright © 2015 Keith Packard <keithp@keithp.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
-->
|
||||||
|
<resources>
|
||||||
|
<color name="old_color">#ffff4040</color>
|
||||||
|
</resources>
|
44
altosdroid/app/src/main/res/values/CustomTheme.xml
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<style name="Small" parent="android:Theme.Material.Light">
|
||||||
|
<item name="android:textColor">#000000</item>
|
||||||
|
<item name="android:textSize">12sp</item>
|
||||||
|
<item name="android:lineHeight">14sp</item>
|
||||||
|
</style>
|
||||||
|
<style name="Medium" parent="android:Theme.Material.Light">
|
||||||
|
<item name="android:textColor">#000000</item>
|
||||||
|
<item name="android:textSize">15sp</item>
|
||||||
|
<item name="android:lineHeight">18sp</item>
|
||||||
|
</style>
|
||||||
|
<style name="Large" parent="android:Theme.Material.Light">
|
||||||
|
<item name="android:textColor">#000000</item>
|
||||||
|
<item name="android:textSize">20sp</item>
|
||||||
|
<item name="android:lineHeight">40sp</item>
|
||||||
|
</style>
|
||||||
|
<style name="Extra" parent="android:Theme.Material.Light">
|
||||||
|
<item name="android:textColor">#000000</item>
|
||||||
|
<item name="android:textSize">27sp</item>
|
||||||
|
<item name="android:lineHeight">32sp</item>
|
||||||
|
</style>
|
||||||
|
<style name="Small.Dialog" parent="android:Theme.Material.Light.Dialog">
|
||||||
|
<item name="android:textColor">#000000</item>
|
||||||
|
<item name="android:textSize">12sp</item>
|
||||||
|
<item name="android:lineHeight">14sp</item>
|
||||||
|
</style>
|
||||||
|
<style name="Medium.Dialog" parent="android:Theme.Material.Light.Dialog">
|
||||||
|
<item name="android:textColor">#000000</item>
|
||||||
|
<item name="android:textSize">15sp</item>
|
||||||
|
<item name="android:lineHeight">18sp</item>
|
||||||
|
</style>
|
||||||
|
<style name="Large.Dialog" parent="android:Theme.Material.Light.Dialog">
|
||||||
|
<item name="android:textColor">#000000</item>
|
||||||
|
<item name="android:textSize">20sp</item>
|
||||||
|
<item name="android:lineHeight">40sp</item>
|
||||||
|
</style>
|
||||||
|
<style name="Extra.Dialog" parent="android:Theme.Material.Light.Dialog">
|
||||||
|
<item name="android:textColor">#000000</item>
|
||||||
|
<item name="android:textSize">27sp</item>
|
||||||
|
<item name="android:lineHeight">32sp</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
156
altosdroid/app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright © 2012-2013 Mike Beattie <mike@ethernal.org>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||||
|
-->
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<string name="app_name">AltosDroid</string>
|
||||||
|
|
||||||
|
<!-- AltosDroid -->
|
||||||
|
<string name="bt_not_enabled">Bluetooth was not enabled.</string>
|
||||||
|
<string name="title_connecting">connecting…</string>
|
||||||
|
<string name="title_connected_to">connected</string>
|
||||||
|
<string name="title_not_connected">not connected</string>
|
||||||
|
|
||||||
|
<!-- Options Menu -->
|
||||||
|
<string name="connect_device">Connect a device</string>
|
||||||
|
<string name="disconnect_device">Disconnect device</string>
|
||||||
|
<string name="quit">Quit</string>
|
||||||
|
<string name="setup">Setup</string>
|
||||||
|
<string name="select_freq">Select radio frequency</string>
|
||||||
|
<string name="select_rate">Select data rate</string>
|
||||||
|
<string name="change_units">Change units</string>
|
||||||
|
<string name="preload_maps">Load Maps</string>
|
||||||
|
<string name="select_tracker">Select Tracker</string>
|
||||||
|
<string name="delete_track">Delete Track</string>
|
||||||
|
<string name="map_type">Map Type</string>
|
||||||
|
<string name="map_source">Map Source</string>
|
||||||
|
|
||||||
|
<!-- MapTypeActivity -->
|
||||||
|
<!-- <string name="map_type">Map Type</string> -->
|
||||||
|
|
||||||
|
<!-- DeviceListActivity -->
|
||||||
|
<string name="scanning">Scanning…</string>
|
||||||
|
<string name="select_device">Select device</string>
|
||||||
|
<string name="none_paired">No devices have been paired</string>
|
||||||
|
<string name="none_found">No devices found</string>
|
||||||
|
<string name="title_paired_devices">Paired Devices</string>
|
||||||
|
<string name="title_other_devices">Other Available Devices</string>
|
||||||
|
<string name="button_scan">Scan for devices</string>
|
||||||
|
|
||||||
|
<!-- TrackerListActivity -->
|
||||||
|
<string name="freq_label">Freq</string>
|
||||||
|
|
||||||
|
<!-- Service -->
|
||||||
|
<string name="telemetry_service_label">AltosDroid Telemetry Service</string>
|
||||||
|
<string name="telemetry_service_started">Telemetry Service Started</string>
|
||||||
|
<string name="telemetry_service_stopped">Telemetry Service Stopped</string>
|
||||||
|
|
||||||
|
<!-- UI fields -->
|
||||||
|
<!-- Header -->
|
||||||
|
<string name="callsign_label">Call</string>
|
||||||
|
<string name="serial_label">Serial</string>
|
||||||
|
<string name="flight_label">Flight</string>
|
||||||
|
<string name="state_label">State</string>
|
||||||
|
<string name="rssi_label">RSSI</string>
|
||||||
|
<string name="age_label">Age</string>
|
||||||
|
|
||||||
|
<!-- Tab fields -->
|
||||||
|
<string name="height_label">Height</string>
|
||||||
|
<string name="altitude_label">Altitude</string>
|
||||||
|
<string name="speed_label">Speed</string>
|
||||||
|
<string name="accel_label">Acceleration</string>
|
||||||
|
<string name="tilt_label">Tilt</string>
|
||||||
|
<string name="bearing_label">Bearing</string>
|
||||||
|
<string name="direction_label">Direction</string>
|
||||||
|
<string name="elevation_label">Elevation</string>
|
||||||
|
<string name="range_label">Range</string>
|
||||||
|
<string name="distance_label">Distance</string>
|
||||||
|
<string name="gnd_distance_label">Ground Distance</string>
|
||||||
|
<string name="max_height_label">Max Height</string>
|
||||||
|
<string name="max_altitude_label">Max Altitude</string>
|
||||||
|
<string name="max_speed_label">Max Speed</string>
|
||||||
|
<string name="max_accel_label">Max Accel</string>
|
||||||
|
<string name="battery_voltage_label">Battery</string>
|
||||||
|
<string name="receiver_voltage_label">Receiver Battery</string>
|
||||||
|
<string name="apogee_voltage_label">Apogee Igniter</string>
|
||||||
|
<string name="main_voltage_label">Main Igniter</string>
|
||||||
|
<string name="ignite_a_voltage_label">Igniter A</string>
|
||||||
|
<string name="ignite_b_voltage_label">Igniter B</string>
|
||||||
|
<string name="ignite_c_voltage_label">Igniter C</string>
|
||||||
|
<string name="ignite_d_voltage_label">Igniter D</string>
|
||||||
|
<string name="logging_label">Data Logging</string>
|
||||||
|
<string name="gps_locked_label">GPS Locked</string>
|
||||||
|
<string name="gps_ready_label">GPS Ready</string>
|
||||||
|
<string name="latitude_label">Latitude</string>
|
||||||
|
<string name="longitude_label">Longitude</string>
|
||||||
|
<string name="target_pos_label">Tar</string>
|
||||||
|
<string name="receiver_pos_label">Me</string>
|
||||||
|
<string name="target_latitude_label">Tar Lat</string>
|
||||||
|
<string name="target_longitude_label">Tar Lon</string>
|
||||||
|
<string name="receiver_latitude_label">My Lat</string>
|
||||||
|
<string name="receiver_longitude_label">My Lon</string>
|
||||||
|
<string name="receiver_altitude_label">My Alt</string>
|
||||||
|
|
||||||
|
<!-- Map preload -->
|
||||||
|
<string name="preload_site_label">Known Launch Sites</string>
|
||||||
|
<string name="preload_latitude_label">Latitude</string>
|
||||||
|
<string name="preload_longitude_label">Longitude</string>
|
||||||
|
|
||||||
|
<string name="preload_types">Map Types</string>
|
||||||
|
<string name="preload_hybrid">Hybrid</string>
|
||||||
|
<string name="preload_satellite">Satellite</string>
|
||||||
|
<string name="preload_roadmap">Roadmap</string>
|
||||||
|
<string name="preload_terrain">Terrain</string>
|
||||||
|
<string name="preload_min_zoom">Minimum Zoom</string>
|
||||||
|
<string name="preload_max_zoom">Maximum Zoom</string>
|
||||||
|
<string name="preload_radius">Radius</string>
|
||||||
|
|
||||||
|
<string name="preload_load">Load Map</string>
|
||||||
|
|
||||||
|
<!-- Idle mode -->
|
||||||
|
<string name="idle_mode">Idle Mode</string>
|
||||||
|
<string name="set_callsign_label">Callsign: </string>
|
||||||
|
<string name="connect_idle">Monitor</string>
|
||||||
|
<string name="disconnect_idle">Disconnect</string>
|
||||||
|
<string name="reboot_idle">Reboot</string>
|
||||||
|
<string name="igniters_idle">Fire Igniters</string>
|
||||||
|
|
||||||
|
<!-- igniters -->
|
||||||
|
<string name="igniters">Igniters</string>
|
||||||
|
<string name="igniter_arm">Arm</string>
|
||||||
|
<string name="igniter_armed">Armed</string>
|
||||||
|
<string name="igniter_fire">Fire</string>
|
||||||
|
|
||||||
|
<!-- setup -->
|
||||||
|
<string name="telemetry_rate">Telemetry Rate</string>
|
||||||
|
<string name="set_units">Units</string>
|
||||||
|
<!-- <string name="map_type">Map Type</string> -->
|
||||||
|
<!-- <string name="map_source">Map Source</string> -->
|
||||||
|
<!-- <string name="preload_maps">Preload Maps</string> -->
|
||||||
|
<string name="font_size">Text Size</string>
|
||||||
|
<string name="manage_frequencies">Manage Frequencies</string>
|
||||||
|
<string name="done">OK</string>
|
||||||
|
|
||||||
|
<!-- manage frequencies -->
|
||||||
|
<string name="set">Set</string>
|
||||||
|
<string name="mhz">MHz</string>
|
||||||
|
<string name="remove">Remove</string>
|
||||||
|
<!-- <string name="done">OK</string> -->
|
||||||
|
<string name="frequency">Frequency</string>
|
||||||
|
<string name="description">Description</string>
|
||||||
|
</resources>
|
6
altosdroid/app/src/main/res/xml/device_filter.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<usb-device vendor-id="65534" />
|
||||||
|
<usb-device vendor-id="1027" />
|
||||||
|
</resources>
|
27
altosdroid/build.gradle
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:3.4.2'
|
||||||
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
// in the individual module build.gradle files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
tasks.withType(JavaCompile) {
|
||||||
|
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task clean(type: Delete) {
|
||||||
|
delete rootProject.buildDir
|
||||||
|
}
|
43
altosdroid/buildinfo.sh
Executable file
@ -0,0 +1,43 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Author: Mike Beattie <mike@ethernal.org>
|
||||||
|
#
|
||||||
|
# Script to parse result from git describe, and push values into
|
||||||
|
# BuildInfo.java for use within altosdroid (to display the current
|
||||||
|
# version and build information, primarily).
|
||||||
|
#
|
||||||
|
|
||||||
|
srcdir=app/src/main/java/org/altusmetrum/AltosDroid
|
||||||
|
infile=${srcdir}/BuildInfo.java.in
|
||||||
|
outfile=${srcdir}/BuildInfo.java
|
||||||
|
|
||||||
|
. ../src/Makedefs
|
||||||
|
version=$VERSION
|
||||||
|
branch=''
|
||||||
|
commitnum=''
|
||||||
|
commithash=''
|
||||||
|
builddate=$(date "+%Y-%m-%d")
|
||||||
|
buildtime=$(date "+%H:%M")
|
||||||
|
buildtz=$(date "+%z")
|
||||||
|
|
||||||
|
|
||||||
|
describe=$(git describe --match "$version" --long --always 2>/dev/null || echo '')
|
||||||
|
if [ -n "$describe" ]; then
|
||||||
|
branch=$(git branch | sed -ne 's/^\* //p')
|
||||||
|
commitdetails=$(echo $describe | sed -e "s/^$version-//")
|
||||||
|
commitnum=$(echo $commitdetails | cut -s -d- -f1)
|
||||||
|
commithash=$(echo $commitdetails | cut -d- -f2)
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
echo "Version $describe, built on $builddate $buildtime $buildtz"
|
||||||
|
|
||||||
|
sed -e "s/@VERSION@/$version/" \
|
||||||
|
-e "s/@DESCRIBE@/$describe/" \
|
||||||
|
-e "s/@BRANCH@/$branch/" \
|
||||||
|
-e "s/@COMMITNUM@/$commitnum/" \
|
||||||
|
-e "s/@COMMITHASH@/$commithash/" \
|
||||||
|
-e "s/@BUILDDATE@/$builddate/" \
|
||||||
|
-e "s/@BUILDTIME@/$buildtime/" \
|
||||||
|
-e "s/@BUILDTZ@/$buildtz/" \
|
||||||
|
$infile > $outfile
|
19
altosdroid/gradle.properties
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Project-wide Gradle settings.
|
||||||
|
|
||||||
|
# IDE (e.g. Android Studio) users:
|
||||||
|
# Gradle settings configured through the IDE *will override*
|
||||||
|
# any settings specified in this file.
|
||||||
|
|
||||||
|
# For more details on how to configure your build environment visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
|
|
||||||
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
|
android.enableJetifier=true
|
||||||
|
android.useAndroidX=true
|
||||||
|
org.gradle.jvmargs=-Xmx1536m
|
||||||
|
|
||||||
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
|
# This option should only be used with decoupled projects. More details, visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
|
# org.gradle.parallel=true
|
BIN
altosdroid/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
6
altosdroid/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#Thu Aug 29 13:53:38 NZST 2019
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
|
172
altosdroid/gradlew
vendored
Executable file
@ -0,0 +1,172 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
(0) set -- ;;
|
||||||
|
(1) set -- "$args0" ;;
|
||||||
|
(2) set -- "$args0" "$args1" ;;
|
||||||
|
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=$(save "$@")
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||||
|
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
84
altosdroid/gradlew.bat
vendored
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:init
|
||||||
|
@rem Get command-line arguments, handling Windows variants
|
||||||
|
|
||||||
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
|
|
||||||
|
:win9xME_args
|
||||||
|
@rem Slurp the command line arguments.
|
||||||
|
set CMD_LINE_ARGS=
|
||||||
|
set _SKIP=2
|
||||||
|
|
||||||
|
:win9xME_args_slurp
|
||||||
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
|
set CMD_LINE_ARGS=%*
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
10
altosdroid/local.properties.in
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# This file is automatically generated by Android Tools.
|
||||||
|
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||||
|
#
|
||||||
|
# This file must *NOT* be checked in Version Control Systems,
|
||||||
|
# as it contains information specific to your local configuration.
|
||||||
|
|
||||||
|
# location of the SDK. This is only used by Ant
|
||||||
|
# For customization when using a Version Control System, please read the
|
||||||
|
# header note.
|
||||||
|
sdk.dir=@ANDROID_SDK@
|
7
altosdroid/release-note-1.5
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Version 1.5
|
||||||
|
|
||||||
|
* TeleGPS support
|
||||||
|
* Reloads previous flight data at startup
|
||||||
|
* Fixes crash when touching map
|
||||||
|
* Reconnect to TeleBT at startup
|
||||||
|
* Remember radio settings
|
1
altosdroid/settings.gradle
Normal file
@ -0,0 +1 @@
|
|||||||
|
include ':app'
|
4
altoslib/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
bin
|
||||||
|
classaltoslib.stamp
|
||||||
|
altoslib*.jar
|
||||||
|
AltosVersion.java
|