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
|