Android
9
android/.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.idea
|
||||
*.iml
|
674
android/LICENCE
Normal file
@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. 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
|
||||
them 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 prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. 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.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey 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;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If 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 convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU 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 that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
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.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
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.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
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
|
||||
state 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
{project} Copyright (C) {year} {fullname}
|
||||
This program 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, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU 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. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
1
android/app/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
67
android/app/build.gradle
Normal file
@ -0,0 +1,67 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'android-apt'
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion '23.0.3'
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.wolkabout.hexiwear"
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 22
|
||||
versionCode 23
|
||||
versionName "1.23"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
packagingOptions {
|
||||
exclude 'META-INF/DEPENDENCIES'
|
||||
exclude 'META-INF/NOTICE'
|
||||
exclude 'META-INF/NOTICE.txt'
|
||||
exclude 'META-INF/notice.txt'
|
||||
exclude 'META-INF/LICENSE'
|
||||
exclude 'META-INF/LICENSE.txt'
|
||||
exclude 'META-INF/license.txt'
|
||||
exclude 'META-INF/ASL2.0'
|
||||
exclude 'LICENSE.txt'
|
||||
exclude 'META-INF/services/org.androidannotations.plugin.AndroidAnnotationsPlugin'
|
||||
exclude 'asm-license.txt'
|
||||
}
|
||||
}
|
||||
|
||||
def SupportVersion = '23.4.0'
|
||||
def AAVersion = '4.0.0'
|
||||
def ParcelerVersion = '1.0.4'
|
||||
|
||||
dependencies {
|
||||
|
||||
// Android support libraries
|
||||
compile "com.android.support:appcompat-v7:$SupportVersion"
|
||||
compile "com.android.support:design:$SupportVersion"
|
||||
compile "com.android.support:cardview-v7:$SupportVersion"
|
||||
|
||||
// Android annotations
|
||||
compile "org.androidannotations:androidannotations:$AAVersion"
|
||||
compile "org.androidannotations:rest-spring-api:$AAVersion"
|
||||
compile "org.androidannotations:rest-spring:$AAVersion"
|
||||
|
||||
// Parceler
|
||||
compile "org.parceler:parceler-api:$ParcelerVersion"
|
||||
apt "org.parceler:parceler:$ParcelerVersion"
|
||||
|
||||
compile 'com.wolkabout:restandroid:1.0.6'
|
||||
compile 'com.wolkabout:wolk:1.0'
|
||||
|
||||
compile 'org.springframework.android:spring-android-rest-template:2.0.0.M3'
|
||||
compile 'com.fasterxml.jackson.core:jackson-databind:2.7.5'
|
||||
}
|
||||
|
||||
apt {
|
||||
arguments {
|
||||
androidManifestFile variant.outputs[0].processResources.manifestFile
|
||||
}
|
||||
}
|
17
android/app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /Users/nmaksimovic/android/sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# 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 *;
|
||||
#}
|
100
android/app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.wolkabout.hexiwear">
|
||||
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="com.google.android.gm.permission.READ_CONTENT_PROVIDER" />
|
||||
<uses-permission android:name="android.permission.READ_CALL_LOG" />
|
||||
<uses-permission android:name="android.permission.READ_SMS" />
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.telephony"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.bluetooth_le"
|
||||
android:required="true" />
|
||||
|
||||
<application
|
||||
android:name=".HexiwearApplication_"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name=".activity.LoginActivity_"
|
||||
android:label="@string/app_name"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Blues">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:host="wolksense.com"
|
||||
android:scheme="https" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.SignUpActivity_"
|
||||
android:label="@string/registration_activity_title"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Blues" />
|
||||
<activity
|
||||
android:name=".activity.MainActivity_"
|
||||
android:label="@string/discovery_available_devices"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/AppTheme.NoActionBar" />
|
||||
<activity
|
||||
android:name=".activity.ReadingsActivity_"
|
||||
android:label="@string/readings_activity_title"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/AppTheme.NoActionBar" />
|
||||
<activity
|
||||
android:name=".activity.SettingsActivity_"
|
||||
android:label="@string/preferences_activity_title"
|
||||
android:parentActivityName=".activity.ReadingsActivity_"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/AppTheme.NoActionBar" />
|
||||
<activity
|
||||
android:name=".activity.FirmwareSelectActivity_"
|
||||
android:label="@string/firmware_update_activity_title"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/AppTheme.NoActionBar" />
|
||||
<activity
|
||||
android:name=".activity.ResetPasswordActivity_"
|
||||
android:label="@string/reset_password_activity_title"
|
||||
android:theme="@style/AppTheme.NoActionBar" />
|
||||
<activity
|
||||
android:name=".activity.PasswordChangeActivity_"
|
||||
android:label="@string/change_password_activity_tile"
|
||||
android:theme="@style/AppTheme.NoActionBar"/>
|
||||
|
||||
<service
|
||||
android:name=".service.BluetoothService_"
|
||||
android:exported="false" />
|
||||
<service
|
||||
android:name=".service.FirmwareUpdateService_"
|
||||
android:exported="false" />
|
||||
<service
|
||||
android:name=".service.NotificationService_"
|
||||
android:exported="false" />
|
||||
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.wolkabout.hexiwear.activity.LoginActivity_;
|
||||
import com.wolkabout.wolkrestandroid.Credentials_;
|
||||
import com.wolkabout.wolkrestandroid.DefaultErrorHandler;
|
||||
|
||||
import org.androidannotations.annotations.AfterInject;
|
||||
import org.androidannotations.annotations.Bean;
|
||||
import org.androidannotations.annotations.EApplication;
|
||||
import org.androidannotations.annotations.sharedpreferences.Pref;
|
||||
|
||||
@EApplication
|
||||
public class HexiwearApplication extends Application{
|
||||
|
||||
@Pref
|
||||
Credentials_ credentials;
|
||||
|
||||
@Bean
|
||||
DefaultErrorHandler defaultErrorHandler;
|
||||
|
||||
@AfterInject
|
||||
void setUpServices() {
|
||||
defaultErrorHandler.setErrorHandler(new DefaultErrorHandler.ErrorHandler() {
|
||||
@Override
|
||||
public void onAuthenticationError() {
|
||||
if (credentials.username().get().equals("Demo")) {
|
||||
return;
|
||||
}
|
||||
|
||||
LoginActivity_.intent(HexiwearApplication.this).flags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK).start();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,201 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.activity;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.ComponentName;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.Environment;
|
||||
import android.os.IBinder;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.ListView;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
import com.wolkabout.hexiwear.adapter.FirmwareListAdapter;
|
||||
import com.wolkabout.hexiwear.model.otap.Image;
|
||||
import com.wolkabout.hexiwear.service.FirmwareUpdateService;
|
||||
import com.wolkabout.hexiwear.service.FirmwareUpdateService_;
|
||||
import com.wolkabout.hexiwear.util.ByteUtils;
|
||||
|
||||
import org.androidannotations.annotations.AfterViews;
|
||||
import org.androidannotations.annotations.Bean;
|
||||
import org.androidannotations.annotations.EActivity;
|
||||
import org.androidannotations.annotations.Extra;
|
||||
import org.androidannotations.annotations.ItemClick;
|
||||
import org.androidannotations.annotations.Receiver;
|
||||
import org.androidannotations.annotations.ViewById;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
@EActivity(R.layout.activity_firmware_select)
|
||||
public class FirmwareSelectActivity extends AppCompatActivity implements ServiceConnection {
|
||||
|
||||
private static final String TAG = FirmwareSelectActivity.class.getSimpleName();
|
||||
|
||||
private static final String UPDATE_INITIATED = "updateStarted";
|
||||
private static final String UPDATE_CANCELED = "updateCanceled";
|
||||
private static final String UPDATE_FINISHED = "updateFinished";
|
||||
private static final String UPDATE_PROGRESS = "updateProgress";
|
||||
private static final String UPDATE_ERROR = "updateError";
|
||||
|
||||
private static final String HEXIWEAR_PREFIX = "HEXIWEAR";
|
||||
private static final String IMAGE_EXTENSION = ".img";
|
||||
private static final int PREFIX_LENGTH = 14;
|
||||
private static final String FACTORY_SETTINGS = "factory_settings";
|
||||
|
||||
|
||||
@ViewById
|
||||
Toolbar toolbar;
|
||||
|
||||
@ViewById
|
||||
ListView firmwareList;
|
||||
|
||||
@Bean
|
||||
FirmwareListAdapter adapter;
|
||||
|
||||
@Extra
|
||||
BluetoothDevice device;
|
||||
|
||||
private ProgressDialog progressDialog;
|
||||
private FirmwareUpdateService firmwareUpdateService;
|
||||
|
||||
@AfterViews
|
||||
void init() {
|
||||
setSupportActionBar(toolbar);
|
||||
firmwareList.setAdapter(adapter);
|
||||
addFactorySettingImages();
|
||||
discoverFirmwareImages();
|
||||
FirmwareUpdateService_.intent(this).start();
|
||||
bindService(FirmwareUpdateService_.intent(this).get(), this, 0);
|
||||
}
|
||||
|
||||
void addFactorySettingImages() {
|
||||
try {
|
||||
final AssetManager assetManager = getAssets();
|
||||
for (String factorySetting : assetManager.list(FACTORY_SETTINGS)) {
|
||||
final InputStream inputStream = assetManager.open(FACTORY_SETTINGS + "/" + factorySetting);
|
||||
final String imageName = factorySetting.substring(PREFIX_LENGTH).replace(IMAGE_EXTENSION, "");
|
||||
final Image image = new Image(imageName, ByteUtils.readBytes(inputStream));
|
||||
adapter.add(image);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Couldn't open factory images.", e);
|
||||
}
|
||||
}
|
||||
|
||||
void discoverFirmwareImages() {
|
||||
final File downloadsDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||
for (final File file : downloadsDirectory.listFiles()) {
|
||||
if (file.getName().startsWith(HEXIWEAR_PREFIX)) {
|
||||
final String imageName = file.getName().substring(PREFIX_LENGTH).replace(IMAGE_EXTENSION, "");
|
||||
final Image image = new Image(imageName, ByteUtils.readBytes(file));
|
||||
adapter.add(image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ItemClick(R.id.firmwareList)
|
||||
void updateFirmware(final Image image) {
|
||||
progressDialog = new ProgressDialog(this);
|
||||
progressDialog.setTitle(R.string.firmware_update_activity_title);
|
||||
progressDialog.setMessage(getString(R.string.firmware_update_start));
|
||||
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||
progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.firmware_update_cancel), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final DialogInterface dialog, final int which) {
|
||||
firmwareUpdateService.cancelUpdate();
|
||||
}
|
||||
});
|
||||
progressDialog.setButton(DialogInterface.BUTTON_NEUTRAL, getString(R.string.firmware_update_hide), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final DialogInterface dialog, final int which) {
|
||||
progressDialog.dismiss();
|
||||
finish();
|
||||
}
|
||||
});
|
||||
progressDialog.show();
|
||||
|
||||
progressDialog.getButton(DialogInterface.BUTTON_NEUTRAL).setVisibility(View.INVISIBLE);
|
||||
firmwareUpdateService.updateFirmware(device, image);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
unbindService(this);
|
||||
if (progressDialog != null) {
|
||||
progressDialog.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(final ComponentName name, final IBinder service) {
|
||||
final FirmwareUpdateService.ServiceBinder binder = (FirmwareUpdateService.ServiceBinder) service;
|
||||
firmwareUpdateService = binder.getService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(final ComponentName name) {
|
||||
// Something terrible happened.
|
||||
}
|
||||
|
||||
@Receiver(actions = UPDATE_INITIATED, local = true)
|
||||
void onUpdateInitiated() {
|
||||
progressDialog.setMessage(getString(R.string.firmware_update_in_progress));
|
||||
progressDialog.setMax(100);
|
||||
progressDialog.getButton(DialogInterface.BUTTON_NEUTRAL).setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Receiver(actions = UPDATE_PROGRESS, local = true)
|
||||
void onUpdateProgress(@Receiver.Extra final int progress) {
|
||||
progressDialog.setProgress(progress);
|
||||
}
|
||||
|
||||
@Receiver(actions = UPDATE_CANCELED, local = true)
|
||||
void onUpdateCanceled() {
|
||||
progressDialog.hide();
|
||||
}
|
||||
|
||||
@Receiver(actions = UPDATE_FINISHED, local = true)
|
||||
void onUpdateFinished() {
|
||||
progressDialog.setProgress(100);
|
||||
progressDialog.setMessage(getString(R.string.firmware_update_complete));
|
||||
progressDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
@Receiver(actions = UPDATE_ERROR, local = true)
|
||||
void onUpdateError() {
|
||||
progressDialog.setMax(0);
|
||||
progressDialog.setProgress(0);
|
||||
progressDialog.setMessage(getString(R.string.firmware_update_error));
|
||||
progressDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,197 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.activity;
|
||||
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
import com.wolkabout.hexiwear.util.Dialog;
|
||||
import com.wolkabout.hexiwear.view.Input;
|
||||
import com.wolkabout.wolkrestandroid.Credentials_;
|
||||
import com.wolkabout.wolkrestandroid.dto.AuthenticationResponseDto;
|
||||
import com.wolkabout.wolkrestandroid.dto.EmailVerificationRequest;
|
||||
import com.wolkabout.wolkrestandroid.dto.SignInDto;
|
||||
import com.wolkabout.wolkrestandroid.service.AuthenticationService;
|
||||
|
||||
import org.androidannotations.annotations.AfterViews;
|
||||
import org.androidannotations.annotations.Background;
|
||||
import org.androidannotations.annotations.Bean;
|
||||
import org.androidannotations.annotations.Click;
|
||||
import org.androidannotations.annotations.EActivity;
|
||||
import org.androidannotations.annotations.EditorAction;
|
||||
import org.androidannotations.annotations.SystemService;
|
||||
import org.androidannotations.annotations.UiThread;
|
||||
import org.androidannotations.annotations.ViewById;
|
||||
import org.androidannotations.annotations.sharedpreferences.Pref;
|
||||
import org.androidannotations.rest.spring.annotations.RestService;
|
||||
|
||||
@EActivity(R.layout.activity_login)
|
||||
public class LoginActivity extends AppCompatActivity {
|
||||
|
||||
public static final String TAG = LoginActivity.class.getSimpleName();
|
||||
|
||||
@ViewById
|
||||
Input emailField;
|
||||
|
||||
@ViewById
|
||||
Input passwordField;
|
||||
|
||||
@ViewById
|
||||
View signInElements;
|
||||
|
||||
@ViewById
|
||||
TextView signingInLabel;
|
||||
|
||||
@ViewById
|
||||
View signingInElements;
|
||||
|
||||
@ViewById
|
||||
Button signInButton;
|
||||
|
||||
@Pref
|
||||
Credentials_ credentials;
|
||||
|
||||
@SystemService
|
||||
InputMethodManager inputMethodManager;
|
||||
|
||||
@Bean
|
||||
Dialog dialog;
|
||||
|
||||
@RestService
|
||||
AuthenticationService authenticationService;
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
String dataString = getIntent().getDataString();
|
||||
Log.i(TAG, "Data string: " + dataString);
|
||||
if (dataString != null && dataString.contains("activationCode=")) {
|
||||
Log.i(TAG, "Activate");
|
||||
verifyEmail(dataString.substring(dataString.indexOf('=') + 1));
|
||||
}
|
||||
}
|
||||
|
||||
@AfterViews
|
||||
void startMainActivity() {
|
||||
if (credentials.username().exists() && !credentials.username().get().equals("Demo")) {
|
||||
MainActivity_.intent(this).start();
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Click(R.id.signUp)
|
||||
void openSignUpScreen() {
|
||||
SignUpActivity_.intent(this).start();
|
||||
}
|
||||
|
||||
@Click(R.id.signInButton)
|
||||
@EditorAction(R.id.passwordField)
|
||||
void attemptSignIn() {
|
||||
if ("demo".equals(emailField.getValue()) && "demo".equals(passwordField.getValue())) {
|
||||
credentials.username().put("Demo");
|
||||
MainActivity_.intent(LoginActivity.this).start();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!validateCredentials()) {
|
||||
return;
|
||||
}
|
||||
|
||||
inputMethodManager.hideSoftInputFromWindow(passwordField.getWindowToken(), 0);
|
||||
|
||||
signInElements.setVisibility(View.GONE);
|
||||
signingInElements.setVisibility(View.VISIBLE);
|
||||
|
||||
signIn();
|
||||
}
|
||||
|
||||
@Background
|
||||
void signIn() {
|
||||
try {
|
||||
final String emailAddress = emailField.getValue();
|
||||
final String password = passwordField.getValue();
|
||||
|
||||
final AuthenticationResponseDto response = authenticationService.signIn(new SignInDto(emailAddress, password));
|
||||
credentials.username().put(response.getEmail());
|
||||
credentials.accessToken().put(response.getAccessToken());
|
||||
credentials.refreshToken().put(response.getRefreshToken());
|
||||
credentials.accessTokenExpires().put(response.getAccessTokenExpires().getTime());
|
||||
credentials.refreshTokenExpires().put(response.getRefreshTokenExpires().getTime());
|
||||
MainActivity_.intent(LoginActivity.this).start();
|
||||
} catch (Exception e) {
|
||||
onSignInError();
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void onSignInError() {
|
||||
signInElements.setVisibility(View.VISIBLE);
|
||||
signingInElements.setVisibility(View.GONE);
|
||||
emailField.requestFocus();
|
||||
passwordField.clear();
|
||||
dialog.showWarning(R.string.login_error_invalid_credentials);
|
||||
}
|
||||
|
||||
@Click
|
||||
void resetPassword() {
|
||||
ResetPasswordActivity_.intent(this).start();
|
||||
}
|
||||
|
||||
private boolean validateCredentials() {
|
||||
if (emailField.isEmpty()) {
|
||||
emailField.setError(R.string.registration_error_field_required);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (passwordField.isEmpty()) {
|
||||
passwordField.setError(R.string.registration_error_field_required);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!android.util.Patterns.EMAIL_ADDRESS.matcher(emailField.getValue()).matches()) {
|
||||
emailField.setError(R.string.registration_error_invalid_email);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Background
|
||||
void verifyEmail(String code) {
|
||||
try {
|
||||
Log.i(TAG, "Verifying email - code: " + code);
|
||||
final EmailVerificationRequest request = new EmailVerificationRequest(code);
|
||||
authenticationService.verifyEmail(request);
|
||||
Toast.makeText(LoginActivity.this, "Email verified.", Toast.LENGTH_LONG).show();
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(LoginActivity.this, "Failed to verify email.", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,237 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.activity;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
import com.wolkabout.hexiwear.adapter.DeviceListAdapter;
|
||||
import com.wolkabout.hexiwear.model.BluetoothDeviceWrapper;
|
||||
import com.wolkabout.hexiwear.service.BluetoothService;
|
||||
import com.wolkabout.hexiwear.service.BluetoothService_;
|
||||
import com.wolkabout.hexiwear.service.DeviceDiscoveryService;
|
||||
import com.wolkabout.hexiwear.service.DeviceRegistrationService;
|
||||
import com.wolkabout.hexiwear.util.HexiwearDevices;
|
||||
import com.wolkabout.wolkrestandroid.Credentials_;
|
||||
|
||||
import org.androidannotations.annotations.AfterInject;
|
||||
import org.androidannotations.annotations.AfterViews;
|
||||
import org.androidannotations.annotations.Bean;
|
||||
import org.androidannotations.annotations.EActivity;
|
||||
import org.androidannotations.annotations.ItemClick;
|
||||
import org.androidannotations.annotations.ItemLongClick;
|
||||
import org.androidannotations.annotations.OptionsItem;
|
||||
import org.androidannotations.annotations.OptionsMenu;
|
||||
import org.androidannotations.annotations.OptionsMenuItem;
|
||||
import org.androidannotations.annotations.Receiver;
|
||||
import org.androidannotations.annotations.ViewById;
|
||||
import org.androidannotations.annotations.sharedpreferences.Pref;
|
||||
|
||||
@OptionsMenu(R.menu.menu_main)
|
||||
@EActivity(R.layout.activity_main)
|
||||
public class MainActivity extends AppCompatActivity implements ServiceConnection {
|
||||
|
||||
private static final String TAG = MainActivity.class.getSimpleName();
|
||||
private static final String OTAP_PREFIX = "OTAP";
|
||||
|
||||
@Bean
|
||||
DeviceListAdapter adapter;
|
||||
|
||||
@ViewById
|
||||
ListView listDevices;
|
||||
|
||||
@ViewById
|
||||
ProgressBar progressBar;
|
||||
|
||||
@ViewById
|
||||
SwipeRefreshLayout swipeRefresh;
|
||||
|
||||
@ViewById
|
||||
Toolbar toolbar;
|
||||
|
||||
@OptionsMenuItem
|
||||
MenuItem toggleTracking;
|
||||
|
||||
@Pref
|
||||
Credentials_ credentials;
|
||||
|
||||
@Bean
|
||||
DeviceDiscoveryService deviceDiscoveryService;
|
||||
|
||||
@Bean
|
||||
DeviceRegistrationService deviceRegistrationService;
|
||||
|
||||
@Bean
|
||||
HexiwearDevices devicesStore;
|
||||
|
||||
private boolean serviceBound;
|
||||
|
||||
@AfterInject
|
||||
void setStore() {
|
||||
devicesStore.init();
|
||||
}
|
||||
|
||||
@AfterViews
|
||||
void setViews() {
|
||||
setSupportActionBar(toolbar);
|
||||
listDevices.setAdapter(adapter);
|
||||
deviceDiscoveryService.startScan();
|
||||
swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
swipeRefresh.setRefreshing(false);
|
||||
adapter.clear();
|
||||
deviceDiscoveryService.startScan();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@AfterInject
|
||||
void checkService() {
|
||||
serviceBound = bindService(BluetoothService_.intent(this).get(), this, 0);
|
||||
}
|
||||
|
||||
@ItemClick(R.id.listDevices)
|
||||
void bondWithDevice(final BluetoothDeviceWrapper wrapper) {
|
||||
deviceDiscoveryService.cancelScan();
|
||||
final BluetoothDevice device = wrapper.getDevice();
|
||||
if (device.getBondState() == BluetoothDevice.BOND_BONDED) {
|
||||
onBonded(device);
|
||||
} else {
|
||||
device.createBond();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO This is a method to help with development. Remove once its no longer needed.
|
||||
@ItemLongClick(R.id.listDevices)
|
||||
void unbindDevice(final BluetoothDeviceWrapper wrapper) {
|
||||
try {
|
||||
final BluetoothDevice device = wrapper.getDevice();
|
||||
device.getClass().getMethod("removeBond", (Class[]) null).invoke(device, (Object[]) null);
|
||||
Toast.makeText(this, "Device unpaired.", Toast.LENGTH_SHORT).show();
|
||||
adapter.notifyDataSetChanged();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Can't remove bond", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
final BluetoothService.ServiceBinder binder = (BluetoothService.ServiceBinder) service;
|
||||
final BluetoothService bluetoothService = binder.getService();
|
||||
final BluetoothDevice currentDevice = bluetoothService.getCurrentDevice();
|
||||
if (currentDevice != null) {
|
||||
ReadingsActivity_.intent(this).device(currentDevice).flags(Intent.FLAG_ACTIVITY_NEW_TASK).start();
|
||||
final BluetoothDeviceWrapper wrapper = new BluetoothDeviceWrapper();
|
||||
wrapper.setDevice(currentDevice);
|
||||
wrapper.setSignalStrength(-65);
|
||||
adapter.add(wrapper);
|
||||
|
||||
}
|
||||
|
||||
unbindService(this);
|
||||
serviceBound = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
// Something terrible happened.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
if (serviceBound) {
|
||||
unbindService(this);
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Receiver(actions = DeviceDiscoveryService.SCAN_STARTED, local = true)
|
||||
void onScanningStarted() {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
toolbar.setTitle(R.string.discovery_scanning);
|
||||
}
|
||||
|
||||
@Receiver(actions = DeviceDiscoveryService.SCAN_STOPPED, local = true)
|
||||
void onScanningStopped() {
|
||||
progressBar.setVisibility(View.INVISIBLE);
|
||||
toolbar.setTitle(credentials.username().get());
|
||||
}
|
||||
|
||||
@Receiver(actions = DeviceDiscoveryService.DEVICE_DISCOVERED, local = true)
|
||||
void onBluetoothDeviceFound(@Receiver.Extra final BluetoothDeviceWrapper wrapper) {
|
||||
adapter.add(wrapper);
|
||||
}
|
||||
|
||||
@Receiver(actions = BluetoothDevice.ACTION_BOND_STATE_CHANGED)
|
||||
void onBondStateChanged(Intent intent) {
|
||||
final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||
final int previousBondState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, -1);
|
||||
final int newBondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
|
||||
|
||||
Log.d(TAG, device.getName() + "(" + device.getAddress() + ") changed state: " + previousBondState + " -> " + newBondState);
|
||||
adapter.notifyDataSetChanged();
|
||||
|
||||
if (newBondState == BluetoothDevice.BOND_BONDED) {
|
||||
onBonded(device);
|
||||
} else if (previousBondState == BluetoothDevice.BOND_BONDING && newBondState == BluetoothDevice.BOND_NONE) {
|
||||
device.createBond();
|
||||
}
|
||||
}
|
||||
|
||||
private void onBonded(BluetoothDevice device) {
|
||||
Log.i(TAG, "Successfully bonded to: " + device.getName());
|
||||
if (!devicesStore.isRegistered(device)) {
|
||||
deviceRegistrationService.registerHexiwearDevice(device);
|
||||
} else if (device.getName().contains(OTAP_PREFIX)) {
|
||||
FirmwareSelectActivity_.intent(this).device(device).start();
|
||||
} else {
|
||||
ReadingsActivity_.intent(this).flags(Intent.FLAG_ACTIVITY_SINGLE_TOP).device(device).start();
|
||||
}
|
||||
}
|
||||
|
||||
@OptionsItem
|
||||
void changePassword() {
|
||||
PasswordChangeActivity_.intent(this).start();
|
||||
}
|
||||
|
||||
@OptionsItem
|
||||
void signOut() {
|
||||
credentials.clear();
|
||||
LoginActivity_.intent(this).flags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK).start();
|
||||
finish();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.activity;
|
||||
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
import com.wolkabout.hexiwear.util.Dialog;
|
||||
import com.wolkabout.hexiwear.view.Input;
|
||||
import com.wolkabout.wolkrestandroid.Credentials_;
|
||||
import com.wolkabout.wolkrestandroid.dto.ChangePasswordRequest;
|
||||
import com.wolkabout.wolkrestandroid.service.AuthenticationService;
|
||||
|
||||
import org.androidannotations.annotations.AfterViews;
|
||||
import org.androidannotations.annotations.Background;
|
||||
import org.androidannotations.annotations.Bean;
|
||||
import org.androidannotations.annotations.Click;
|
||||
import org.androidannotations.annotations.EActivity;
|
||||
import org.androidannotations.annotations.UiThread;
|
||||
import org.androidannotations.annotations.ViewById;
|
||||
import org.androidannotations.annotations.sharedpreferences.Pref;
|
||||
import org.androidannotations.rest.spring.annotations.RestService;
|
||||
|
||||
@EActivity(R.layout.activity_password_change)
|
||||
public class PasswordChangeActivity extends AppCompatActivity {
|
||||
|
||||
@ViewById
|
||||
Toolbar toolbar;
|
||||
|
||||
@ViewById
|
||||
Input currentPassword;
|
||||
|
||||
@ViewById
|
||||
Input newPassword;
|
||||
|
||||
@Bean
|
||||
Dialog dialog;
|
||||
|
||||
@Pref
|
||||
Credentials_ credentials;
|
||||
|
||||
@RestService
|
||||
AuthenticationService authenticationService;
|
||||
|
||||
@AfterViews
|
||||
void init() {
|
||||
setSupportActionBar(toolbar);
|
||||
}
|
||||
|
||||
@Click
|
||||
@Background
|
||||
void changePassword() {
|
||||
if (!validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final ChangePasswordRequest requestDto = new ChangePasswordRequest(credentials.username().get(), currentPassword.getValue(), newPassword.getValue());
|
||||
authenticationService.changePassword(requestDto);
|
||||
onPasswordChanged(requestDto);
|
||||
} catch (Exception e) {
|
||||
dialog.showWarning(R.string.change_password_error);
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void onPasswordChanged(ChangePasswordRequest requestDto) {
|
||||
currentPassword.clear();
|
||||
newPassword.clear();
|
||||
dialog.showInfo(R.string.change_password_success, false);
|
||||
}
|
||||
|
||||
private boolean validate() {
|
||||
if (newPassword.isEmpty()) {
|
||||
newPassword.setError(R.string.registration_error_field_required);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (newPassword.getValue().length() < 8) {
|
||||
newPassword.setError(R.string.registration_error_invalid_password);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,330 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.activity;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
import com.wolkabout.hexiwear.model.Characteristic;
|
||||
import com.wolkabout.hexiwear.model.HexiwearDevice;
|
||||
import com.wolkabout.hexiwear.model.Mode;
|
||||
import com.wolkabout.hexiwear.service.BluetoothService;
|
||||
import com.wolkabout.hexiwear.service.BluetoothService_;
|
||||
import com.wolkabout.hexiwear.util.Dialog;
|
||||
import com.wolkabout.hexiwear.util.HexiwearDevices;
|
||||
import com.wolkabout.hexiwear.view.Reading;
|
||||
import com.wolkabout.hexiwear.view.SingleReading;
|
||||
import com.wolkabout.hexiwear.view.TripleReading;
|
||||
import com.wolkabout.wolkrestandroid.Credentials_;
|
||||
|
||||
import org.androidannotations.annotations.AfterInject;
|
||||
import org.androidannotations.annotations.AfterViews;
|
||||
import org.androidannotations.annotations.Bean;
|
||||
import org.androidannotations.annotations.EActivity;
|
||||
import org.androidannotations.annotations.Extra;
|
||||
import org.androidannotations.annotations.OptionsItem;
|
||||
import org.androidannotations.annotations.OptionsMenu;
|
||||
import org.androidannotations.annotations.Receiver;
|
||||
import org.androidannotations.annotations.ViewById;
|
||||
import org.androidannotations.annotations.sharedpreferences.Pref;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@EActivity(R.layout.activity_readings)
|
||||
@OptionsMenu(R.menu.menu_readings)
|
||||
public class ReadingsActivity extends AppCompatActivity implements ServiceConnection {
|
||||
|
||||
private static final String TAG = ReadingsActivity.class.getSimpleName();
|
||||
|
||||
@Extra
|
||||
BluetoothDevice device;
|
||||
|
||||
@ViewById
|
||||
View coordinator;
|
||||
|
||||
@ViewById
|
||||
Toolbar toolbar;
|
||||
|
||||
@ViewById
|
||||
SingleReading readingBattery;
|
||||
|
||||
@ViewById
|
||||
SingleReading readingTemperature;
|
||||
|
||||
@ViewById
|
||||
SingleReading readingHumidity;
|
||||
|
||||
@ViewById
|
||||
SingleReading readingPressure;
|
||||
|
||||
@ViewById
|
||||
SingleReading readingHeartRate;
|
||||
|
||||
@ViewById
|
||||
SingleReading readingLight;
|
||||
|
||||
@ViewById
|
||||
SingleReading readingSteps;
|
||||
|
||||
@ViewById
|
||||
SingleReading readingCalories;
|
||||
|
||||
@ViewById
|
||||
TripleReading readingAcceleration;
|
||||
|
||||
@ViewById
|
||||
TripleReading readingMagnet;
|
||||
|
||||
@ViewById
|
||||
TripleReading readingGyro;
|
||||
|
||||
@ViewById
|
||||
TextView connectionStatus;
|
||||
|
||||
@ViewById
|
||||
ProgressBar progressBar;
|
||||
|
||||
@ViewById
|
||||
LinearLayout readings;
|
||||
|
||||
@Bean
|
||||
HexiwearDevices hexiwearDevices;
|
||||
|
||||
@Pref
|
||||
Credentials_ credentials;
|
||||
|
||||
@Bean
|
||||
Dialog dialog;
|
||||
|
||||
private HexiwearDevice hexiwearDevice;
|
||||
private BluetoothService bluetoothService;
|
||||
private boolean isBound;
|
||||
private Mode mode = Mode.IDLE;
|
||||
|
||||
@AfterInject
|
||||
void startService() {
|
||||
hexiwearDevice = hexiwearDevices.getDevice(device.getAddress());
|
||||
if (hexiwearDevices.shouldKeepAlive(hexiwearDevice)) {
|
||||
BluetoothService_.intent(this).start();
|
||||
}
|
||||
isBound = bindService(BluetoothService_.intent(this).get(), this, BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
@AfterViews
|
||||
void setViews() {
|
||||
toolbar.setTitle(hexiwearDevice.getWolkName());
|
||||
setSupportActionBar(toolbar);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
invalidateOptionsMenu();
|
||||
setReadingVisibility(mode);
|
||||
}
|
||||
|
||||
@Receiver(actions = BluetoothService.MODE_CHANGED, local = true)
|
||||
void onModeChanged(@Receiver.Extra final Mode mode) {
|
||||
this.mode = mode;
|
||||
connectionStatus.setText(mode.getStringResource());
|
||||
|
||||
if (mode == Mode.IDLE) {
|
||||
dialog.showInfo(R.string.readings_idle_mode, false);
|
||||
}
|
||||
|
||||
setReadingVisibility(mode);
|
||||
}
|
||||
|
||||
private void setReadingVisibility(final Mode mode) {
|
||||
final Map<String, Boolean> displayPreferences = hexiwearDevices.getDisplayPreferences(device.getAddress());
|
||||
for (int i = 0; i < readings.getChildCount(); i++) {
|
||||
final Reading reading = (Reading) readings.getChildAt(i);
|
||||
final Characteristic readingType = reading.getReadingType();
|
||||
final boolean readingEnabled = displayPreferences.get(readingType.name());
|
||||
reading.setVisibility(readingEnabled && mode.hasCharacteristic(readingType) ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(final ComponentName name, final IBinder service) {
|
||||
final BluetoothService.ServiceBinder binder = (BluetoothService.ServiceBinder) service;
|
||||
bluetoothService = binder.getService();
|
||||
if (!bluetoothService.isConnected()) {
|
||||
bluetoothService.startReading(device);
|
||||
}
|
||||
final Mode mode = bluetoothService.getCurrentMode();
|
||||
if (mode != null) {
|
||||
onModeChanged(mode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(final ComponentName name) {
|
||||
// Something terrible happened.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
if (isBound) {
|
||||
unbindService(this);
|
||||
isBound = false;
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Receiver(actions = BluetoothService.ACTION_NEEDS_BOND, local = true)
|
||||
void onBondRequested() {
|
||||
connectionStatus.setText(R.string.discovery_bonding);
|
||||
Snackbar.make(coordinator, R.string.discovery_bonding, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
@Receiver(actions = BluetoothService.CONNECTION_STATE_CHANGED, local = true)
|
||||
void onConnectionStateChanged(@Receiver.Extra final boolean connectionState) {
|
||||
connectionStatus.setText(connectionState ? R.string.readings_connection_connected : R.string.readings_connection_reconnecting);
|
||||
}
|
||||
|
||||
@Receiver(actions = BluetoothService.DATA_AVAILABLE, local = true)
|
||||
void onDataAvailable(Intent intent) {
|
||||
progressBar.setVisibility(View.INVISIBLE);
|
||||
|
||||
final String uuid = intent.getStringExtra(BluetoothService.READING_TYPE);
|
||||
final String data = intent.getStringExtra(BluetoothService.STRING_DATA);
|
||||
|
||||
if (data.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Characteristic characteristic = Characteristic.byUuid(uuid);
|
||||
if (characteristic == null) {
|
||||
Log.w(TAG, "UUID " + uuid + " is unknown. Skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (characteristic) {
|
||||
case BATTERY:
|
||||
readingBattery.setValue(data);
|
||||
break;
|
||||
case TEMPERATURE:
|
||||
readingTemperature.setValue(data);
|
||||
break;
|
||||
case HUMIDITY:
|
||||
readingHumidity.setValue(data);
|
||||
break;
|
||||
case PRESSURE:
|
||||
readingPressure.setValue(data);
|
||||
break;
|
||||
case HEARTRATE:
|
||||
readingHeartRate.setValue(data);
|
||||
break;
|
||||
case LIGHT:
|
||||
readingLight.setValue(data);
|
||||
break;
|
||||
case STEPS:
|
||||
readingSteps.setValue(data);
|
||||
break;
|
||||
case CALORIES:
|
||||
readingCalories.setValue(data);
|
||||
break;
|
||||
case ACCELERATION:
|
||||
final String[] accelerationReadings = data.split(";");
|
||||
readingAcceleration.setFirstValue(accelerationReadings[0]);
|
||||
readingAcceleration.setSecondValue(accelerationReadings[1]);
|
||||
readingAcceleration.setThirdValue(accelerationReadings[2]);
|
||||
break;
|
||||
case MAGNET:
|
||||
final String[] magnetReadings = data.split(";");
|
||||
readingMagnet.setFirstValue(magnetReadings[0]);
|
||||
readingMagnet.setSecondValue(magnetReadings[1]);
|
||||
readingMagnet.setThirdValue(magnetReadings[2]);
|
||||
break;
|
||||
case GYRO:
|
||||
final String[] gyroscopeReadings = data.split(";");
|
||||
readingGyro.setFirstValue(gyroscopeReadings[0]);
|
||||
readingGyro.setSecondValue(gyroscopeReadings[1]);
|
||||
readingGyro.setThirdValue(gyroscopeReadings[2]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Receiver(actions = BluetoothService.STOP)
|
||||
void onStopReading() {
|
||||
Log.i(TAG, "Stop command received. Finishing...");
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
final boolean shouldTransmit = hexiwearDevices.shouldTransmit(device);
|
||||
final int icon = shouldTransmit ? R.drawable.ic_cloud_queue_white_48dp : R.drawable.ic_cloud_off_white_48dp;
|
||||
menu.getItem(0).setIcon(icon);
|
||||
return super.onPrepareOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@OptionsItem
|
||||
void openSettings() {
|
||||
SettingsActivity_.intent(this).device(hexiwearDevice).manufacturerInfo(bluetoothService.getManufacturerInfo()).start();
|
||||
}
|
||||
|
||||
@OptionsItem
|
||||
void setTime() {
|
||||
bluetoothService.setTime();
|
||||
}
|
||||
|
||||
@OptionsItem
|
||||
void toggleTracking() {
|
||||
if (credentials.username().get().equals("Demo")) {
|
||||
return;
|
||||
}
|
||||
|
||||
hexiwearDevices.toggleTracking(device);
|
||||
final boolean shouldTransmit = hexiwearDevices.shouldTransmit(device);
|
||||
bluetoothService.setTracking(shouldTransmit);
|
||||
supportInvalidateOptionsMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (isTaskRoot()) {
|
||||
MainActivity_.intent(this).start();
|
||||
}
|
||||
BluetoothService_.intent(this).stop();
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.activity;
|
||||
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
import com.wolkabout.hexiwear.util.Dialog;
|
||||
import com.wolkabout.hexiwear.view.Input;
|
||||
import com.wolkabout.wolkrestandroid.dto.ResetPasswordRequest;
|
||||
import com.wolkabout.wolkrestandroid.service.AuthenticationService;
|
||||
|
||||
import org.androidannotations.annotations.Background;
|
||||
import org.androidannotations.annotations.Bean;
|
||||
import org.androidannotations.annotations.Click;
|
||||
import org.androidannotations.annotations.EActivity;
|
||||
import org.androidannotations.annotations.EditorAction;
|
||||
import org.androidannotations.annotations.ViewById;
|
||||
import org.androidannotations.rest.spring.annotations.RestService;
|
||||
|
||||
@EActivity(R.layout.activity_reset_password)
|
||||
public class ResetPasswordActivity extends AppCompatActivity {
|
||||
|
||||
private static final String TAG = ResetPasswordActivity.class.getSimpleName();
|
||||
|
||||
@ViewById
|
||||
Input email;
|
||||
|
||||
@Bean
|
||||
Dialog dialog;
|
||||
|
||||
@RestService
|
||||
AuthenticationService authenticationService;
|
||||
|
||||
@Click
|
||||
@EditorAction(R.id.email)
|
||||
@Background
|
||||
void resetPassword() {
|
||||
try {
|
||||
authenticationService.resetPassword(new ResetPasswordRequest(email.getValue()));
|
||||
dialog.showInfo(R.string.reset_password_success, true);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Couldn't reset password", e);
|
||||
dialog.showWarning(R.string.reset_password_error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.activity;
|
||||
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
import com.wolkabout.hexiwear.model.HexiwearDevice;
|
||||
import com.wolkabout.hexiwear.model.ManufacturerInfo;
|
||||
|
||||
import org.androidannotations.annotations.AfterViews;
|
||||
import org.androidannotations.annotations.EActivity;
|
||||
import org.androidannotations.annotations.Extra;
|
||||
import org.androidannotations.annotations.ViewById;
|
||||
|
||||
@EActivity(R.layout.activity_settings)
|
||||
public class SettingsActivity extends AppCompatActivity {
|
||||
|
||||
@Extra
|
||||
public HexiwearDevice device;
|
||||
|
||||
@Extra
|
||||
public ManufacturerInfo manufacturerInfo;
|
||||
|
||||
@ViewById
|
||||
Toolbar toolbar;
|
||||
|
||||
@AfterViews
|
||||
void setToolbar() {
|
||||
setSupportActionBar(toolbar);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.activity;
|
||||
|
||||
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
import com.wolkabout.hexiwear.util.Dialog;
|
||||
import com.wolkabout.hexiwear.view.Input;
|
||||
import com.wolkabout.wolkrestandroid.dto.SignUpDto;
|
||||
import com.wolkabout.wolkrestandroid.service.AuthenticationService;
|
||||
|
||||
import org.androidannotations.annotations.AfterViews;
|
||||
import org.androidannotations.annotations.Background;
|
||||
import org.androidannotations.annotations.Bean;
|
||||
import org.androidannotations.annotations.Click;
|
||||
import org.androidannotations.annotations.EActivity;
|
||||
import org.androidannotations.annotations.UiThread;
|
||||
import org.androidannotations.annotations.ViewById;
|
||||
import org.androidannotations.rest.spring.annotations.RestService;
|
||||
|
||||
@EActivity(R.layout.activity_sign_up)
|
||||
public class SignUpActivity extends AppCompatActivity {
|
||||
|
||||
@ViewById
|
||||
Input emailField;
|
||||
|
||||
@ViewById
|
||||
Input passwordField;
|
||||
|
||||
@ViewById
|
||||
Input firstNameField;
|
||||
|
||||
@ViewById
|
||||
Input lastNameField;
|
||||
|
||||
@ViewById
|
||||
CheckBox termsAndConditions;
|
||||
|
||||
@Bean
|
||||
Dialog dialog;
|
||||
|
||||
@RestService
|
||||
AuthenticationService authenticationService;
|
||||
|
||||
@AfterViews
|
||||
void init() {
|
||||
termsAndConditions.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
|
||||
@Click(R.id.signUpButton)
|
||||
@Background
|
||||
void signUp() {
|
||||
if (!validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!termsAndConditions.isChecked()) {
|
||||
Toast.makeText(this, R.string.registration_accept_terms, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
final SignUpDto signUpDto = parseUserInput();
|
||||
try {
|
||||
authenticationService.signUp(signUpDto);
|
||||
onSignUpSuccess();
|
||||
} catch (Exception e) {
|
||||
onSignUpError();
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void onSignUpSuccess() {
|
||||
dialog.showInfo(R.string.registration_success, true);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void onSignUpError() {
|
||||
Toast.makeText(this, R.string.registration_failed, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
private boolean validate() {
|
||||
if (emailField.isEmpty()) {
|
||||
emailField.setError(R.string.registration_error_field_required);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (passwordField.isEmpty()) {
|
||||
passwordField.setError(R.string.registration_error_field_required);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (passwordField.getValue().length() < 8) {
|
||||
passwordField.setError(R.string.registration_error_invalid_password);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!android.util.Patterns.EMAIL_ADDRESS.matcher(emailField.getValue()).matches()) {
|
||||
emailField.setError(R.string.registration_error_invalid_email);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (firstNameField.isEmpty()) {
|
||||
firstNameField.setError(R.string.registration_error_field_required);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lastNameField.isEmpty()) {
|
||||
lastNameField.setError(R.string.registration_error_field_required);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private SignUpDto parseUserInput() {
|
||||
final String email = emailField.getValue();
|
||||
final String password = passwordField.getValue();
|
||||
final String firstName = firstNameField.getValue();
|
||||
final String lastName = lastNameField.getValue();
|
||||
return new SignUpDto(email, password, firstName, lastName);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.adapter;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
import com.wolkabout.hexiwear.model.BluetoothDeviceWrapper;
|
||||
import com.wolkabout.hexiwear.model.HexiwearDevice;
|
||||
import com.wolkabout.hexiwear.util.HexiwearDevices;
|
||||
|
||||
import org.androidannotations.annotations.Bean;
|
||||
import org.androidannotations.annotations.EBean;
|
||||
import org.androidannotations.annotations.EViewGroup;
|
||||
import org.androidannotations.annotations.RootContext;
|
||||
import org.androidannotations.annotations.UiThread;
|
||||
import org.androidannotations.annotations.ViewById;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@EBean
|
||||
public class DeviceListAdapter extends BaseAdapter {
|
||||
|
||||
private final List<BluetoothDeviceWrapper> devices = new ArrayList<>();
|
||||
|
||||
@RootContext
|
||||
Context context;
|
||||
|
||||
@Bean
|
||||
static HexiwearDevices hexiwearDevices;
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return devices.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BluetoothDeviceWrapper getItem(int position) {
|
||||
return devices.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void add(BluetoothDeviceWrapper wrapper) {
|
||||
for (BluetoothDeviceWrapper deviceWrapper : devices) {
|
||||
if(deviceWrapper.getDevice().getAddress().equals(wrapper.getDevice().getAddress())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
devices.add(wrapper);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void clear() {
|
||||
devices.clear();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
DeviceItemView deviceItemView;
|
||||
if (convertView == null) {
|
||||
deviceItemView = DeviceListAdapter_.DeviceItemView_.build(context);
|
||||
} else {
|
||||
deviceItemView = (DeviceItemView) convertView;
|
||||
}
|
||||
deviceItemView.bind(getItem(position));
|
||||
return deviceItemView;
|
||||
}
|
||||
|
||||
@EViewGroup(R.layout.item_device)
|
||||
public static class DeviceItemView extends LinearLayout {
|
||||
|
||||
@ViewById
|
||||
TextView deviceName;
|
||||
|
||||
@ViewById
|
||||
TextView deviceUUID;
|
||||
|
||||
@ViewById
|
||||
TextView status;
|
||||
|
||||
@ViewById
|
||||
ImageView signalStrength;
|
||||
|
||||
@ViewById
|
||||
TextView otapMode;
|
||||
|
||||
public DeviceItemView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
void bind(BluetoothDeviceWrapper wrapper) {
|
||||
final BluetoothDevice device = wrapper.getDevice();
|
||||
|
||||
final String name = device.getName();
|
||||
final HexiwearDevice hexiwearDevice = hexiwearDevices.getDevice(device.getAddress());
|
||||
if (hexiwearDevice != null) {
|
||||
deviceName.setText(hexiwearDevice.getWolkName());
|
||||
} else if (!TextUtils.isEmpty(name)) {
|
||||
deviceName.setText(name);
|
||||
} else {
|
||||
deviceName.setText(device.getAddress());
|
||||
}
|
||||
|
||||
status.setVisibility(device.getBondState() == BluetoothDevice.BOND_BONDED ? VISIBLE : GONE);
|
||||
|
||||
deviceUUID.setText(device.getAddress());
|
||||
signalStrength.setImageResource(wrapper.getSignalStrength());
|
||||
otapMode.setVisibility(wrapper.isInOtapMode() ? VISIBLE : GONE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
import com.wolkabout.hexiwear.model.otap.Image;
|
||||
|
||||
import org.androidannotations.annotations.EBean;
|
||||
import org.androidannotations.annotations.EViewGroup;
|
||||
import org.androidannotations.annotations.RootContext;
|
||||
import org.androidannotations.annotations.UiThread;
|
||||
import org.androidannotations.annotations.ViewById;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@EBean
|
||||
public class FirmwareListAdapter extends BaseAdapter {
|
||||
|
||||
@RootContext
|
||||
Context context;
|
||||
|
||||
private final List<Image> images = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return images.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image getItem(final int position) {
|
||||
return images.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(final int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final int position, final View convertView, final ViewGroup parent) {
|
||||
FirmwareItemView firmwareItemView;
|
||||
if (convertView == null) {
|
||||
firmwareItemView = FirmwareListAdapter_.FirmwareItemView_.build(context);
|
||||
} else {
|
||||
firmwareItemView = (FirmwareItemView) convertView;
|
||||
}
|
||||
firmwareItemView.bind(getItem(position));
|
||||
return firmwareItemView;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
images.clear();
|
||||
refreshList();
|
||||
}
|
||||
|
||||
public void add(final Image image) {
|
||||
images.add(image);
|
||||
refreshList();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void refreshList() {
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@EViewGroup(R.layout.item_firmware)
|
||||
public static class FirmwareItemView extends LinearLayout {
|
||||
|
||||
@ViewById
|
||||
TextView fileName;
|
||||
|
||||
@ViewById
|
||||
TextView version;
|
||||
|
||||
@ViewById
|
||||
TextView type;
|
||||
|
||||
public FirmwareItemView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
void bind(final Image image) {
|
||||
fileName.setText(image.getFileName());
|
||||
version.setText(formatValue(R.string.firmware_update_version, image.getVersion()));
|
||||
type.setText(formatValue(R.string.firmware_update_type, image.getType().name()));
|
||||
}
|
||||
|
||||
private String formatValue(final int stringResource, final String value) {
|
||||
return String.format(Locale.ENGLISH, getContext().getString(stringResource), value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.Checkable;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
import com.wolkabout.hexiwear.model.HexiwearDevice;
|
||||
|
||||
import org.androidannotations.annotations.EBean;
|
||||
import org.androidannotations.annotations.EViewGroup;
|
||||
import org.androidannotations.annotations.RootContext;
|
||||
import org.androidannotations.annotations.UiThread;
|
||||
import org.androidannotations.annotations.ViewById;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@EBean
|
||||
public class HexiwearListAdapter extends BaseAdapter {
|
||||
|
||||
private final List<HexiwearDevice> devices = new ArrayList<>();
|
||||
|
||||
@RootContext
|
||||
Context context;
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return devices.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HexiwearDevice getItem(int position) {
|
||||
return devices.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
HexiwearItemView hexiwearItemView;
|
||||
if (convertView == null) {
|
||||
hexiwearItemView = HexiwearListAdapter_.HexiwearItemView_.build(context);
|
||||
} else {
|
||||
hexiwearItemView = (HexiwearItemView) convertView;
|
||||
}
|
||||
hexiwearItemView.bind(getItem(position));
|
||||
return hexiwearItemView;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void update(List<HexiwearDevice> devices) {
|
||||
this.devices.clear();
|
||||
this.devices.add(new HexiwearDevice());
|
||||
this.devices.addAll(devices);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@EViewGroup(R.layout.item_hexiwear)
|
||||
static class HexiwearItemView extends LinearLayout implements Checkable {
|
||||
|
||||
@ViewById
|
||||
ImageView icon;
|
||||
|
||||
@ViewById
|
||||
TextView deviceName;
|
||||
|
||||
private boolean checked;
|
||||
|
||||
public HexiwearItemView(final Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public void bind(HexiwearDevice device) {
|
||||
final String wolkName = device.getWolkName();
|
||||
final boolean isNewDevice = TextUtils.isEmpty(wolkName);
|
||||
deviceName.setText(isNewDevice ? getContext().getString(R.string.activation_dialog_new_device) : wolkName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChecked(final boolean checked) {
|
||||
this.checked = checked;
|
||||
onCheckedChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
return checked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggle() {
|
||||
checked = !checked;
|
||||
onCheckedChanged();
|
||||
}
|
||||
|
||||
private void onCheckedChanged() {
|
||||
icon.setBackgroundResource(checked ? R.drawable.ic_arrow_forward_black_24dp : 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.fragment;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceFragment;
|
||||
import android.preference.SwitchPreference;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
import com.wolkabout.hexiwear.activity.SettingsActivity;
|
||||
import com.wolkabout.hexiwear.model.HexiwearDevice;
|
||||
import com.wolkabout.hexiwear.service.BluetoothService;
|
||||
import com.wolkabout.hexiwear.service.BluetoothService_;
|
||||
import com.wolkabout.hexiwear.util.HexiwearDevices;
|
||||
import com.wolkabout.wolkrestandroid.Credentials_;
|
||||
|
||||
import org.androidannotations.annotations.AfterPreferences;
|
||||
import org.androidannotations.annotations.Bean;
|
||||
import org.androidannotations.annotations.EFragment;
|
||||
import org.androidannotations.annotations.PreferenceByKey;
|
||||
import org.androidannotations.annotations.PreferenceScreen;
|
||||
import org.androidannotations.annotations.sharedpreferences.Pref;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
@PreferenceScreen(R.xml.pref_hexiwear_settings)
|
||||
@EFragment
|
||||
public class HexiwearSettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener, Preference.OnPreferenceChangeListener {
|
||||
|
||||
private static final String TAG = HexiwearSettingsFragment.class.getSimpleName();
|
||||
|
||||
@Bean
|
||||
HexiwearDevices hexiwearDevices;
|
||||
|
||||
@PreferenceByKey(R.string.preferences_manufacturer_info)
|
||||
Preference manufacturerInfo;
|
||||
|
||||
@PreferenceByKey(R.string.preferences_fw_version)
|
||||
Preference fwVersion;
|
||||
|
||||
@PreferenceByKey(R.string.preferences_publish_interval_key)
|
||||
Preference publishInterval;
|
||||
|
||||
@PreferenceByKey(R.string.preferences_keep_alive_key)
|
||||
SwitchPreference keepAlive;
|
||||
|
||||
@PreferenceByKey(R.string.preferences_publish_key)
|
||||
SwitchPreference publish;
|
||||
|
||||
@Pref
|
||||
Credentials_ credentials;
|
||||
|
||||
private HexiwearDevice device;
|
||||
private Map<String, Boolean> displayPreferences;
|
||||
|
||||
@AfterPreferences
|
||||
void initPrefs() {
|
||||
SettingsActivity settingsActivity = (SettingsActivity) getActivity();
|
||||
device = settingsActivity.device;
|
||||
displayPreferences = hexiwearDevices.getDisplayPreferences(device.getDeviceAddress());
|
||||
|
||||
for (Map.Entry<String, Boolean> entry : displayPreferences.entrySet()) {
|
||||
Log.d(TAG, "Key: " + entry.getKey() + " value " + entry.getValue());
|
||||
final SwitchPreference displayPref = (SwitchPreference) findPreference(entry.getKey());
|
||||
if (displayPref != null) {
|
||||
displayPref.setChecked(entry.getValue());
|
||||
displayPref.setOnPreferenceChangeListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
final String interval = String.format(getActivity().getString(R.string.preferences_publish_interval_value), hexiwearDevices.getPublishInterval(device));
|
||||
publishInterval.setSummary(interval);
|
||||
publishInterval.setOnPreferenceChangeListener(this);
|
||||
keepAlive.setChecked(hexiwearDevices.shouldKeepAlive(device));
|
||||
keepAlive.setOnPreferenceChangeListener(this);
|
||||
publish.setChecked(hexiwearDevices.shouldTransmit(device));
|
||||
publish.setOnPreferenceChangeListener(this);
|
||||
manufacturerInfo.setSummary(settingsActivity.manufacturerInfo.manufacturer);
|
||||
fwVersion.setSummary(settingsActivity.manufacturerInfo.firmwareRevision);
|
||||
|
||||
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
final boolean isDemo = credentials.username().get().equals("Demo");
|
||||
if (preference == publishInterval) {
|
||||
if (isDemo) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Log.d(TAG, "Publishing time changed. New value is: " + newValue);
|
||||
hexiwearDevices.setPublishInterval(device, Integer.valueOf((String) newValue));
|
||||
final String interval = String.format(getActivity().getString(R.string.preferences_publish_interval_value), hexiwearDevices.getPublishInterval(device));
|
||||
publishInterval.setSummary(interval);
|
||||
LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(new Intent(BluetoothService.PUBLISH_TIME_CHANGED));
|
||||
} else if (preference == keepAlive) {
|
||||
Log.d(TAG, "Keep alive changed. New value: " + newValue);
|
||||
final boolean shouldKeepAlive = (boolean) newValue;
|
||||
hexiwearDevices.setKeepAlive(device, shouldKeepAlive);
|
||||
if (shouldKeepAlive) {
|
||||
BluetoothService_.intent(getActivity()).start();
|
||||
} else {
|
||||
BluetoothService_.intent(getActivity()).stop();
|
||||
}
|
||||
} else if (preference == publish) {
|
||||
if (isDemo) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Log.d(TAG, "Should publish changed. New value: " + newValue);
|
||||
hexiwearDevices.toggleTracking(device);
|
||||
LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(new Intent(BluetoothService.SHOULD_PUBLISH_CHANGED));
|
||||
} else {
|
||||
Log.d(TAG, "Key: " + preference.getKey() + " value " + newValue);
|
||||
displayPreferences.put(preference.getKey(), (Boolean) newValue);
|
||||
hexiwearDevices.setDisplayPreferences(device.getDeviceAddress(), displayPreferences);
|
||||
|
||||
final Intent preferenceChanged = new Intent(BluetoothService.PREFERENCE_CHANGED);
|
||||
preferenceChanged.putExtra(BluetoothService.PREFERENCE_NAME, preference.getKey());
|
||||
preferenceChanged.putExtra(BluetoothService.PREFERENCE_ENABLED, (boolean) newValue);
|
||||
LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(preferenceChanged);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
// Preferences are changed on this screen only.
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.model;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
|
||||
import org.parceler.Parcel;
|
||||
|
||||
@Parcel
|
||||
public class BluetoothDeviceWrapper {
|
||||
|
||||
BluetoothDevice device;
|
||||
|
||||
int signalStrength;
|
||||
|
||||
boolean isInOtapMode;
|
||||
|
||||
public BluetoothDevice getDevice() {
|
||||
return device;
|
||||
}
|
||||
|
||||
public void setDevice(BluetoothDevice device) {
|
||||
this.device = device;
|
||||
}
|
||||
|
||||
public int getSignalStrength() {
|
||||
if (signalStrength > -50.0) {
|
||||
return R.drawable.ic_signal_strength_5;
|
||||
} else if (signalStrength > -60.0) {
|
||||
return R.drawable.ic_signal_strength_4;
|
||||
} else if (signalStrength > -70.0) {
|
||||
return R.drawable.ic_signal_strength_3;
|
||||
} else if (signalStrength > -80.0) {
|
||||
return R.drawable.ic_signal_strength_2;
|
||||
} else {
|
||||
return R.drawable.ic_signal_strength_1;
|
||||
}
|
||||
}
|
||||
|
||||
public void setSignalStrength(int signalStrength) {
|
||||
this.signalStrength = signalStrength;
|
||||
}
|
||||
|
||||
public boolean isInOtapMode() {
|
||||
return isInOtapMode;
|
||||
}
|
||||
|
||||
public void setInOtapMode(boolean inOtapMode) {
|
||||
isInOtapMode = inOtapMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BluetoothDeviceWrapper{" +
|
||||
"device=" + device +
|
||||
", signalStrength=" + signalStrength +
|
||||
", isInOtapMode=" + isInOtapMode +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public enum Characteristic {
|
||||
|
||||
ACCELERATION(Type.READING, "00002001-0000-1000-8000-00805f9b34fb", "g"),
|
||||
GYRO(Type.READING, "00002002-0000-1000-8000-00805f9b34fb", "\u00B0/s"),
|
||||
MAGNET(Type.READING, "00002003-0000-1000-8000-00805f9b34fb", "\u00B5T"),
|
||||
LIGHT(Type.READING, "00002011-0000-1000-8000-00805f9b34fb", "%"),
|
||||
TEMPERATURE(Type.READING, "00002012-0000-1000-8000-00805f9b34fb", "\u2103"),
|
||||
HUMIDITY(Type.READING, "00002013-0000-1000-8000-00805f9b34fb", "%"),
|
||||
PRESSURE(Type.READING, "00002014-0000-1000-8000-00805f9b34fb", "kPa"),
|
||||
BATTERY(Type.READING, "00002a19-0000-1000-8000-00805f9b34fb", "%"),
|
||||
HEARTRATE(Type.READING, "00002021-0000-1000-8000-00805f9b34fb", "bpm"),
|
||||
STEPS(Type.READING, "00002022-0000-1000-8000-00805f9b34fb", ""),
|
||||
CALORIES(Type.READING, "00002023-0000-1000-8000-00805f9b34fb", ""),
|
||||
|
||||
ALERT_IN(Type.ALERT, "00002031-0000-1000-8000-00805f9b34fb"),
|
||||
ALERT_OUT(Type.ALERT, "00002032-0000-1000-8000-00805f9b34fb"),
|
||||
|
||||
MODE(Type.MODE, "00002041-0000-1000-8000-00805f9b34fb"),
|
||||
|
||||
SERIAL(Type.INFO, "00002a25-0000-1000-8000-00805f9b34fb"),
|
||||
FW_REVISION(Type.INFO, "00002a26-0000-1000-8000-00805f9b34fb"),
|
||||
HW_REVISION(Type.INFO, "00002a27-0000-1000-8000-00805f9b34fb"),
|
||||
MANUFACTURER(Type.INFO, "00002a29-0000-1000-8000-00805f9b34fb"),
|
||||
|
||||
CONTROL_POINT(Type.OTAP, "01ff5551-ba5e-f4ee-5ca1-eb1e5e4b1ce0"),
|
||||
DATA(Type.OTAP, "01ff5552-ba5e-f4ee-5ca1-eb1e5e4b1ce0"),
|
||||
STATE(Type.OTAP, "01ff5553-ba5e-f4ee-5ca1-eb1e5e4b1ce0");
|
||||
|
||||
private final Type type;
|
||||
private final String uuid;
|
||||
private final String unit;
|
||||
|
||||
Characteristic(final Type type, final String uuid) {
|
||||
this.type = type;
|
||||
this.uuid = uuid;
|
||||
this.unit = "";
|
||||
}
|
||||
|
||||
Characteristic(final Type type, final String uuid, final String unit) {
|
||||
this.type = type;
|
||||
this.uuid = uuid;
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public String getUnit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
public static Characteristic byUuid(final String uuid) {
|
||||
for (final Characteristic characteristic : values()) {
|
||||
if (characteristic.uuid.equals(uuid)) {
|
||||
return characteristic;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Characteristic byOrdinal(final int ordinal) {
|
||||
return values()[ordinal];
|
||||
}
|
||||
|
||||
public static List<Characteristic> getReadings() {
|
||||
final List<Characteristic> readingCharacteristics = new ArrayList<>();
|
||||
for (Characteristic characteristic : values()) {
|
||||
if (characteristic.type == Type.READING) {
|
||||
readingCharacteristics.add(characteristic);
|
||||
}
|
||||
}
|
||||
return readingCharacteristics;
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
READING, ALERT, MODE, INFO, OTAP
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.model;
|
||||
|
||||
import com.wolkabout.wolk.Device;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
public class HexiwearDevice extends Device implements Serializable {
|
||||
|
||||
private String deviceName;
|
||||
private String deviceAddress;
|
||||
private String wolkName;
|
||||
|
||||
public HexiwearDevice() {}
|
||||
|
||||
public HexiwearDevice(String deviceName, String deviceSerial, String deviceAddress, String devicePassword, String wolkName) {
|
||||
this.deviceName = deviceName;
|
||||
this.serialId = deviceSerial;
|
||||
this.deviceAddress = deviceAddress;
|
||||
this.wolkName = wolkName;
|
||||
this.password = devicePassword;
|
||||
}
|
||||
|
||||
public String getDeviceName() {
|
||||
return deviceName;
|
||||
}
|
||||
|
||||
public String getDeviceSerial() {
|
||||
return serialId;
|
||||
}
|
||||
|
||||
public String getDeviceAddress() {
|
||||
return deviceAddress;
|
||||
}
|
||||
|
||||
public String getWolkName() {
|
||||
return wolkName;
|
||||
}
|
||||
|
||||
public String getDevicePassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HexiwearDevice{" +
|
||||
"deviceName='" + deviceName + '\'' +
|
||||
", deviceSerial='" + serialId + '\'' +
|
||||
", deviceAddress='" + deviceAddress + '\'' +
|
||||
", devicePassword='" + password + '\'' +
|
||||
", wolkName='" + wolkName + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.wolkabout.hexiwear.model;
|
||||
|
||||
import org.parceler.Parcel;
|
||||
|
||||
@Parcel
|
||||
public class ManufacturerInfo {
|
||||
public String manufacturer;
|
||||
public String hardwareRevision;
|
||||
public String firmwareRevision;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ManufacturerInfo{" +
|
||||
"manufacturer='" + manufacturer + '\'' +
|
||||
", hardwareRevision='" + hardwareRevision + '\'' +
|
||||
", firmwareRevision='" + firmwareRevision + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.model;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public enum Mode {
|
||||
|
||||
IDLE(0, R.string.mode_idle),
|
||||
WATCH(1, R.string.mode_watch),
|
||||
SENSOR_TAG(2, R.string.mode_sensor_tag),
|
||||
WEATHER_STATION(3, R.string.mode_weather_station),
|
||||
MOTION_CONTROL(4, R.string.mode_motion_control),
|
||||
HEARTRATE(5, R.string.mode_heartrate),
|
||||
PEDOMETER(6, R.string.mode_pedometer),
|
||||
COMPASS(7, R.string.mode_compass);
|
||||
|
||||
private final int symbol;
|
||||
private final int stringResource;
|
||||
|
||||
Mode(final int symbol, final int stringResource) {
|
||||
this.symbol = symbol;
|
||||
this.stringResource = stringResource;
|
||||
}
|
||||
|
||||
public int getStringResource() {
|
||||
return stringResource;
|
||||
}
|
||||
|
||||
public static Mode bySymbol(final int symbol) {
|
||||
for (Mode mode : values()) {
|
||||
if (mode.symbol == symbol) {
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("No mode with such symbol: " + symbol);
|
||||
}
|
||||
|
||||
public List<Characteristic> getCharacteristics() {
|
||||
final List<Characteristic> characteristics = new ArrayList<>();
|
||||
|
||||
switch (this) {
|
||||
case SENSOR_TAG:
|
||||
characteristics.add(Characteristic.BATTERY);
|
||||
characteristics.add(Characteristic.ACCELERATION);
|
||||
characteristics.add(Characteristic.MAGNET);
|
||||
characteristics.add(Characteristic.GYRO);
|
||||
characteristics.add(Characteristic.TEMPERATURE);
|
||||
characteristics.add(Characteristic.HUMIDITY);
|
||||
characteristics.add(Characteristic.PRESSURE);
|
||||
characteristics.add(Characteristic.LIGHT);
|
||||
break;
|
||||
case PEDOMETER:
|
||||
characteristics.add(Characteristic.STEPS);
|
||||
characteristics.add(Characteristic.CALORIES);
|
||||
break;
|
||||
case HEARTRATE:
|
||||
characteristics.add(Characteristic.HEARTRATE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return characteristics;
|
||||
}
|
||||
|
||||
public boolean hasCharacteristic(final String characteristicName) {
|
||||
return hasCharacteristic(Characteristic.valueOf(characteristicName));
|
||||
}
|
||||
|
||||
public boolean hasCharacteristic(final Characteristic characteristic) {
|
||||
final List<Characteristic> characteristics = getCharacteristics();
|
||||
return characteristics.contains(characteristic);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.model.otap;
|
||||
|
||||
public enum Command {
|
||||
|
||||
NEW_IMAGE_NOTIFICATION(1),
|
||||
NEW_IMAGE_INFO_REQUEST(2),
|
||||
NEW_IMAGE_INFO_RESPONSE(3),
|
||||
IMAGE_BLOCK_REQUEST(4),
|
||||
IMAGE_CHUNK(5),
|
||||
IMAGE_TRANSFER_COMPLETE(6),
|
||||
ERROR_NOTIFICATION(7),
|
||||
STOP_IMAGE_TRANSFER(8);
|
||||
|
||||
private final byte commandByte;
|
||||
|
||||
Command(final int commandByte) {
|
||||
this.commandByte = (byte) commandByte;
|
||||
}
|
||||
|
||||
public byte getCommandByte() {
|
||||
return commandByte;
|
||||
}
|
||||
|
||||
public static Command byCommandByte(final byte commandByte) {
|
||||
for (Command command : values()) {
|
||||
if (command.getCommandByte() == commandByte) {
|
||||
return command;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown command: " + commandByte);
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.model.otap;
|
||||
|
||||
import com.wolkabout.hexiwear.model.otap.response.ImageBlockRequest;
|
||||
import com.wolkabout.hexiwear.util.ByteInputStream;
|
||||
import com.wolkabout.hexiwear.util.ByteUtils;
|
||||
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class Image {
|
||||
|
||||
private final String fileName;
|
||||
private final ImageHeader header;
|
||||
|
||||
byte[] data;
|
||||
|
||||
public Image(final String fileName, final byte[] bytes) {
|
||||
this.fileName = fileName;
|
||||
final ByteInputStream inputStream = new ByteInputStream(bytes);
|
||||
header = new ImageHeader(inputStream.nextBytes(ImageHeader.SIZE));
|
||||
data = bytes;
|
||||
}
|
||||
|
||||
public byte[] getNewImageInfoResponse() {
|
||||
final ByteBuffer buffer = ByteBuffer.allocate(15);
|
||||
buffer.put(Command.NEW_IMAGE_INFO_RESPONSE.getCommandByte());
|
||||
buffer.put(header.getImageId());
|
||||
buffer.put(header.getImageVersion().getRawData());
|
||||
buffer.put(header.getTotalImageFileSize());
|
||||
return buffer.array();
|
||||
}
|
||||
|
||||
public ImageBlock getBlock(final ImageBlockRequest imageBlockRequest) {
|
||||
final ByteInputStream inputStream = new ByteInputStream(data, imageBlockRequest.getStartPosition());
|
||||
final byte[] blockData = inputStream.nextBytes(imageBlockRequest.getBlockSize());
|
||||
return new ImageBlock(blockData, imageBlockRequest);
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return header.getImageVersion().getBuildVersion();
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return ByteUtils.parseLong(header.getTotalImageFileSize());
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return Type.fromBytes(header.getImageId());
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
KW40, MK64;
|
||||
|
||||
public static Type fromBytes(final byte[] bytes) {
|
||||
final int id = (int) ByteUtils.parseLong(bytes);
|
||||
switch (id) {
|
||||
case 1:
|
||||
return KW40;
|
||||
case 2:
|
||||
return MK64;
|
||||
default:
|
||||
throw new IllegalArgumentException("No such type: " + id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.model.otap;
|
||||
|
||||
import com.wolkabout.hexiwear.model.otap.response.ImageBlockRequest;
|
||||
import com.wolkabout.hexiwear.util.ByteInputStream;
|
||||
import com.wolkabout.hexiwear.util.ByteUtils;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ImageBlock {
|
||||
|
||||
private final List<byte[]> chunks = new ArrayList<>();
|
||||
private final int numberOfChunks;
|
||||
private int position = 0;
|
||||
|
||||
public ImageBlock(final byte[] data, final ImageBlockRequest imageBlockRequest) {
|
||||
final int chunkSize = imageBlockRequest.getChunkSize();
|
||||
numberOfChunks = (int) Math.ceil((double) imageBlockRequest.getBlockSize() / chunkSize);
|
||||
final ByteInputStream inputStream = new ByteInputStream(data);
|
||||
for (int i = 0; i < numberOfChunks - 1; i++) {
|
||||
chunks.add(inputStream.nextBytes(chunkSize));
|
||||
}
|
||||
chunks.add(inputStream.remainingBytes());
|
||||
}
|
||||
|
||||
public byte[] getNextChunk() {
|
||||
final byte[] nextChunk = chunks.get(position);
|
||||
final ByteBuffer buffer = ByteBuffer.allocate(nextChunk.length + 2);
|
||||
buffer.put(Command.IMAGE_CHUNK.getCommandByte());
|
||||
buffer.put(ByteUtils.intToByte(position));
|
||||
buffer.put(nextChunk);
|
||||
position++;
|
||||
return buffer.array();
|
||||
}
|
||||
|
||||
public boolean isCompleted() {
|
||||
return position == numberOfChunks;
|
||||
}
|
||||
|
||||
public int getNumberOfChunks() {
|
||||
return numberOfChunks;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position + 1;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.model.otap;
|
||||
|
||||
import com.wolkabout.hexiwear.util.ByteInputStream;
|
||||
import com.wolkabout.hexiwear.util.ByteUtils;
|
||||
|
||||
import org.parceler.Parcel;
|
||||
|
||||
|
||||
public class ImageHeader {
|
||||
|
||||
public static final int SIZE = 58;
|
||||
|
||||
private final byte[] upgradeFileIdentifier;
|
||||
private final byte[] headerVersion;
|
||||
private final byte[] headerLength;
|
||||
private final byte[] headerFieldControl;
|
||||
private final byte[] companyIdentifier;
|
||||
private final byte[] imageId;
|
||||
private final ImageVersion imageVersion;
|
||||
private final byte[] headerString;
|
||||
private final byte[] totalImageFileSize;
|
||||
|
||||
public ImageHeader(final byte[] bytes) {
|
||||
if (bytes.length != SIZE) {
|
||||
throw new IllegalArgumentException("Header data must contain exactly " + SIZE + " bytes.");
|
||||
}
|
||||
|
||||
final ByteInputStream inputStream = new ByteInputStream(bytes);
|
||||
upgradeFileIdentifier = inputStream.nextBytes(4);
|
||||
headerVersion = inputStream.nextBytes(2);
|
||||
headerLength = inputStream.nextBytes(2);
|
||||
headerFieldControl = inputStream.nextBytes(2);
|
||||
companyIdentifier = inputStream.nextBytes(2);
|
||||
imageId = inputStream.nextBytes(2);
|
||||
imageVersion = new ImageVersion(inputStream.nextBytes(8));
|
||||
headerString = inputStream.nextBytes(32);
|
||||
totalImageFileSize = inputStream.nextBytes(4);
|
||||
}
|
||||
|
||||
public byte[] getUpgradeFileIdentifier() {
|
||||
return upgradeFileIdentifier;
|
||||
}
|
||||
|
||||
public byte[] getHeaderVersion() {
|
||||
return headerVersion;
|
||||
}
|
||||
|
||||
public byte[] getHeaderLength() {
|
||||
return headerLength;
|
||||
}
|
||||
|
||||
public byte[] getHeaderFieldControl() {
|
||||
return headerFieldControl;
|
||||
}
|
||||
|
||||
public byte[] getCompanyIdentifier() {
|
||||
return companyIdentifier;
|
||||
}
|
||||
|
||||
public byte[] getImageId() {
|
||||
return imageId;
|
||||
}
|
||||
|
||||
public ImageVersion getImageVersion() {
|
||||
return imageVersion;
|
||||
}
|
||||
|
||||
public byte[] getHeaderString() {
|
||||
return headerString;
|
||||
}
|
||||
|
||||
public byte[] getTotalImageFileSize() {
|
||||
return totalImageFileSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ImageHeader{" +
|
||||
"upgradeFileIdentifier=" + ByteUtils.parseLong(upgradeFileIdentifier) +
|
||||
", headerVersion=" + ByteUtils.parseLong(headerVersion) +
|
||||
", headerLength=" + ByteUtils.parseLong(headerLength) +
|
||||
", headerFieldControl=" + ByteUtils.parseLong(headerFieldControl) +
|
||||
", companyIdentifier=" + ByteUtils.parseLong(companyIdentifier) +
|
||||
", imageId=" + ByteUtils.parseLong(imageId) +
|
||||
", imageVersion=" + imageVersion +
|
||||
", headerString=" + ByteUtils.parseString(headerString) +
|
||||
", totalImageFileSize=" + ByteUtils.parseLong(totalImageFileSize) +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.model.otap;
|
||||
|
||||
import com.wolkabout.hexiwear.util.ByteInputStream;
|
||||
|
||||
import org.parceler.Parcel;
|
||||
|
||||
public class ImageVersion {
|
||||
|
||||
byte[] rawData;
|
||||
|
||||
private final String buildVersion;
|
||||
private final int stackVersion;
|
||||
private final String hardwareId;
|
||||
private final int endManufacturerId;
|
||||
|
||||
public ImageVersion(final byte[] bytes) {
|
||||
rawData = bytes;
|
||||
|
||||
final ByteInputStream inputStream = new ByteInputStream(bytes);
|
||||
buildVersion = inputStream.nextNumberArray(3, 1);
|
||||
stackVersion = inputStream.nextInt(1);
|
||||
hardwareId = inputStream.nextNumberArray(3, 1);
|
||||
endManufacturerId = inputStream.nextInt(1);
|
||||
}
|
||||
|
||||
public byte[] getRawData() {
|
||||
return rawData;
|
||||
}
|
||||
|
||||
public String getBuildVersion() {
|
||||
return buildVersion;
|
||||
}
|
||||
|
||||
public int getStackVersion() {
|
||||
return stackVersion;
|
||||
}
|
||||
|
||||
public String getHardwareId() {
|
||||
return hardwareId;
|
||||
}
|
||||
|
||||
public int getEndManufacturerId() {
|
||||
return endManufacturerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ImageVersion{" +
|
||||
"buildVersion=" + buildVersion +
|
||||
", stackVersion=" + stackVersion +
|
||||
", hardwareId=" + hardwareId +
|
||||
", endManufacturerId=" + endManufacturerId +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.model.otap.response;
|
||||
|
||||
import com.wolkabout.hexiwear.util.ByteInputStream;
|
||||
|
||||
public class ErrorNotification {
|
||||
|
||||
private final int commandId;
|
||||
private final ErrorStatus errorStatus;
|
||||
|
||||
public ErrorNotification(final byte[] bytes) {
|
||||
final ByteInputStream inputStream = new ByteInputStream(bytes);
|
||||
inputStream.nextBytes(1); // Skip command byte
|
||||
commandId = inputStream.nextInt(1);
|
||||
errorStatus = ErrorStatus.byValue(inputStream.nextInt(1));
|
||||
}
|
||||
|
||||
public int getCommandId() {
|
||||
return commandId;
|
||||
}
|
||||
|
||||
public ErrorStatus getErrorStatus() {
|
||||
return errorStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ErrorNotification{" +
|
||||
"commandId=" + commandId +
|
||||
", errorStatus=" + errorStatus +
|
||||
'}';
|
||||
}
|
||||
|
||||
public enum ErrorStatus {
|
||||
SUCCESS(0), /*!< The operation was successful. */
|
||||
IMAGE_DATA_NOT_EXPECTED(1), /*!< The OTAP Server tried to send an image data chunk to the OTAP Client but the Client was not expecting it. */
|
||||
UNEXPECTED_TRANSFER_METHOD(2), /*!< The OTAP Server tried to send an image data chunk using a transfer method the OTAP Client does not support/expect. */
|
||||
UNEXPECTED_COMMAND_ON_DATA_CHANNEL(3), /*!< The OTAP Server tried to send an unexpected command (different from a data chunk), on a data Channel (ATT or CoC), */
|
||||
UNEXPECTED_L2CAP_OR_PSM(4), /*!< The selected channel or PSM is not valid for the selected transfer method (ATT or CoC),. */
|
||||
UNEXPECTED_OTAP_PEER(5), /*!< A command was received from an unexpected OTAP Server or Client device. */
|
||||
UNEXPECTED_COMMAND(6), /*!< The command sent from the OTAP peer device is not expected in the current state. */
|
||||
UNKNOWN_COMMAND(7), /*!< The command sent from the OTAP peer device is not known. */
|
||||
INVALID_COMMAND_LENGTH(8), /*!< Invalid command length. */
|
||||
INVALID_COMMAND_PARAMETER(9), /*!< A parameter of the command was not valid. */
|
||||
FAILED_IMAGE_INTEGRITY_CHECK(10), /*!< The image integrity check has failed. */
|
||||
UNEXPECTED_SEQUENCE_NUMBER(11), /*!< A chunk with an unexpected sequence number has been received. */
|
||||
IMAGE_SIZE_TOO_LARGE(12), /*!< The upgrade image size is too large for the OTAP Client. */
|
||||
UNEXPECTED_DATA_LENGTH(13), /*!< The length of a Data Chunk was not expected. */
|
||||
UNKNOWN_FILE_IDENTIFIER(14), /*!< The image file identifier is not recognized. */
|
||||
UNKNOWN_HEADER_VERSION(15), /*!< The image file header version is not recognized. */
|
||||
UNEXPECTED_HEADER_LENGTH(0x16), /*!< The image file header length is not expected for the current header version. */
|
||||
UNEXPECTED_HEADER_FIELD_CONTROL(0x17), /*!< The image file header field control is not expected for the current header version. */
|
||||
UNKNOWN_COMPANY_ID(0x18), /*!< The image file header company identifier is not recognized. */
|
||||
UNEXPECTED_IMAGE_ID(0x19), /*!< The image file header image identifier is not as expected. */
|
||||
UNEXPECTED_IMAGE_VERSION(20), /*!< The image file header image version is not as expected. */
|
||||
UNEXPECTED_IMAGE_FILE_SIZE(21), /*!< The image file header image file size is not as expected. */
|
||||
INVALID_SUB_ELEMENT_LENGTH(22), /*!< One of the sub-elements has an invalid length. */
|
||||
IMAGE_STORAGE_ERROR(23), /*!< An image storage error has occurred. */
|
||||
INVALID_IMAGE_CRC(24), /*!< The computed CRC does not match the received CRC. */
|
||||
INVALID_IMAGE_FILE_SIZE(25), /*!< The image file size is not valid. */
|
||||
INVALID_L2CAP_PSM(26), /*!< A block transfer request has been made via the L2CAP CoC method but the specified Psm is not known. */
|
||||
NO_L2CAP_PSM_CONNECTION(27), /*!< A block transfer request has been made via the L2CAP CoC method but there is no valid PSM connection. */
|
||||
NUMBER_OF_STATUSES(28);
|
||||
|
||||
private final int value;
|
||||
|
||||
ErrorStatus(final int value) {
|
||||
this.value = (byte) value;
|
||||
}
|
||||
|
||||
public static ErrorStatus byValue(final int value) {
|
||||
for (ErrorStatus errorStatus : values()) {
|
||||
if (errorStatus.value == value) {
|
||||
return errorStatus;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.model.otap.response;
|
||||
|
||||
import com.wolkabout.hexiwear.util.ByteInputStream;
|
||||
|
||||
public class ImageBlockRequest {
|
||||
|
||||
private final int imageId; // Image ID
|
||||
private final int startPosition; // Start position of the image block to be transferred.
|
||||
private final int blockSize; // Requested total block size in bytes.
|
||||
private final int chunkSize; // Chunk size in bytes. Maximum of 256 chunks per block.
|
||||
private final int transferMethod; // 0 = ATT, 1 = L2CAP PSM Credit based channel
|
||||
private final int l2capChannelOrPsm; // 4 = ATT, Other values = PSM for credit based channels
|
||||
|
||||
public ImageBlockRequest(final byte[] bytes) {
|
||||
final ByteInputStream inputStream = new ByteInputStream(bytes);
|
||||
inputStream.nextBytes(1); // Skip command byte
|
||||
imageId = inputStream.nextInt(2);
|
||||
startPosition = inputStream.nextInt(4);
|
||||
blockSize = inputStream.nextInt(4);
|
||||
chunkSize = inputStream.nextInt(2);
|
||||
transferMethod = inputStream.nextInt(1);
|
||||
l2capChannelOrPsm = inputStream.nextInt(2);
|
||||
}
|
||||
|
||||
public int getImageId() {
|
||||
return imageId;
|
||||
}
|
||||
|
||||
public int getStartPosition() {
|
||||
return startPosition;
|
||||
}
|
||||
|
||||
public int getBlockSize() {
|
||||
return blockSize;
|
||||
}
|
||||
|
||||
public int getChunkSize() {
|
||||
return chunkSize;
|
||||
}
|
||||
|
||||
public int getTransferMethod() {
|
||||
return transferMethod;
|
||||
}
|
||||
|
||||
public int getL2capChannelOrPsm() {
|
||||
return l2capChannelOrPsm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ImageBlockRequest{" +
|
||||
"imageId=" + imageId +
|
||||
", startPosition=" + startPosition +
|
||||
", blockSize=" + blockSize +
|
||||
", chunkSize=" + chunkSize +
|
||||
", transferMethod=" + transferMethod +
|
||||
", l2capChannelOrPsm=" + l2capChannelOrPsm +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.model.otap.response;
|
||||
|
||||
import com.wolkabout.hexiwear.util.ByteInputStream;
|
||||
|
||||
public class ImageTransferComplete {
|
||||
|
||||
private final int imageId;
|
||||
private final int status; // 0 = Success
|
||||
|
||||
public ImageTransferComplete(final byte[] bytes) {
|
||||
final ByteInputStream inputStream = new ByteInputStream(bytes);
|
||||
inputStream.nextBytes(1); // Skip command byte
|
||||
imageId = inputStream.nextInt(2);
|
||||
status = inputStream.nextInt(1);
|
||||
}
|
||||
|
||||
public int getImageId() {
|
||||
return imageId;
|
||||
}
|
||||
|
||||
public boolean isSuccessful() {
|
||||
return status == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ImageTransferComplete{" +
|
||||
"imageId=" + imageId +
|
||||
", status=" + status +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.model.otap.response;
|
||||
|
||||
import com.wolkabout.hexiwear.model.otap.ImageVersion;
|
||||
import com.wolkabout.hexiwear.util.ByteInputStream;
|
||||
|
||||
public class NewImageInfoRequest {
|
||||
|
||||
private final long currentImageId; // Should be 0.
|
||||
private final ImageVersion currentImageVersion;
|
||||
|
||||
public NewImageInfoRequest(final byte[] bytes) {
|
||||
final ByteInputStream inputStream = new ByteInputStream(bytes);
|
||||
inputStream.nextBytes(1); // Skip command byte
|
||||
currentImageId = inputStream.nextLong(2);
|
||||
currentImageVersion = new ImageVersion(inputStream.nextBytes(8));
|
||||
}
|
||||
|
||||
public long getCurrentImageId() {
|
||||
return currentImageId;
|
||||
}
|
||||
|
||||
public ImageVersion getCurrentImageVersion() {
|
||||
return currentImageVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NewImageInfoRequest{" +
|
||||
"currentImageId=" + currentImageId +
|
||||
", currentImageVersion=" + currentImageVersion +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,555 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.service;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCallback;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattDescriptor;
|
||||
import android.bluetooth.BluetoothGattService;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
import com.wolkabout.hexiwear.activity.MainActivity_;
|
||||
import com.wolkabout.hexiwear.activity.ReadingsActivity_;
|
||||
import com.wolkabout.hexiwear.model.Characteristic;
|
||||
import com.wolkabout.hexiwear.model.HexiwearDevice;
|
||||
import com.wolkabout.hexiwear.model.ManufacturerInfo;
|
||||
import com.wolkabout.hexiwear.model.Mode;
|
||||
import com.wolkabout.hexiwear.util.ByteUtils;
|
||||
import com.wolkabout.hexiwear.util.DataConverter;
|
||||
import com.wolkabout.hexiwear.util.HexiwearDevices;
|
||||
import com.wolkabout.wolk.Logger;
|
||||
import com.wolkabout.wolk.ReadingType;
|
||||
import com.wolkabout.wolk.Wolk;
|
||||
import com.wolkabout.wolkrestandroid.Credentials_;
|
||||
|
||||
import org.androidannotations.annotations.Bean;
|
||||
import org.androidannotations.annotations.EService;
|
||||
import org.androidannotations.annotations.Receiver;
|
||||
import org.androidannotations.annotations.SystemService;
|
||||
import org.androidannotations.annotations.sharedpreferences.Pref;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
|
||||
@EService
|
||||
public class BluetoothService extends Service {
|
||||
|
||||
private static final String TAG = BluetoothService.class.getSimpleName();
|
||||
|
||||
public static final String SERVICES_AVAILABLE = "servicesAvailable";
|
||||
public static final String DATA_AVAILABLE = "dataAvailable";
|
||||
public static final String READING_TYPE = "readingType";
|
||||
public static final String CONNECTION_STATE_CHANGED = "ConnectionStateChange";
|
||||
public static final String CONNECTION_STATE = "connectionState";
|
||||
public static final String STRING_DATA = "stringData";
|
||||
public static final String STOP = "stop";
|
||||
public static final String ACTION_NEEDS_BOND = "noBond";
|
||||
public static final String PREFERENCE_CHANGED = "preferenceChanged";
|
||||
public static final String PREFERENCE_NAME = "preferenceName";
|
||||
public static final String PREFERENCE_ENABLED = "preferenceEnabled";
|
||||
public static final String PUBLISH_TIME_CHANGED = "publishTimeChanged";
|
||||
public static final String SHOULD_PUBLISH_CHANGED = "shouldPublishChanged";
|
||||
public static final String MODE_CHANGED = "modeChanged";
|
||||
public static final String MODE = "mode";
|
||||
|
||||
// Notification types
|
||||
private static final byte MISSED_CALLS = 2;
|
||||
private static final byte UNREAD_MESSAGES = 4;
|
||||
private static final byte UNREAD_EMAILS = 6;
|
||||
|
||||
// Alert In commands
|
||||
private static final byte WRITE_NOTIFICATION = 1;
|
||||
private static final byte WRITE_TIME = 3;
|
||||
|
||||
private static final Map<String, BluetoothGattCharacteristic> readableCharacteristics = new HashMap<>();
|
||||
private static final ManufacturerInfo manufacturerInfo = new ManufacturerInfo();
|
||||
private static final Queue<String> readingQueue = new ArrayBlockingQueue<>(12);
|
||||
private static final Queue<byte[]> notificationsQueue = new LinkedBlockingDeque<>();
|
||||
|
||||
private volatile boolean isConnected;
|
||||
private BluetoothDevice bluetoothDevice;
|
||||
private HexiwearDevice hexiwearDevice;
|
||||
private BluetoothGattCharacteristic alertIn;
|
||||
private BluetoothGatt bluetoothGatt;
|
||||
private Wolk wolk;
|
||||
private Mode mode;
|
||||
|
||||
@Bean
|
||||
HexiwearDevices hexiwearDevices;
|
||||
|
||||
@SystemService
|
||||
NotificationManager notificationManager;
|
||||
|
||||
@Pref
|
||||
Credentials_ credentials;
|
||||
|
||||
@Receiver(actions = BluetoothDevice.ACTION_BOND_STATE_CHANGED)
|
||||
void onBondStateChanged(Intent intent) {
|
||||
final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||
final int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
|
||||
final int previousBondState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, -1);
|
||||
|
||||
Log.d(TAG, "Bond state changed for: " + device.getAddress() + " new state: " + bondState + " previous: " + previousBondState);
|
||||
|
||||
if (bondState == BluetoothDevice.BOND_BONDED) {
|
||||
Log.i(TAG, "Bonded");
|
||||
createGATT(device);
|
||||
} else if (bondState == BluetoothDevice.BOND_NONE) {
|
||||
device.createBond();
|
||||
}
|
||||
}
|
||||
|
||||
@Receiver(actions = STOP)
|
||||
void onStopCommand() {
|
||||
Log.i(TAG, "Stop command received.");
|
||||
stopForeground(true);
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Log.i(TAG, "Stopping service...");
|
||||
if (bluetoothGatt != null) {
|
||||
bluetoothGatt.close();
|
||||
NotificationService_.intent(this).stop();
|
||||
}
|
||||
if (wolk != null && hexiwearDevices.shouldTransmit(hexiwearDevice)) {
|
||||
wolk.stopAutoPublishing();
|
||||
}
|
||||
}
|
||||
|
||||
public void startReading(BluetoothDevice device) {
|
||||
Log.i(TAG, "Starting to read data for device: " + device.getName());
|
||||
hexiwearDevice = hexiwearDevices.getDevice(device.getAddress());
|
||||
bluetoothDevice = device;
|
||||
createGATT(device);
|
||||
|
||||
if(credentials.username().get().equals("Demo")) {
|
||||
return;
|
||||
}
|
||||
|
||||
wolk = new Wolk(hexiwearDevice);
|
||||
wolk.setLogger(new Logger() {
|
||||
@Override
|
||||
public void info(final String message) {
|
||||
Log.i(TAG, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(final String message, final Throwable e) {
|
||||
Log.e(TAG, message, e);
|
||||
}
|
||||
});
|
||||
|
||||
if (hexiwearDevices.shouldTransmit(device)) {
|
||||
final int publishInterval = hexiwearDevices.getPublishInterval(hexiwearDevice);
|
||||
wolk.startAutoPublishing(publishInterval);
|
||||
}
|
||||
}
|
||||
|
||||
private void createGATT(final BluetoothDevice device) {
|
||||
bluetoothGatt = device.connectGatt(this, true, new BluetoothGattCallback() {
|
||||
@Override
|
||||
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
|
||||
isConnected = BluetoothProfile.STATE_CONNECTED == newState;
|
||||
if (isConnected) {
|
||||
Log.i(TAG, "GATT connected.");
|
||||
startForeground(442, getNotification(device));
|
||||
gatt.discoverServices();
|
||||
} else {
|
||||
Log.i(TAG, "GATT disconnected.");
|
||||
NotificationService_.intent(BluetoothService.this).stop();
|
||||
notificationManager.notify(442, getNotification(device));
|
||||
gatt.connect();
|
||||
}
|
||||
|
||||
final Intent connectionStateChanged = new Intent(CONNECTION_STATE_CHANGED);
|
||||
connectionStateChanged.putExtra(CONNECTION_STATE, isConnected);
|
||||
sendBroadcast(connectionStateChanged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
|
||||
Log.i(TAG, "Services discovered.");
|
||||
if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) {
|
||||
handleAuthenticationError(gatt);
|
||||
return;
|
||||
}
|
||||
|
||||
discoverCharacteristics(gatt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
||||
Log.i(TAG, "Characteristic written: " + status);
|
||||
|
||||
if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) {
|
||||
handleAuthenticationError(gatt);
|
||||
return;
|
||||
}
|
||||
|
||||
final byte command = characteristic.getValue()[0];
|
||||
switch (command) {
|
||||
case WRITE_TIME:
|
||||
Log.i(TAG, "Time written.");
|
||||
final BluetoothGattCharacteristic batteryCharacteristic = readableCharacteristics.get(Characteristic.BATTERY.getUuid());
|
||||
gatt.setCharacteristicNotification(batteryCharacteristic, true);
|
||||
for (BluetoothGattDescriptor descriptor : batteryCharacteristic.getDescriptors()) {
|
||||
if (descriptor.getUuid().toString().startsWith("00002904")) {
|
||||
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
|
||||
gatt.writeDescriptor(descriptor);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WRITE_NOTIFICATION:
|
||||
Log.i(TAG, "Notification sent.");
|
||||
if (notificationsQueue.isEmpty()) {
|
||||
Log.i(TAG, "Reading characteristics...");
|
||||
readNextCharacteristics(gatt);
|
||||
} else {
|
||||
Log.i(TAG, "writing next notification...");
|
||||
alertIn.setValue(notificationsQueue.poll());
|
||||
gatt.writeCharacteristic(alertIn);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Log.w(TAG, "No such ALERT IN command: " + command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
|
||||
readCharacteristic(gatt, Characteristic.MANUFACTURER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicRead(BluetoothGatt gatt, final BluetoothGattCharacteristic gattCharacteristic, int status) {
|
||||
if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) {
|
||||
handleAuthenticationError(gatt);
|
||||
return;
|
||||
}
|
||||
|
||||
final String characteristicUuid = gattCharacteristic.getUuid().toString();
|
||||
final Characteristic characteristic = Characteristic.byUuid(characteristicUuid);
|
||||
switch (characteristic) {
|
||||
case MANUFACTURER:
|
||||
manufacturerInfo.manufacturer = gattCharacteristic.getStringValue(0);
|
||||
readCharacteristic(gatt, Characteristic.FW_REVISION);
|
||||
break;
|
||||
case FW_REVISION:
|
||||
manufacturerInfo.firmwareRevision = gattCharacteristic.getStringValue(0);
|
||||
readCharacteristic(gatt, Characteristic.MODE);
|
||||
break;
|
||||
default:
|
||||
Log.v(TAG, "Characteristic read: " + characteristic.name());
|
||||
if (characteristic == Characteristic.MODE) {
|
||||
final Mode newMode = Mode.bySymbol(gattCharacteristic.getValue()[0]);
|
||||
if (mode != newMode) {
|
||||
onModeChanged(newMode);
|
||||
}
|
||||
} else {
|
||||
onBluetoothDataReceived(characteristic, gattCharacteristic.getValue());
|
||||
}
|
||||
|
||||
if (notificationsQueue.isEmpty()) {
|
||||
readNextCharacteristics(gatt);
|
||||
} else {
|
||||
alertIn.setValue(notificationsQueue.poll());
|
||||
gatt.writeCharacteristic(alertIn);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic gattCharacteristic) {
|
||||
final String characteristicUuid = gattCharacteristic.getUuid().toString();
|
||||
final Characteristic characteristic = Characteristic.byUuid(characteristicUuid);
|
||||
Log.d(TAG, "Characteristic changed: " + characteristic);
|
||||
|
||||
if (characteristic == Characteristic.BATTERY) {
|
||||
onBluetoothDataReceived(Characteristic.BATTERY, gattCharacteristic.getValue());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onModeChanged(final Mode newMode) {
|
||||
Log.i(TAG, "Mode changed. New mode is: " + mode);
|
||||
mode = newMode;
|
||||
|
||||
setReadingQueue();
|
||||
|
||||
final Intent modeChanged = new Intent(MODE_CHANGED);
|
||||
modeChanged.putExtra(MODE, newMode);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(modeChanged);
|
||||
}
|
||||
|
||||
private void setReadingQueue() {
|
||||
readingQueue.clear();
|
||||
readingQueue.add(Characteristic.MODE.name());
|
||||
final List<String> enabledPreferences = hexiwearDevices.getEnabledPreferences(bluetoothDevice.getAddress());
|
||||
for (String characteristic : enabledPreferences) {
|
||||
if (mode.hasCharacteristic(characteristic)) {
|
||||
readingQueue.add(characteristic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Receiver(actions = PREFERENCE_CHANGED, local = true)
|
||||
void preferenceChanged(@Receiver.Extra String preferenceName, @Receiver.Extra boolean preferenceEnabled) {
|
||||
if (!mode.hasCharacteristic(preferenceName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (preferenceEnabled) {
|
||||
readingQueue.add(preferenceName);
|
||||
} else {
|
||||
readingQueue.remove(preferenceName);
|
||||
}
|
||||
}
|
||||
|
||||
@Receiver(actions = PUBLISH_TIME_CHANGED, local = true)
|
||||
void onPublishTimeChanged() {
|
||||
if (hexiwearDevices.shouldTransmit(hexiwearDevice)) {
|
||||
setTracking(false);
|
||||
setTracking(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Receiver(actions = SHOULD_PUBLISH_CHANGED, local = true)
|
||||
void onShouldPublishChanged() {
|
||||
setTracking(hexiwearDevices.shouldTransmit(hexiwearDevice));
|
||||
}
|
||||
|
||||
@Receiver(actions = NotificationService.MISSED_CALLS_AMOUNT_CHANGED, local = true)
|
||||
void onMissedCallsAmountChanged(@Receiver.Extra final int value) {
|
||||
queueNotification(MISSED_CALLS, value);
|
||||
}
|
||||
|
||||
@Receiver(actions = NotificationService.UNREAD_MESSAGES_AMOUNT_CHANGED, local = true)
|
||||
void onUnreadMessagesAmountChanged(@Receiver.Extra final int value) {
|
||||
queueNotification(UNREAD_MESSAGES, value);
|
||||
}
|
||||
|
||||
@Receiver(actions = NotificationService.UNREAD_EMAILS_AMOUNT_CHANGED, local = true)
|
||||
void onUnreadEmailAmountChanged(@Receiver.Extra final int value) {
|
||||
queueNotification(UNREAD_EMAILS, value);
|
||||
}
|
||||
|
||||
private void queueNotification(final byte type, final int amount) {
|
||||
final byte[] notification = new byte[20];
|
||||
notification[0] = WRITE_NOTIFICATION;
|
||||
notification[1] = type;
|
||||
notification[2] = ByteUtils.intToByte(amount);
|
||||
notificationsQueue.add(notification);
|
||||
}
|
||||
|
||||
private void onBluetoothDataReceived(final Characteristic type, final byte[] data) {
|
||||
if (wolk != null && hexiwearDevices.shouldTransmit(hexiwearDevice) && type != Characteristic.BATTERY) {
|
||||
final ReadingType readingType = ReadingType.valueOf(type.name());
|
||||
wolk.addReading(readingType, DataConverter.formatForPublushing(type, data));
|
||||
}
|
||||
|
||||
final Intent dataRead = new Intent(DATA_AVAILABLE);
|
||||
dataRead.putExtra(READING_TYPE, type.getUuid());
|
||||
dataRead.putExtra(STRING_DATA, DataConverter.parseBluetoothData(type, data));
|
||||
sendBroadcast(dataRead);
|
||||
}
|
||||
|
||||
void readNextCharacteristics(final BluetoothGatt gatt) {
|
||||
final String characteristicUuid = readingQueue.poll();
|
||||
readingQueue.add(characteristicUuid);
|
||||
readCharacteristic(gatt, Characteristic.valueOf(characteristicUuid));
|
||||
}
|
||||
|
||||
private void readCharacteristic(final BluetoothGatt gatt, final Characteristic characteristic) {
|
||||
if (!isConnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
final BluetoothGattCharacteristic gattCharacteristic = readableCharacteristics.get(characteristic.getUuid());
|
||||
if (gattCharacteristic != null) {
|
||||
gatt.readCharacteristic(gattCharacteristic);
|
||||
}
|
||||
}
|
||||
|
||||
private void discoverCharacteristics(final BluetoothGatt gatt) {
|
||||
if (gatt.getServices().size() == 0) {
|
||||
Log.i(TAG, "No services found.");
|
||||
}
|
||||
|
||||
for (BluetoothGattService gattService : gatt.getServices()) {
|
||||
storeCharacteristicsFromService(gattService);
|
||||
}
|
||||
|
||||
sendBroadcast(new Intent(SERVICES_AVAILABLE));
|
||||
}
|
||||
|
||||
private void storeCharacteristicsFromService(BluetoothGattService gattService) {
|
||||
for (BluetoothGattCharacteristic gattCharacteristic : gattService.getCharacteristics()) {
|
||||
final String characteristicUuid = gattCharacteristic.getUuid().toString();
|
||||
final Characteristic characteristic = Characteristic.byUuid(characteristicUuid);
|
||||
|
||||
if (characteristic == Characteristic.ALERT_IN) {
|
||||
Log.d(TAG, "ALERT_IN DISCOVERED");
|
||||
alertIn = gattCharacteristic;
|
||||
setTime();
|
||||
NotificationService_.intent(BluetoothService.this).start();
|
||||
} else if (characteristic != null) {
|
||||
Log.v(TAG, characteristic.getType() + ": " + characteristic.name());
|
||||
readableCharacteristics.put(characteristicUuid, gattCharacteristic);
|
||||
} else {
|
||||
Log.v(TAG, "UNKNOWN: " + characteristicUuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setTime() {
|
||||
Log.d(TAG, "Setting time...");
|
||||
if (!isConnected || alertIn == null) {
|
||||
Log.w(TAG, "Time not set.");
|
||||
return;
|
||||
}
|
||||
|
||||
final byte[] time = new byte[20];
|
||||
final long currentTime = System.currentTimeMillis();
|
||||
final long currentTimeWithTimeZoneOffset = (currentTime + TimeZone.getDefault().getOffset(currentTime)) / 1000;
|
||||
|
||||
final ByteBuffer buffer = ByteBuffer.allocate(8);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN).asLongBuffer().put(currentTimeWithTimeZoneOffset);
|
||||
final byte[] utcBytes = buffer.array();
|
||||
|
||||
final byte length = 0x04;
|
||||
|
||||
time[0] = WRITE_TIME;
|
||||
time[1] = length;
|
||||
time[2] = utcBytes[0];
|
||||
time[3] = utcBytes[1];
|
||||
time[4] = utcBytes[2];
|
||||
time[5] = utcBytes[3];
|
||||
|
||||
alertIn.setValue(time);
|
||||
alertIn.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
|
||||
bluetoothGatt.writeCharacteristic(alertIn);
|
||||
}
|
||||
|
||||
public void setTracking(final boolean enabled) {
|
||||
if (enabled) {
|
||||
final int publishInterval = hexiwearDevices.getPublishInterval(hexiwearDevice);
|
||||
wolk.startAutoPublishing(publishInterval);
|
||||
} else {
|
||||
wolk.stopAutoPublishing();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return isConnected;
|
||||
}
|
||||
|
||||
public Mode getCurrentMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
public BluetoothDevice getCurrentDevice() {
|
||||
return bluetoothDevice;
|
||||
}
|
||||
|
||||
private void handleAuthenticationError(final BluetoothGatt gatt) {
|
||||
gatt.close();
|
||||
sendBroadcast(new Intent(BluetoothService.ACTION_NEEDS_BOND));
|
||||
gatt.getDevice().createBond();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return new ServiceBinder(this);
|
||||
}
|
||||
|
||||
public ManufacturerInfo getManufacturerInfo() {
|
||||
return manufacturerInfo;
|
||||
}
|
||||
|
||||
protected Notification getNotification(final BluetoothDevice device) {
|
||||
final boolean connectionEstablished = isConnected && bluetoothGatt != null && bluetoothGatt.getDevice() != null;
|
||||
final String text = connectionEstablished ? "Device is connected to " + hexiwearDevice.getWolkName() : "Device is not connected.";
|
||||
final int icon = connectionEstablished ? R.drawable.ic_bluetooth_connected_white_48dp : R.drawable.ic_bluetooth_searching_white_48dp;
|
||||
|
||||
final Intent readingsActivityIntent = ReadingsActivity_.intent(this).flags(Intent.FLAG_ACTIVITY_SINGLE_TOP).device(device).get();
|
||||
final Intent mainActivityIntent = MainActivity_.intent(this).flags(Intent.FLAG_ACTIVITY_SINGLE_TOP).get();
|
||||
final Intent launchActivity = connectionEstablished ? readingsActivityIntent : mainActivityIntent;
|
||||
final PendingIntent pendingIntentMain = PendingIntent.getActivity(this, 71, launchActivity, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
final Intent stopIntent = new Intent(STOP);
|
||||
final PendingIntent pendingStopIntent = PendingIntent.getBroadcast(this, 0, stopIntent, 0);
|
||||
|
||||
return new NotificationCompat.Builder(this)
|
||||
.setSmallIcon(icon)
|
||||
.setContentTitle(getResources().getString(R.string.app_name))
|
||||
.setContentIntent(pendingIntentMain)
|
||||
.setLights(Color.GREEN, 100, 5000)
|
||||
.addAction(R.drawable.ic_cloud_off_black_24dp, "Stop", pendingStopIntent)
|
||||
.setContentText(text).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendBroadcast(Intent intent) {
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
|
||||
}
|
||||
|
||||
public class ServiceBinder extends Binder {
|
||||
|
||||
private BluetoothService service;
|
||||
|
||||
public ServiceBinder(BluetoothService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
public BluetoothService getService() {
|
||||
return service;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,171 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.service;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.le.ScanCallback;
|
||||
import android.bluetooth.le.ScanResult;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.wolkabout.hexiwear.model.BluetoothDeviceWrapper;
|
||||
|
||||
import org.androidannotations.annotations.AfterInject;
|
||||
import org.androidannotations.annotations.Background;
|
||||
import org.androidannotations.annotations.EBean;
|
||||
import org.androidannotations.annotations.RootContext;
|
||||
import org.androidannotations.api.BackgroundExecutor;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
@EBean
|
||||
public class DeviceDiscoveryService {
|
||||
|
||||
private static final String TAG = DeviceDiscoveryService.class.getSimpleName();
|
||||
|
||||
private static final long SCAN_PERIOD = 5000;
|
||||
private static final String SCAN_TASK = "scan";
|
||||
private static final String HEXIWEAR_TAG = "hexiwear";
|
||||
private static final String HEXI_OTAP_TAG = "hexiotap";
|
||||
|
||||
public static final String SCAN_STARTED = "scanStarted";
|
||||
public static final String SCAN_STOPPED = "scanStopped";
|
||||
public static final String DEVICE_DISCOVERED = "deviceDiscovered";
|
||||
public static final String WRAPPER = "wrapper";
|
||||
|
||||
private static final BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
|
||||
@RootContext
|
||||
Context context;
|
||||
|
||||
private static ScanCallback lolipopScanCallback;
|
||||
private static BluetoothAdapter.LeScanCallback kitKatScanCallback;
|
||||
|
||||
@AfterInject
|
||||
void checkEnabled() {
|
||||
isEnabled();
|
||||
}
|
||||
|
||||
public void startScan() {
|
||||
if (!isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
startKitKatScan();
|
||||
} else {
|
||||
startLolipopScan();
|
||||
}
|
||||
|
||||
setScanTimeLimit();
|
||||
sendBroadcast(new Intent(SCAN_STARTED));
|
||||
Log.i(TAG, "Bluetooth device discovery started.");
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
public void startKitKatScan() {
|
||||
kitKatScanCallback = new BluetoothAdapter.LeScanCallback() {
|
||||
@Override
|
||||
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
|
||||
final BluetoothDeviceWrapper wrapper = new BluetoothDeviceWrapper();
|
||||
wrapper.setDevice(device);
|
||||
wrapper.setSignalStrength(rssi);
|
||||
onDeviceDiscovered(wrapper);
|
||||
}
|
||||
};
|
||||
bluetoothAdapter.startLeScan(kitKatScanCallback);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private void startLolipopScan() {
|
||||
lolipopScanCallback = new ScanCallback() {
|
||||
@Override
|
||||
public void onScanResult(int callbackType, ScanResult result) {
|
||||
super.onScanResult(callbackType, result);
|
||||
final BluetoothDeviceWrapper wrapper = new BluetoothDeviceWrapper();
|
||||
wrapper.setDevice(result.getDevice());
|
||||
wrapper.setSignalStrength(result.getRssi());
|
||||
onDeviceDiscovered(wrapper);
|
||||
}
|
||||
};
|
||||
bluetoothAdapter.getBluetoothLeScanner().startScan(lolipopScanCallback);
|
||||
}
|
||||
|
||||
private void onDeviceDiscovered(final BluetoothDeviceWrapper wrapper) {
|
||||
final BluetoothDevice device = wrapper.getDevice();
|
||||
Log.i(TAG, "Discovered device: " + device.getName() + "(" + device.getAddress() + ")");
|
||||
final String name = wrapper.getDevice().getName();
|
||||
if (HEXI_OTAP_TAG.equalsIgnoreCase(name)) {
|
||||
wrapper.setInOtapMode(true);
|
||||
} else if (!HEXIWEAR_TAG.equalsIgnoreCase(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Intent deviceDiscovered = new Intent(DEVICE_DISCOVERED);
|
||||
deviceDiscovered.putExtra(WRAPPER, Parcels.wrap(wrapper));
|
||||
sendBroadcast(deviceDiscovered);
|
||||
}
|
||||
|
||||
@Background(id = SCAN_TASK, delay = SCAN_PERIOD)
|
||||
void setScanTimeLimit() {
|
||||
cancelScan();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public void cancelScan() {
|
||||
if (!isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
bluetoothAdapter.stopLeScan(kitKatScanCallback);
|
||||
} else {
|
||||
bluetoothAdapter.getBluetoothLeScanner().stopScan(lolipopScanCallback);
|
||||
}
|
||||
|
||||
sendBroadcast(new Intent(SCAN_STOPPED));
|
||||
Log.i(TAG, "Bluetooth device discovery canceled");
|
||||
BackgroundExecutor.cancelAll(SCAN_TASK, true);
|
||||
}
|
||||
|
||||
boolean isEnabled() {
|
||||
if (bluetoothAdapter == null) {
|
||||
Log.e(TAG, "Bluetooth not supported");
|
||||
return false;
|
||||
} else if (!bluetoothAdapter.isEnabled()) {
|
||||
context.startActivity(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE));
|
||||
return false;
|
||||
} else {
|
||||
Log.v(TAG, "Bluetooth is enabled and functioning properly.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void sendBroadcast(Intent intent) {
|
||||
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
|
||||
}
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.service;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
import com.wolkabout.hexiwear.activity.ReadingsActivity_;
|
||||
import com.wolkabout.hexiwear.model.HexiwearDevice;
|
||||
import com.wolkabout.hexiwear.util.HexiwearDevices;
|
||||
import com.wolkabout.hexiwear.view.DeviceActivation;
|
||||
import com.wolkabout.hexiwear.view.DeviceActivation_;
|
||||
import com.wolkabout.wolkrestandroid.Credentials_;
|
||||
import com.wolkabout.wolkrestandroid.dto.CreatePointBodyDTO;
|
||||
import com.wolkabout.wolkrestandroid.dto.CreatedPointDto;
|
||||
import com.wolkabout.wolkrestandroid.dto.PointWithFeedsResponse;
|
||||
import com.wolkabout.wolkrestandroid.dto.SerialDto;
|
||||
import com.wolkabout.wolkrestandroid.enumeration.SensorType;
|
||||
import com.wolkabout.wolkrestandroid.service.DeviceService;
|
||||
import com.wolkabout.wolkrestandroid.service.PointService;
|
||||
|
||||
import org.androidannotations.annotations.Background;
|
||||
import org.androidannotations.annotations.Bean;
|
||||
import org.androidannotations.annotations.EBean;
|
||||
import org.androidannotations.annotations.RootContext;
|
||||
import org.androidannotations.annotations.UiThread;
|
||||
import org.androidannotations.annotations.sharedpreferences.Pref;
|
||||
import org.androidannotations.rest.spring.annotations.RestService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@EBean
|
||||
public class DeviceRegistrationService {
|
||||
|
||||
private static final String TAG = DeviceRegistrationService.class.getSimpleName();
|
||||
|
||||
@RootContext
|
||||
Context context;
|
||||
|
||||
@Pref
|
||||
Credentials_ credentials;
|
||||
|
||||
@Bean
|
||||
HexiwearDevices devicesStore;
|
||||
|
||||
@RestService
|
||||
DeviceService deviceService;
|
||||
|
||||
@RestService
|
||||
PointService pointService;
|
||||
|
||||
@Background
|
||||
public void registerHexiwearDevice(final BluetoothDevice device) {
|
||||
if (credentials.username().get().equals("Demo")) {
|
||||
final int demoNumber = devicesStore.getDevices().size() + 1;
|
||||
final HexiwearDevice hexiwearDevice = new HexiwearDevice(device.getName(), "", device.getAddress(), "", "Demo device " + demoNumber);
|
||||
devicesStore.storeDevice(hexiwearDevice);
|
||||
ReadingsActivity_.intent(context).device(device).start();
|
||||
}
|
||||
|
||||
final SerialDto serialDto = deviceService.getRandomSerial(SensorType.HEXIWEAR);
|
||||
final String serial = serialDto.getSerial();
|
||||
getDevices(device, serial);
|
||||
}
|
||||
|
||||
@Background
|
||||
void getDevices(final BluetoothDevice device, final String serial) {
|
||||
final List<PointWithFeedsResponse> points = pointService.getPoints();
|
||||
final List<HexiwearDevice> hexiwearDevices = HexiwearDevices.getDevices(points);
|
||||
displayActivationDialog(device, serial, hexiwearDevices);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void displayActivationDialog(final BluetoothDevice device, final String serial, final List<HexiwearDevice> hexiwearDevices) {
|
||||
final DeviceActivation view = DeviceActivation_.build(context);
|
||||
view.setDevices(hexiwearDevices);
|
||||
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||
builder.setView(view);
|
||||
builder.setMessage(R.string.activation_dialog_title);
|
||||
builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
//dialog.cancel();
|
||||
}
|
||||
});
|
||||
builder.setPositiveButton(R.string.activation_dialog_confirm, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
final String wolkName = view.getWolkName();
|
||||
if (view.isNewDeviceSelected()) {
|
||||
Log.d(TAG, "New device selected. Activating...");
|
||||
registerHexiwearDevice(device, serial, wolkName);
|
||||
} else {
|
||||
deactivateDeviceAndRegister(device, serial, wolkName, view.getDeviceSerial());
|
||||
}
|
||||
}
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
|
||||
@Background
|
||||
void deactivateDeviceAndRegister(final BluetoothDevice device, final String serial, final String wolkName, String existingDeviceSerial) {
|
||||
try {
|
||||
Log.d(TAG, "Deactivating existing device...");
|
||||
deviceService.deactivateDevice(existingDeviceSerial);
|
||||
Log.d(TAG, "Deactivated.");
|
||||
registerHexiwearDevice(device, serial, wolkName);
|
||||
} catch (Exception e) {
|
||||
onDeactivateError();
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void onDeactivateError() {
|
||||
Toast.makeText(context, "Failed to activate as exiting device", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
@Background
|
||||
void registerHexiwearDevice(final BluetoothDevice device, final String serial, final String wolkName) {
|
||||
Log.d(TAG, "Registering hexiwear device.");
|
||||
final CreatePointBodyDTO bodyDto = new CreatePointBodyDTO(wolkName, "", "T:ON,P:ON,H:ON");
|
||||
final ArrayList<CreatePointBodyDTO> bodyDtos = new ArrayList<>();
|
||||
bodyDtos.add(bodyDto);
|
||||
final List<CreatedPointDto> response = deviceService.createPointWithThings(serial, bodyDtos);
|
||||
if (!response.isEmpty()) {
|
||||
final String name = device.getName();
|
||||
final String address = device.getAddress();
|
||||
final String password = response.get(0).getPassword();
|
||||
final HexiwearDevice hexiwearDevice = new HexiwearDevice(name, serial, address, password, wolkName);
|
||||
devicesStore.storeDevice(hexiwearDevice);
|
||||
Log.d(TAG, "Hexiwear registered." + hexiwearDevice);
|
||||
|
||||
ReadingsActivity_.intent(context).device(device).start();
|
||||
} else {
|
||||
onRegistrationError();
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void onRegistrationError() {
|
||||
Toast.makeText(context, "Failed to activate device.", Toast.LENGTH_LONG).show();
|
||||
Log.e(TAG, "Registration failed");
|
||||
}
|
||||
}
|
@ -0,0 +1,298 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.service;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCallback;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattDescriptor;
|
||||
import android.bluetooth.BluetoothGattService;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
import com.wolkabout.hexiwear.model.Characteristic;
|
||||
import com.wolkabout.hexiwear.model.otap.Command;
|
||||
import com.wolkabout.hexiwear.model.otap.Image;
|
||||
import com.wolkabout.hexiwear.model.otap.ImageBlock;
|
||||
import com.wolkabout.hexiwear.model.otap.response.ErrorNotification;
|
||||
import com.wolkabout.hexiwear.model.otap.response.ImageBlockRequest;
|
||||
import com.wolkabout.hexiwear.model.otap.response.ImageTransferComplete;
|
||||
import com.wolkabout.hexiwear.util.ByteUtils;
|
||||
|
||||
import org.androidannotations.annotations.EService;
|
||||
import org.androidannotations.annotations.Receiver;
|
||||
import org.androidannotations.annotations.SystemService;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@EService
|
||||
public class FirmwareUpdateService extends Service {
|
||||
|
||||
public static final String UPDATE_INITIATED = "updateStarted";
|
||||
public static final String UPDATE_CANCELED = "updateCanceled";
|
||||
public static final String UPDATE_FINISHED = "updateFinished";
|
||||
public static final String UPDATE_ERROR = "updateError";
|
||||
public static final String UPDATE_PROGRESS = "updateProgress";
|
||||
public static final String UPDATE_PROGRESS_VALUE = "progress";
|
||||
public static final String CANCEL_UPDATE = "cancelUpdate";
|
||||
|
||||
private static final String TAG = FirmwareUpdateService.class.getSimpleName();
|
||||
private static final int NOTIFICATION_ID = 1;
|
||||
|
||||
@SystemService
|
||||
static NotificationManager notificationManager;
|
||||
|
||||
private static NotificationCompat.Builder notificationBuilder;
|
||||
private static BluetoothGatt bluetoothGatt;
|
||||
|
||||
@Override
|
||||
public int onStartCommand(final Intent intent, final int flags, final int startId) {
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(final Intent intent) {
|
||||
return new ServiceBinder(this);
|
||||
}
|
||||
|
||||
public void updateFirmware(final BluetoothDevice device, final Image image) {
|
||||
notificationBuilder = new NotificationCompat.Builder(this)
|
||||
.setSmallIcon(R.drawable.ic_bluetooth_connected_white_48dp)
|
||||
.setContentTitle(getResources().getString(R.string.app_name))
|
||||
.setLights(Color.GREEN, 100, 5000);
|
||||
startForeground(NOTIFICATION_ID, notificationBuilder.build());
|
||||
|
||||
bluetoothGatt = device.connectGatt(this, true, new FirmwareUpdater(image));
|
||||
}
|
||||
|
||||
@Receiver(actions = CANCEL_UPDATE)
|
||||
public void cancelUpdate() {
|
||||
sendBroadcast(UPDATE_CANCELED);
|
||||
stopService();
|
||||
}
|
||||
|
||||
private void stopService() {
|
||||
if (bluetoothGatt != null) {
|
||||
bluetoothGatt.close();
|
||||
}
|
||||
stopForeground(true);
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
private void sendBroadcast(final String event) {
|
||||
sendBroadcast(new Intent(event));
|
||||
}
|
||||
|
||||
public void sendBroadcast(final Intent intent) {
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
|
||||
}
|
||||
|
||||
private class FirmwareUpdater extends BluetoothGattCallback {
|
||||
|
||||
private static final String OTAP_SERVICE_UUID = "01ff5550-ba5e-f4ee-5ca1-eb1e5e4b1ce0";
|
||||
private static final String CONTROL_POINT_DESCRIPTOR_UUID = "00002902-0000-1000-8000-00805f9b34fb";
|
||||
|
||||
private final Image image;
|
||||
|
||||
private BluetoothGattCharacteristic controlPoint;
|
||||
private BluetoothGattCharacteristic data;
|
||||
private BluetoothGattCharacteristic state;
|
||||
|
||||
private ImageBlock currentBlock;
|
||||
|
||||
public FirmwareUpdater(final Image image) {
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) {
|
||||
if (BluetoothProfile.STATE_CONNECTED == newState) {
|
||||
Log.i(TAG, "GATT connected.");
|
||||
gatt.discoverServices();
|
||||
} else {
|
||||
Log.i(TAG, "GATT disconnected.");
|
||||
gatt.connect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
|
||||
Log.i(TAG, "Services discovered.");
|
||||
if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) {
|
||||
gatt.disconnect();
|
||||
gatt.getDevice().createBond();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get GATT characteristics from the device.
|
||||
final BluetoothGattService otapService = gatt.getService(UUID.fromString(OTAP_SERVICE_UUID));
|
||||
controlPoint = otapService.getCharacteristic(UUID.fromString(Characteristic.CONTROL_POINT.getUuid()));
|
||||
data = otapService.getCharacteristic(UUID.fromString(Characteristic.DATA.getUuid()));
|
||||
state = otapService.getCharacteristic(UUID.fromString(Characteristic.STATE.getUuid()));
|
||||
|
||||
gatt.readCharacteristic(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicRead(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int status) {
|
||||
if (characteristic != state) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int value = ByteUtils.parseInt(characteristic.getValue());
|
||||
if (value == 0) {
|
||||
Log.e(TAG, "OTAP mode is not enabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean wrongModeForMK64 = value == 1 && image.getType() == Image.Type.MK64;
|
||||
final boolean wrongModeForKW40 = value == 2 && image.getType() == Image.Type.KW40;
|
||||
if (wrongModeForMK64 || wrongModeForKW40) {
|
||||
Log.e(TAG, "Wrong OTAP mode for the selected image.");
|
||||
sendBroadcast(UPDATE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.i(TAG, "The device is in the correct OTAP mode. Starting communication.");
|
||||
|
||||
gatt.setCharacteristicNotification(controlPoint, true);
|
||||
|
||||
final BluetoothGattDescriptor descriptor = controlPoint.getDescriptor(UUID.fromString(CONTROL_POINT_DESCRIPTOR_UUID));
|
||||
gatt.readDescriptor(descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDescriptorRead(final BluetoothGatt gatt, final BluetoothGattDescriptor descriptor, final int status) {
|
||||
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
|
||||
gatt.writeDescriptor(descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicChanged(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
|
||||
final byte[] value = characteristic.getValue();
|
||||
final Command command = Command.byCommandByte(value[0]);
|
||||
Log.i(TAG, "Received command: " + command);
|
||||
switch (command) {
|
||||
case NEW_IMAGE_INFO_REQUEST:
|
||||
controlPoint.setValue(image.getNewImageInfoResponse());
|
||||
gatt.writeCharacteristic(controlPoint);
|
||||
Log.i(TAG, "Writing command: NEW_IMAGE_INFO_RESPONSE");
|
||||
final PendingIntent pendingIntent = PendingIntent.getBroadcast(FirmwareUpdateService.this, 0, new Intent(CANCEL_UPDATE), 0);
|
||||
notificationBuilder.addAction(R.drawable.ic_clear_white_24dp, getString(R.string.firmware_update_cancel), pendingIntent);
|
||||
setNotificationText(R.string.firmware_update_start);
|
||||
sendBroadcast(UPDATE_INITIATED);
|
||||
break;
|
||||
case IMAGE_BLOCK_REQUEST:
|
||||
final ImageBlockRequest imageBlockRequest = new ImageBlockRequest(value);
|
||||
Log.i(TAG, "Block request is: " + imageBlockRequest);
|
||||
currentBlock = image.getBlock(imageBlockRequest);
|
||||
setProgress(imageBlockRequest.getStartPosition());
|
||||
writeChunk(gatt);
|
||||
break;
|
||||
case IMAGE_TRANSFER_COMPLETE:
|
||||
final ImageTransferComplete imageTransferComplete = new ImageTransferComplete(value);
|
||||
Log.i(TAG, "Image transfer completed: " + imageTransferComplete);
|
||||
setNotificationText(R.string.firmware_update_complete);
|
||||
sendBroadcast(UPDATE_FINISHED);
|
||||
stopService();
|
||||
break;
|
||||
case ERROR_NOTIFICATION:
|
||||
final ErrorNotification errorNotification = new ErrorNotification(value);
|
||||
Log.e(TAG, "Error during firmware update: " + errorNotification);
|
||||
setNotificationText(R.string.firmware_update_error);
|
||||
sendBroadcast(UPDATE_ERROR);
|
||||
stopService();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicWrite(final BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int status) {
|
||||
if (characteristic == controlPoint) {
|
||||
Log.i(TAG, "Successfully written to control point.");
|
||||
} else if (characteristic == data && !currentBlock.isCompleted()) {
|
||||
writeChunk(gatt);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void writeChunk(BluetoothGatt gatt) {
|
||||
// The first block will block and stop OTAP if the chunks are sent too fast.
|
||||
try {
|
||||
wait(50);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Log.v(TAG, "Writing chunk " + currentBlock.getPosition() + " / " + currentBlock.getNumberOfChunks());
|
||||
data.setValue(currentBlock.getNextChunk());
|
||||
gatt.writeCharacteristic(data);
|
||||
}
|
||||
|
||||
private void setProgress(final int value) {
|
||||
notificationBuilder.setContentText(getString(R.string.firmware_update_notification_text));
|
||||
final int progress = (int)(((double)value / image.getSize()) * 100);
|
||||
notificationBuilder.setProgress(100, progress, false);
|
||||
notificationBuilder.setOngoing(true);
|
||||
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
|
||||
|
||||
final Intent progressUpdated = new Intent(UPDATE_PROGRESS);
|
||||
progressUpdated.putExtra(UPDATE_PROGRESS_VALUE, progress);
|
||||
sendBroadcast(progressUpdated);
|
||||
}
|
||||
|
||||
private void setNotificationText(final int stringResource) {
|
||||
notificationBuilder.setContentText(FirmwareUpdateService.this.getString(stringResource));
|
||||
notificationBuilder.setProgress(0, 0, false);
|
||||
notificationBuilder.setOngoing(false);
|
||||
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
public class ServiceBinder extends Binder {
|
||||
|
||||
private FirmwareUpdateService service;
|
||||
|
||||
public ServiceBinder(final FirmwareUpdateService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
public FirmwareUpdateService getService() {
|
||||
return service;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,184 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.service;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.ContentObserver;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.provider.CallLog;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.util.Log;
|
||||
|
||||
import org.androidannotations.annotations.AfterInject;
|
||||
import org.androidannotations.annotations.Background;
|
||||
import org.androidannotations.annotations.EService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@EService
|
||||
public class NotificationService extends Service {
|
||||
|
||||
public static final String MISSED_CALLS_AMOUNT_CHANGED = "missedCallsAmountChanged";
|
||||
public static final String UNREAD_MESSAGES_AMOUNT_CHANGED = "unreadMessagesAmountChanged";
|
||||
public static final String UNREAD_EMAILS_AMOUNT_CHANGED = "unreadEmailsAmountChanged";
|
||||
public static final String VALUE = "value";
|
||||
|
||||
private static final String TAG = NotificationService.class.getSimpleName();
|
||||
private static final int UNREAD_EMAILS_COLUMN_NUMBER = 5;
|
||||
|
||||
private final List<ContentObserver> contentObservers = new ArrayList<>();
|
||||
private final Map<String, Integer> unreadEmailCounts = new HashMap<>();
|
||||
private int numberOfMissedCalls = -1;
|
||||
private int numberOfUnreadMessages = -1;
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Log.i(TAG, "Shutting down. Unregistering all observers...");
|
||||
for (ContentObserver contentObserver : contentObservers) {
|
||||
getContentResolver().unregisterContentObserver(contentObserver);
|
||||
}
|
||||
contentObservers.clear();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@AfterInject
|
||||
void setMissedCallObserver() {
|
||||
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkMissedCallsCount();
|
||||
final ContentObserver contentObserver = new ContentObserver(new Handler()) {
|
||||
public void onChange(boolean selfChange) {
|
||||
checkMissedCallsCount();
|
||||
}
|
||||
};
|
||||
Log.i(TAG, "Observing missed calls.");
|
||||
contentObservers.add(contentObserver);
|
||||
getContentResolver().registerContentObserver(CallLog.Calls.CONTENT_URI, true, contentObserver);
|
||||
}
|
||||
|
||||
@Background
|
||||
void checkMissedCallsCount() {
|
||||
final Cursor cursor = getContentResolver().query(CallLog.Calls.CONTENT_URI, null, CallLog.Calls.TYPE + " = ? AND " + CallLog.Calls.NEW + " = ?", new String[]{Integer.toString(CallLog.Calls.MISSED_TYPE), "1"}, CallLog.Calls.DATE + " DESC ");
|
||||
if (cursor != null) {
|
||||
final int count = cursor.getCount();
|
||||
if (numberOfMissedCalls != count) {
|
||||
numberOfMissedCalls = count;
|
||||
Log.d(TAG, "Missed calls: " + count);
|
||||
notifyValueChanged(MISSED_CALLS_AMOUNT_CHANGED, count);
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
@AfterInject
|
||||
void setUnreadMessageObserver() {
|
||||
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkUnreadMessageCount();
|
||||
final ContentObserver contentObserver = new ContentObserver(new Handler()) {
|
||||
public void onChange(boolean selfChange) {
|
||||
checkUnreadMessageCount();
|
||||
}
|
||||
};
|
||||
Log.i(TAG, "Observing unread messages.");
|
||||
contentObservers.add(contentObserver);
|
||||
getContentResolver().registerContentObserver(Uri.parse("content://sms/"), true, contentObserver);
|
||||
}
|
||||
|
||||
@Background
|
||||
void checkUnreadMessageCount() {
|
||||
final Cursor cursor = getContentResolver().query(Uri.parse("content://sms/"), null, "read = 0", null, null);
|
||||
if (cursor != null) {
|
||||
final int count = cursor.getCount();
|
||||
if (numberOfUnreadMessages != count) {
|
||||
numberOfUnreadMessages = count;
|
||||
Log.d(TAG, "Unread messages: " + count);
|
||||
notifyValueChanged(UNREAD_MESSAGES_AMOUNT_CHANGED, count);
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
@AfterInject
|
||||
void setUnreadEmailsObserver() {
|
||||
final Account[] accounts = AccountManager.get(this).getAccounts();
|
||||
for (final Account account : accounts) {
|
||||
unreadEmailCounts.put(account.name, -1);
|
||||
checkUnreadEmailCount(account);
|
||||
final ContentObserver contentObserver = new ContentObserver(new Handler()) {
|
||||
public void onChange(boolean selfChange) {
|
||||
checkUnreadEmailCount(account);
|
||||
}
|
||||
};
|
||||
Log.i(TAG, "Observing unread emails for " + account.name);
|
||||
contentObservers.add(contentObserver);
|
||||
getContentResolver().registerContentObserver(getEmailQuery(account), true, contentObserver);
|
||||
}
|
||||
}
|
||||
|
||||
@Background
|
||||
void checkUnreadEmailCount(Account account) {
|
||||
final Cursor cursor = getContentResolver().query(getEmailQuery(account), null, null, null, null);
|
||||
if (cursor != null) {
|
||||
if (cursor.moveToFirst()) {
|
||||
final int count = cursor.getInt(UNREAD_EMAILS_COLUMN_NUMBER);
|
||||
if (unreadEmailCounts.get(account.name) != count) {
|
||||
unreadEmailCounts.put(account.name, cursor.getInt(5));
|
||||
Log.d(TAG, "Unread emails: " + count);
|
||||
notifyValueChanged(UNREAD_EMAILS_AMOUNT_CHANGED, count);
|
||||
}
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
private Uri getEmailQuery(final Account account) {
|
||||
return Uri.parse("content://com.google.android.gm/" + account.name + "/labels");
|
||||
}
|
||||
|
||||
private void notifyValueChanged(final String type, final int newValue) {
|
||||
final Intent valueChanged = new Intent(type);
|
||||
valueChanged.putExtra(VALUE, newValue);
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(valueChanged);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.util;
|
||||
|
||||
|
||||
public class ByteInputStream {
|
||||
|
||||
private static final String NUMBER_ARRAY_DELIMITER = ".";
|
||||
|
||||
private final byte[] bytes;
|
||||
|
||||
private int position = 0;
|
||||
|
||||
public ByteInputStream(final byte[] bytes) {
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
public ByteInputStream(final byte[] bytes, final int offset) {
|
||||
this.bytes = bytes;
|
||||
position = offset;
|
||||
}
|
||||
|
||||
public long nextLong(final int length) {
|
||||
final byte[] bytes = nextBytes(length);
|
||||
return ByteUtils.parseLong(bytes);
|
||||
}
|
||||
|
||||
public int nextInt(final int length) {
|
||||
if (length > 4) {
|
||||
throw new IllegalArgumentException("Integer value can be changed during conversion.");
|
||||
}
|
||||
|
||||
final byte[] bytes = nextBytes(length);
|
||||
return (int) ByteUtils.parseLong(bytes);
|
||||
}
|
||||
|
||||
public String nextString(final int length) {
|
||||
final byte[] bytes = nextBytes(length);
|
||||
return ByteUtils.parseString(bytes);
|
||||
}
|
||||
|
||||
public String nextNumberArray(final int arrayLength, final int numberLength) {
|
||||
final StringBuilder numberArray = new StringBuilder();
|
||||
for (int i = 0; i < arrayLength; i++) {
|
||||
numberArray.append(nextInt(numberLength));
|
||||
if (i != arrayLength - 1) {
|
||||
numberArray.append(NUMBER_ARRAY_DELIMITER);
|
||||
}
|
||||
}
|
||||
return numberArray.toString();
|
||||
}
|
||||
|
||||
public byte[] nextBytes(final int length) {
|
||||
final byte[] subArray = new byte[length];
|
||||
System.arraycopy(bytes, position, subArray, 0, length);
|
||||
position += length;
|
||||
return subArray;
|
||||
}
|
||||
|
||||
public byte[] remainingBytes() {
|
||||
final byte[] remainingBytes = new byte[bytes.length - position];
|
||||
System.arraycopy(bytes, position, remainingBytes, 0, remainingBytes.length);
|
||||
return remainingBytes;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.util;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class ByteUtils {
|
||||
|
||||
private static final String TAG = ByteUtils.class.getSimpleName();
|
||||
private static final String CHARSET_NAME = "ISO646-US"; // ASCII
|
||||
|
||||
private ByteUtils() {
|
||||
// Not meant to be instantiated.
|
||||
}
|
||||
|
||||
public static int parseInt(final byte[] bytes) {
|
||||
if (bytes.length > 4) {
|
||||
throw new IllegalArgumentException("The integer value might change during conversion.");
|
||||
}
|
||||
|
||||
return (int) parseLong(bytes);
|
||||
}
|
||||
|
||||
public static long parseLong(final byte[] bytes) {
|
||||
long value = 0;
|
||||
for (int i = bytes.length - 1; i >= 0; i--) {
|
||||
final byte aByte = bytes[i];
|
||||
value = (value << 8) + (aByte & 0xff);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static String parseString(final byte[] bytes) {
|
||||
try {
|
||||
return new String(bytes, CHARSET_NAME);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
Log.e(TAG, "Unsupported charset " + CHARSET_NAME, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] readBytes(final File file) {
|
||||
final byte[] data = new byte[(int) file.length()];
|
||||
try {
|
||||
final FileInputStream fileInputStream = new FileInputStream(file);
|
||||
fileInputStream.read(data);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error reading file: " + file.getName(), e);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public static byte[] readBytes(final InputStream inputStream) {
|
||||
final ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
|
||||
|
||||
final byte[] buffer = new byte[1024];
|
||||
|
||||
try {
|
||||
int length;
|
||||
while ((length = inputStream.read(buffer)) != -1) {
|
||||
byteBuffer.write(buffer, 0, length);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Couldn't convert input stream to bytes.", e);
|
||||
}
|
||||
|
||||
return byteBuffer.toByteArray();
|
||||
}
|
||||
|
||||
public static byte intToByte(final int value){
|
||||
return ByteBuffer.allocate(4).putInt(value).array()[3];
|
||||
}
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.util;
|
||||
|
||||
import com.wolkabout.hexiwear.model.Characteristic;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class DataConverter {
|
||||
|
||||
private static final String INTEGER = "%d";
|
||||
private static final String FLOAT = "%.0f";
|
||||
private static final String TRIPLE_VALUE = "%+.0f%+.0f%+.0f";
|
||||
|
||||
private DataConverter() {
|
||||
// Not meant to be instantiated.
|
||||
}
|
||||
|
||||
public static String parseBluetoothData(final Characteristic characteristic, final byte[] data) {
|
||||
if (data == null || data.length == 0) return "";
|
||||
|
||||
float floatVal;
|
||||
float xfloatVal;
|
||||
float yfloatVal;
|
||||
float zfloatVal;
|
||||
|
||||
final String unit = characteristic.getUnit();
|
||||
|
||||
switch (characteristic) {
|
||||
case HEARTRATE:
|
||||
case BATTERY:
|
||||
case LIGHT:
|
||||
case CALORIES:
|
||||
case STEPS:
|
||||
floatVal = (data[0] & 0xff);
|
||||
return String.format("%.0f %s", floatVal, unit);
|
||||
case TEMPERATURE:
|
||||
case HUMIDITY:
|
||||
case PRESSURE:
|
||||
final int intVal = (data[1] << 8) & 0xff00 | (data[0] & 0xff);
|
||||
floatVal = (float) intVal / 100;
|
||||
return String.format("%.2f %s", floatVal, unit);
|
||||
case ACCELERATION:
|
||||
case MAGNET:
|
||||
final int xintVal = ((int) data[1] << 8) | (data[0] & 0xff);
|
||||
xfloatVal = (float) xintVal / 100;
|
||||
|
||||
final int yintVal = ((int) data[3] << 8) | (data[2] & 0xff);
|
||||
yfloatVal = (float) yintVal / 100;
|
||||
|
||||
final int zintVal = ((int) data[5] << 8) | (data[4] & 0xff);
|
||||
zfloatVal = (float) zintVal / 100;
|
||||
|
||||
return String.format("%.2f %s;%.2f %s;%.2f %s", xfloatVal, unit, yfloatVal, unit, zfloatVal, unit);
|
||||
case GYRO:
|
||||
final int gyroXintVal = ((int) data[1] << 8) | (data[0] & 0xff);
|
||||
xfloatVal = (float) gyroXintVal;
|
||||
|
||||
final int gyroYintVal = ((int) data[3] << 8) | (data[2] & 0xff);
|
||||
yfloatVal = (float) gyroYintVal;
|
||||
|
||||
final int gyroZintVal = ((int) data[5] << 8) | (data[4] & 0xff);
|
||||
zfloatVal = (float) gyroZintVal;
|
||||
|
||||
return String.format("%.2f %s;%.2f %s;%.2f %s", xfloatVal, unit, yfloatVal, unit, zfloatVal, unit);
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
public static String formatForPublushing(final Characteristic characteristic, final byte[] data) {
|
||||
if (data == null || data.length == 0) return "";
|
||||
|
||||
switch (characteristic) {
|
||||
case HEARTRATE:
|
||||
case LIGHT:
|
||||
case BATTERY:
|
||||
case CALORIES:
|
||||
final int heartRate = (data[0] & 0xff);
|
||||
return format(INTEGER, heartRate);
|
||||
case STEPS:
|
||||
case TEMPERATURE:
|
||||
case HUMIDITY:
|
||||
final int intVal = (data[1] << 8) & 0xff00 | (data[0] & 0xff);
|
||||
final float floatVal = (float) intVal / 10;
|
||||
return format(FLOAT, floatVal);
|
||||
case PRESSURE:
|
||||
final int pressureVal = (data[1] << 8) & 0xff00 | (data[0] & 0xff);
|
||||
return format(INTEGER, pressureVal);
|
||||
case ACCELERATION:
|
||||
case MAGNET:
|
||||
final int xintVal = ((int) data[1] << 8) | (data[0] & 0xff);
|
||||
final float xfloatVal = (float) xintVal / 10;
|
||||
|
||||
final int yintVal = ((int) data[3] << 8) | (data[2] & 0xff);
|
||||
final float yfloatVal = (float) yintVal / 10;
|
||||
|
||||
final int zintVal = ((int) data[5] << 8) | (data[4] & 0xff);
|
||||
final float zfloatVal = (float) zintVal / 10;
|
||||
return format(TRIPLE_VALUE, xfloatVal, yfloatVal, zfloatVal);
|
||||
case GYRO:
|
||||
final int gyroXintVal = ((int) data[1] << 8) | (data[0] & 0xff);
|
||||
final float gyroXfloatVal = (float) gyroXintVal * 10;
|
||||
|
||||
final int gyroYintVal = ((int) data[3] << 8) | (data[2] & 0xff);
|
||||
final float gyroYfloatVal = (float) gyroYintVal * 10;
|
||||
|
||||
final int gyroZintVal = ((int) data[5] << 8) | (data[4] & 0xff);
|
||||
|
||||
final float gyroZfloatVal = (float) gyroZintVal * 10;
|
||||
return format(TRIPLE_VALUE, gyroXfloatVal, gyroYfloatVal, gyroZfloatVal);
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
private static String format(final String type, final Object... values) {
|
||||
return String.format(Locale.ENGLISH, type, values);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
import com.wolkabout.hexiwear.view.Input;
|
||||
|
||||
import org.androidannotations.annotations.EBean;
|
||||
import org.androidannotations.annotations.RootContext;
|
||||
import org.androidannotations.annotations.UiThread;
|
||||
|
||||
|
||||
@EBean
|
||||
public class Dialog {
|
||||
|
||||
@RootContext
|
||||
Activity activity;
|
||||
|
||||
@UiThread
|
||||
public void showWarning(final int stringResource){
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setPositiveButton(activity.getString(R.string.dismiss), new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
builder.setMessage(activity.getString(stringResource));
|
||||
builder.show();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void showInfo(final int stringResource, final boolean finishActivity){
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setPositiveButton(activity.getString(R.string.ok), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final DialogInterface dialog, final int which) {
|
||||
if (finishActivity) {
|
||||
activity.finish();
|
||||
}
|
||||
}
|
||||
});
|
||||
builder.setMessage(activity.getString(stringResource));
|
||||
builder.show();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.util;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import com.wolkabout.hexiwear.model.Characteristic;
|
||||
import com.wolkabout.hexiwear.model.HexiwearDevice;
|
||||
import com.wolkabout.wolkrestandroid.Credentials_;
|
||||
import com.wolkabout.wolkrestandroid.dto.PointWithFeedsResponse;
|
||||
|
||||
import org.androidannotations.annotations.AfterInject;
|
||||
import org.androidannotations.annotations.EBean;
|
||||
import org.androidannotations.annotations.RootContext;
|
||||
import org.androidannotations.annotations.sharedpreferences.Pref;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@EBean(scope = EBean.Scope.Singleton)
|
||||
public class HexiwearDevices {
|
||||
|
||||
private static final String NAME_SUFFIX = "_deviceName";
|
||||
private static final String SERIAL_SUFFIX = "_deviceSerial";
|
||||
private static final String PASSWORD_SUFFIX = "_devicePassword";
|
||||
private static final String WOLK_NAME_SUFFIX = "_wolkName";
|
||||
private static final String SHOULD_TRANSMIT_SUFFIX = "_shouldTransmit";
|
||||
private static final String PUBLISH_INTERVAL_SUFFIX = "_publishInterval";
|
||||
private static final String KEEP_ALIVE_SUFFIX = "_keepAlive";
|
||||
|
||||
private SharedPreferences preferences;
|
||||
private String account;
|
||||
|
||||
@Pref
|
||||
Credentials_ credentials;
|
||||
|
||||
@RootContext
|
||||
Context context;
|
||||
|
||||
@AfterInject
|
||||
public void init() {
|
||||
account = credentials.username().get();
|
||||
preferences = context.getSharedPreferences("HEX_" + account, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public Set<HexiwearDevice> getDevices() {
|
||||
final Set<HexiwearDevice> devices = new HashSet<>();
|
||||
final Set<String> addresses = preferences.getStringSet(account, new HashSet<String>());
|
||||
for (String address : addresses) {
|
||||
final String name = preferences.getString(address + NAME_SUFFIX, "");
|
||||
final String serial = preferences.getString(address + SERIAL_SUFFIX, "");
|
||||
final String password = preferences.getString(address + PASSWORD_SUFFIX, "");
|
||||
final String wolkName = preferences.getString(address + WOLK_NAME_SUFFIX, "");
|
||||
|
||||
final HexiwearDevice device = new HexiwearDevice(name, serial, address, password, wolkName);
|
||||
devices.add(device);
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
public HexiwearDevice getDevice(String address) {
|
||||
final Set<HexiwearDevice> hexiwearDevices = getDevices();
|
||||
for (HexiwearDevice device : hexiwearDevices) {
|
||||
if (device.getDeviceAddress().equalsIgnoreCase(address)) {
|
||||
return device;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isRegistered(BluetoothDevice device) {
|
||||
return getDevice(device.getAddress()) != null;
|
||||
}
|
||||
|
||||
public void storeDevice(HexiwearDevice hexiwearDevice) {
|
||||
final SharedPreferences.Editor editor = preferences.edit();
|
||||
final Set<String> addresses = preferences.getStringSet(account, new HashSet<String>());
|
||||
final String address = hexiwearDevice.getDeviceAddress();
|
||||
addresses.add(address);
|
||||
editor.putStringSet(account, addresses);
|
||||
editor.putString(address + NAME_SUFFIX, hexiwearDevice.getDeviceName());
|
||||
editor.putString(address + SERIAL_SUFFIX, hexiwearDevice.getDeviceSerial());
|
||||
editor.putString(address + PASSWORD_SUFFIX, hexiwearDevice.getDevicePassword());
|
||||
editor.putString(address + WOLK_NAME_SUFFIX, hexiwearDevice.getWolkName());
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public Map<String, Boolean> getDisplayPreferences(String deviceAddress) {
|
||||
final Map<String, Boolean> displayPrefs = new HashMap<>();
|
||||
|
||||
final List<Characteristic> readings = Characteristic.getReadings();
|
||||
for (Characteristic reading : readings) {
|
||||
displayPrefs.put(reading.name(), preferences.getBoolean(deviceAddress + reading.name(), true));
|
||||
}
|
||||
|
||||
return displayPrefs;
|
||||
}
|
||||
|
||||
public List<String> getEnabledPreferences(String deviceAddress) {
|
||||
final List<String> enabledPrefs = new ArrayList<>();
|
||||
|
||||
final Map<String, Boolean> displayPreferences = getDisplayPreferences(deviceAddress);
|
||||
for (Map.Entry<String, Boolean> entry : displayPreferences.entrySet()) {
|
||||
if (entry.getValue()) {
|
||||
enabledPrefs.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
return enabledPrefs;
|
||||
}
|
||||
|
||||
public void setDisplayPreferences(String deviceAddress, Map<String, Boolean> displayPrefs) {
|
||||
final SharedPreferences.Editor editor = preferences.edit();
|
||||
for (Map.Entry<String, Boolean> entry : displayPrefs.entrySet()) {
|
||||
editor.putBoolean(deviceAddress + entry.getKey(), entry.getValue());
|
||||
}
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static List<HexiwearDevice> getDevices(List<PointWithFeedsResponse> response) {
|
||||
final List<HexiwearDevice> devices = new ArrayList<>();
|
||||
|
||||
for (PointWithFeedsResponse pwf : response) {
|
||||
if (pwf.getDeviceSerial().length() > 8 && "HX".equalsIgnoreCase(pwf.getDeviceSerial().substring(4, 6))) {
|
||||
devices.add(new HexiwearDevice(pwf.getName(), pwf.getDeviceSerial(), "", "", pwf.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
public void setPublishInterval(final HexiwearDevice device, final int interval) {
|
||||
preferences.edit().putInt(device.getDeviceAddress() + PUBLISH_INTERVAL_SUFFIX, interval).apply();
|
||||
}
|
||||
|
||||
public int getPublishInterval(final HexiwearDevice device) {
|
||||
return preferences.getInt(device.getDeviceAddress() + PUBLISH_INTERVAL_SUFFIX, 10);
|
||||
}
|
||||
|
||||
public void toggleTracking(final BluetoothDevice device) {
|
||||
final boolean isTracking = shouldTransmit(device);
|
||||
preferences.edit().putBoolean(device.getAddress() + SHOULD_TRANSMIT_SUFFIX, !isTracking).apply();
|
||||
}
|
||||
|
||||
public void toggleTracking(final HexiwearDevice device) {
|
||||
final boolean isTracking = shouldTransmit(device);
|
||||
preferences.edit().putBoolean(device.getDeviceAddress() + SHOULD_TRANSMIT_SUFFIX, !isTracking).apply();
|
||||
}
|
||||
|
||||
public boolean shouldTransmit(final BluetoothDevice device) {
|
||||
return preferences.getBoolean(device.getAddress() + SHOULD_TRANSMIT_SUFFIX, false);
|
||||
}
|
||||
|
||||
public boolean shouldTransmit(final HexiwearDevice device) {
|
||||
return preferences.getBoolean(device.getDeviceAddress() + SHOULD_TRANSMIT_SUFFIX, false);
|
||||
}
|
||||
|
||||
public void setKeepAlive(final HexiwearDevice device, final boolean keepAlive) {
|
||||
preferences.edit().putBoolean(device.getDeviceAddress() + KEEP_ALIVE_SUFFIX, keepAlive).apply();
|
||||
}
|
||||
|
||||
public boolean shouldKeepAlive(final HexiwearDevice device) {
|
||||
return preferences.getBoolean(device.getDeviceAddress() + KEEP_ALIVE_SUFFIX, true);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
import com.wolkabout.hexiwear.adapter.HexiwearListAdapter;
|
||||
import com.wolkabout.hexiwear.model.HexiwearDevice;
|
||||
|
||||
import org.androidannotations.annotations.AfterViews;
|
||||
import org.androidannotations.annotations.Bean;
|
||||
import org.androidannotations.annotations.EViewGroup;
|
||||
import org.androidannotations.annotations.ItemClick;
|
||||
import org.androidannotations.annotations.ViewById;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@EViewGroup(R.layout.view_register_device)
|
||||
public class DeviceActivation extends LinearLayout {
|
||||
|
||||
@ViewById
|
||||
TextView serialField;
|
||||
|
||||
@ViewById
|
||||
Input deviceName;
|
||||
|
||||
@ViewById
|
||||
ListView hexiwearDevices;
|
||||
|
||||
@Bean
|
||||
HexiwearListAdapter adapter;
|
||||
|
||||
public DeviceActivation(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@AfterViews
|
||||
void setDeviceList() {
|
||||
hexiwearDevices.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
|
||||
hexiwearDevices.setAdapter(adapter);
|
||||
hexiwearDevices.setItemChecked(0, true);
|
||||
}
|
||||
|
||||
public void setDevices(List<HexiwearDevice> devices) {
|
||||
adapter.update(devices);
|
||||
}
|
||||
|
||||
public boolean isNewDeviceSelected() {
|
||||
return deviceName.isEnabled();
|
||||
}
|
||||
|
||||
public String getDeviceSerial() {
|
||||
return serialField.getText().toString();
|
||||
}
|
||||
|
||||
public String getWolkName() {
|
||||
return deviceName.getValue();
|
||||
}
|
||||
|
||||
@ItemClick(R.id.hexiwearDevices)
|
||||
void onDeviceSelected(HexiwearDevice device) {
|
||||
final String wolkName = device.getWolkName();
|
||||
final String deviceSerial = device.getDeviceSerial();
|
||||
|
||||
deviceName.setText(TextUtils.isEmpty(wolkName) ? getContext().getString(R.string.activation_dialog_default_name) : wolkName);
|
||||
deviceName.setEnabled(TextUtils.isEmpty(deviceSerial));
|
||||
serialField.setText(deviceSerial);
|
||||
}
|
||||
|
||||
}
|
160
android/app/src/main/java/com/wolkabout/hexiwear/view/Input.java
Normal file
@ -0,0 +1,160 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.support.design.widget.TextInputLayout;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.text.InputFilter;
|
||||
import android.text.InputType;
|
||||
import android.text.TextUtils;
|
||||
import android.text.method.PasswordTransformationMethod;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
|
||||
import org.androidannotations.annotations.AfterViews;
|
||||
import org.androidannotations.annotations.EViewGroup;
|
||||
import org.androidannotations.annotations.UiThread;
|
||||
import org.androidannotations.annotations.ViewById;
|
||||
|
||||
@EViewGroup(R.layout.view_input)
|
||||
public class Input extends TextInputLayout {
|
||||
|
||||
@ViewById
|
||||
TextView text;
|
||||
|
||||
private String defaultText;
|
||||
private String hint;
|
||||
private int drawable;
|
||||
private int maxLength;
|
||||
private int textColor;
|
||||
private String actionLabel;
|
||||
private Type type;
|
||||
|
||||
public Input(final Context context, final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.Input);
|
||||
defaultText = typedArray.getString(R.styleable.Input_text);
|
||||
hint = typedArray.getString(R.styleable.Input_hint);
|
||||
drawable = typedArray.getResourceId(R.styleable.Input_drawable, 0);
|
||||
maxLength = typedArray.getResourceId(R.styleable.Input_maxLength, 100);
|
||||
actionLabel = typedArray.getString(R.styleable.Input_actionLabel);
|
||||
textColor = typedArray.getColor(R.styleable.Input_textColor, ContextCompat.getColor(getContext(), R.color.primary_text));
|
||||
|
||||
final int typeOrdinal = typedArray.getInt(R.styleable.Input_type, InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||
type = Type.byOrdinal(typeOrdinal);
|
||||
|
||||
typedArray.recycle();
|
||||
}
|
||||
|
||||
@AfterViews
|
||||
void init() {
|
||||
setHint(hint);
|
||||
text.setCompoundDrawablesWithIntrinsicBounds(drawable, 0, 0, 0);
|
||||
text.setFilters(new InputFilter[] {new InputFilter.LengthFilter(maxLength)});
|
||||
text.setInputType(type.flag);
|
||||
text.setTextColor(textColor);
|
||||
|
||||
if (!TextUtils.isEmpty(defaultText)) {
|
||||
text.setText(defaultText);
|
||||
}
|
||||
|
||||
if (type == Type.PASSWORD) {
|
||||
text.setTransformationMethod(PasswordTransformationMethod.getInstance());
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(actionLabel)) {
|
||||
text.setImeActionLabel(actionLabel, EditorInfo.IME_ACTION_UNSPECIFIED);
|
||||
}
|
||||
}
|
||||
|
||||
public void setText(String value) {
|
||||
text.setText(value);
|
||||
}
|
||||
|
||||
public void setText(int stringResource) {
|
||||
text.setText(getContext().getString(stringResource));
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return text.getText().toString();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return text.getText().toString().isEmpty();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void clear() {
|
||||
text.setText("");
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void setError(String error) {
|
||||
text.setError(error);
|
||||
text.requestFocus();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void setError(int errorResource) {
|
||||
text.setError(getContext().getString(errorResource));
|
||||
text.requestFocus();
|
||||
}
|
||||
|
||||
public void setPasswordVisibility(boolean visible) {
|
||||
text.setTransformationMethod(visible ? null : PasswordTransformationMethod.getInstance());
|
||||
}
|
||||
|
||||
public void setOnEditorActionListener(TextView.OnEditorActionListener listener) {
|
||||
text.setOnEditorActionListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
text.setEnabled(enabled);
|
||||
super.setEnabled(enabled);
|
||||
}
|
||||
|
||||
private enum Type {
|
||||
NAME(InputType.TYPE_TEXT_VARIATION_PERSON_NAME | InputType.TYPE_TEXT_FLAG_CAP_WORDS),
|
||||
TEXT(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS),
|
||||
EMAIL(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS),
|
||||
PASSWORD(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD),
|
||||
NUMBER(InputType.TYPE_CLASS_NUMBER);
|
||||
|
||||
private int flag;
|
||||
|
||||
Type(int flag) {
|
||||
this.flag = flag;
|
||||
}
|
||||
|
||||
static Type byOrdinal(int ordinal) {
|
||||
return Type.values()[ordinal];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
import com.wolkabout.hexiwear.model.Characteristic;
|
||||
|
||||
import org.androidannotations.annotations.AfterViews;
|
||||
import org.androidannotations.annotations.EViewGroup;
|
||||
import org.androidannotations.annotations.ViewById;
|
||||
|
||||
@EViewGroup
|
||||
public class Reading extends LinearLayout {
|
||||
|
||||
@ViewById
|
||||
ImageView image;
|
||||
|
||||
private Characteristic characteristic;
|
||||
private int drawable;
|
||||
|
||||
public Reading(final Context context, final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.Reading);
|
||||
drawable = typedArray.getResourceId(R.styleable.Reading_image, 0);
|
||||
final int readingTypeOrdinal = typedArray.getInt(R.styleable.Reading_readingType, 0);
|
||||
characteristic = Characteristic.byOrdinal(readingTypeOrdinal);
|
||||
typedArray.recycle();
|
||||
}
|
||||
|
||||
@AfterViews
|
||||
void init() {
|
||||
image.setImageResource(drawable);
|
||||
}
|
||||
|
||||
public Characteristic getReadingType() {
|
||||
return characteristic;
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
|
||||
import org.androidannotations.annotations.AfterViews;
|
||||
import org.androidannotations.annotations.EViewGroup;
|
||||
import org.androidannotations.annotations.ViewById;
|
||||
|
||||
@EViewGroup(R.layout.view_reading_single)
|
||||
public class SingleReading extends Reading {
|
||||
|
||||
@ViewById
|
||||
TextView text;
|
||||
|
||||
public SingleReading(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
text.setText(value);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Hexiwear application is used to pair with Hexiwear BLE devices
|
||||
* and send sensor readings to WolkSense sensor data cloud
|
||||
*
|
||||
* Copyright (C) 2016 WolkAbout Technology s.r.o.
|
||||
*
|
||||
* Hexiwear 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Hexiwear 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.wolkabout.hexiwear.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.wolkabout.hexiwear.R;
|
||||
|
||||
import org.androidannotations.annotations.AfterViews;
|
||||
import org.androidannotations.annotations.EViewGroup;
|
||||
import org.androidannotations.annotations.ViewById;
|
||||
|
||||
@EViewGroup(R.layout.view_reading_tripple)
|
||||
public class TripleReading extends Reading {
|
||||
|
||||
@ViewById
|
||||
TextView firstValue;
|
||||
|
||||
@ViewById
|
||||
TextView secondValue;
|
||||
|
||||
@ViewById
|
||||
TextView thirdValue;
|
||||
|
||||
public TripleReading(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public void setFirstValue(String value) {
|
||||
firstValue.setText(value);
|
||||
}
|
||||
|
||||
public void setSecondValue(String value) {
|
||||
secondValue.setText(value);
|
||||
}
|
||||
|
||||
public void setThirdValue(String value) {
|
||||
thirdValue.setText(value);
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 147 B |
BIN
android/app/src/main/res/drawable-hdpi/ic_clear_white_24dp.png
Normal file
After Width: | Height: | Size: 221 B |
After Width: | Height: | Size: 460 B |
After Width: | Height: | Size: 114 B |
BIN
android/app/src/main/res/drawable-mdpi/ic_clear_white_24dp.png
Normal file
After Width: | Height: | Size: 175 B |
After Width: | Height: | Size: 326 B |
BIN
android/app/src/main/res/drawable-xhdpi/ic_accelerometer.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 150 B |
BIN
android/app/src/main/res/drawable-xhdpi/ic_battery.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
After Width: | Height: | Size: 652 B |
After Width: | Height: | Size: 945 B |
BIN
android/app/src/main/res/drawable-xhdpi/ic_calories.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
android/app/src/main/res/drawable-xhdpi/ic_clear_white_24dp.png
Normal file
After Width: | Height: | Size: 257 B |
After Width: | Height: | Size: 582 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.0 KiB |
BIN
android/app/src/main/res/drawable-xhdpi/ic_gyroscope.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
android/app/src/main/res/drawable-xhdpi/ic_heartbeat.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
android/app/src/main/res/drawable-xhdpi/ic_humidity.png
Normal file
After Width: | Height: | Size: 9.3 KiB |
BIN
android/app/src/main/res/drawable-xhdpi/ic_light.png
Normal file
After Width: | Height: | Size: 8.2 KiB |
BIN
android/app/src/main/res/drawable-xhdpi/ic_magnet.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
android/app/src/main/res/drawable-xhdpi/ic_pressure.png
Normal file
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 562 B |
BIN
android/app/src/main/res/drawable-xhdpi/ic_signal_strength_1.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
android/app/src/main/res/drawable-xhdpi/ic_signal_strength_2.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
android/app/src/main/res/drawable-xhdpi/ic_signal_strength_3.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
android/app/src/main/res/drawable-xhdpi/ic_signal_strength_4.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
android/app/src/main/res/drawable-xhdpi/ic_signal_strength_5.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
android/app/src/main/res/drawable-xhdpi/ic_steps.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
android/app/src/main/res/drawable-xhdpi/ic_temperature.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
android/app/src/main/res/drawable-xhdpi/logo.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 186 B |
BIN
android/app/src/main/res/drawable-xxhdpi/ic_clear_white_24dp.png
Normal file
After Width: | Height: | Size: 347 B |
After Width: | Height: | Size: 843 B |
After Width: | Height: | Size: 222 B |
After Width: | Height: | Size: 436 B |
After Width: | Height: | Size: 1.0 KiB |
5
android/app/src/main/res/drawable/button_text.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true" android:color="@color/secondary_text"/>
|
||||
<item android:color="@color/grayish"/>
|
||||
</selector>
|
34
android/app/src/main/res/layout/activity_firmware_select.xml
Normal file
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context="com.wolkabout.hexiwear.activity.FirmwareSelectActivity">
|
||||
|
||||
<android.support.design.widget.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay"/>
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
|
||||
<ListView
|
||||
android:id="@+id/firmwareList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:dividerHeight="1dp"
|
||||
android:padding="8dp"
|
||||
android:divider="@color/primary"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
149
android/app/src/main/res/layout/activity_login.xml
Normal file
@ -0,0 +1,149 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/primary"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
tools:context=".activity.LoginActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/signInElements"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_marginLeft="24dp"
|
||||
android:layout_marginRight="24dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/signInForm"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageReadingSensor"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:src="@drawable/logo"/>
|
||||
|
||||
<com.wolkabout.hexiwear.view.Input_
|
||||
android:id="@+id/emailField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
app:hint="@string/login_email_hint"
|
||||
app:textColor="@color/white"
|
||||
app:type="email"/>
|
||||
|
||||
<com.wolkabout.hexiwear.view.Input_
|
||||
android:id="@+id/passwordField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
app:actionLabel="@string/login_sign_in"
|
||||
app:hint="@string/login_password_hint"
|
||||
app:textColor="@color/white"
|
||||
app:type="password"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/signInButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginTop="@dimen/min_margin"
|
||||
android:background="@android:color/transparent"
|
||||
android:gravity="end"
|
||||
android:text="@string/login_sign_in"
|
||||
android:textColor="@color/white"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/signUpForm"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<Button
|
||||
android:id="@+id/resetPassword"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/min_margin"
|
||||
android:background="@android:color/transparent"
|
||||
android:gravity="center"
|
||||
android:text="@string/login_forgot_password"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/createAccountText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:text="@string/login_register_hint"
|
||||
android:textColor="@color/grayish"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/signUp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="@dimen/min_margin"
|
||||
android:background="@android:color/transparent"
|
||||
android:gravity="center"
|
||||
android:text="@string/login_registration"
|
||||
android:textColor="@color/white"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/signingInElements"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageReadingTrend"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/logo"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/signingInLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:elevation="25dp"
|
||||
android:text="@string/login_signing_in"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
53
android/app/src/main/res/layout/activity_main.xml
Normal file
@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".activity.MainActivity">
|
||||
|
||||
<android.support.design.widget.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right"
|
||||
android:indeterminate="true"
|
||||
android:visibility="invisible" />
|
||||
|
||||
</android.support.v7.widget.Toolbar>
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
|
||||
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/swipeRefresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="@dimen/activity_vertical_margin"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
tools:context="eu.execom.wolkabout.readings.ReadingsFragment">
|
||||
|
||||
<ListView
|
||||
android:id="@+id/listDevices"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:divider="@color/primary"
|
||||
android:dividerHeight="1dp"
|
||||
android:scrollbars="vertical" />
|
||||
|
||||
</android.support.v4.widget.SwipeRefreshLayout>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
73
android/app/src/main/res/layout/activity_password_change.xml
Normal file
@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context="com.wolkabout.hexiwear.activity.PasswordChangeActivity">
|
||||
|
||||
<android.support.design.widget.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
|
||||
<android.support.v7.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/change_password_header"
|
||||
android:textColor="@color/primary_dark" />
|
||||
|
||||
<com.wolkabout.hexiwear.view.Input_
|
||||
android:id="@+id/currentPassword"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
app:hint="@string/change_password_current_password"
|
||||
app:type="password" />
|
||||
|
||||
<com.wolkabout.hexiwear.view.Input_
|
||||
android:id="@+id/newPassword"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:hint="@string/change_password_new_password"
|
||||
app:type="password" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/changePassword"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@color/primary"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingStart="8dp"
|
||||
android:text="@string/discovery_change_password"
|
||||
android:textColor="@color/white" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</android.support.v7.widget.CardView>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
143
android/app/src/main/res/layout/activity_readings.xml
Normal file
@ -0,0 +1,143 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/coordinator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".activity.ReadingsActivity">
|
||||
|
||||
<android.support.design.widget.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:indeterminate="true"
|
||||
android:visibility="invisible"/>
|
||||
</android.support.v7.widget.Toolbar>
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/scrollView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/connectionStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="Connecting..."
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/readings"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.wolkabout.hexiwear.view.SingleReading_
|
||||
android:id="@+id/readingBattery"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:image="@drawable/ic_battery"
|
||||
app:readingType="battery"/>
|
||||
|
||||
<com.wolkabout.hexiwear.view.SingleReading_
|
||||
android:id="@+id/readingTemperature"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:image="@drawable/ic_temperature"
|
||||
app:readingType="temperature"/>
|
||||
|
||||
<com.wolkabout.hexiwear.view.SingleReading_
|
||||
android:id="@+id/readingHumidity"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:image="@drawable/ic_humidity"
|
||||
app:readingType="humidity"/>
|
||||
|
||||
<com.wolkabout.hexiwear.view.SingleReading_
|
||||
android:id="@+id/readingPressure"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:image="@drawable/ic_pressure"
|
||||
app:readingType="pressure"/>
|
||||
|
||||
<com.wolkabout.hexiwear.view.SingleReading_
|
||||
android:id="@+id/readingHeartRate"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:image="@drawable/ic_heartbeat"
|
||||
app:readingType="heartrate"/>
|
||||
|
||||
<com.wolkabout.hexiwear.view.SingleReading_
|
||||
android:id="@+id/readingLight"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:image="@drawable/ic_light"
|
||||
app:readingType="light"/>
|
||||
|
||||
<com.wolkabout.hexiwear.view.SingleReading_
|
||||
android:id="@+id/readingSteps"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:image="@drawable/ic_steps"
|
||||
app:readingType="steps"/>
|
||||
|
||||
<com.wolkabout.hexiwear.view.SingleReading_
|
||||
android:id="@+id/readingCalories"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:image="@drawable/ic_calories"
|
||||
app:readingType="calories"/>
|
||||
|
||||
<com.wolkabout.hexiwear.view.TripleReading_
|
||||
android:id="@+id/readingAcceleration"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:image="@drawable/ic_accelerometer"
|
||||
app:readingType="acceleration"/>
|
||||
|
||||
<com.wolkabout.hexiwear.view.TripleReading_
|
||||
android:id="@+id/readingMagnet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:image="@drawable/ic_magnet"
|
||||
app:readingType="magnet"/>
|
||||
|
||||
<com.wolkabout.hexiwear.view.TripleReading_
|
||||
android:id="@+id/readingGyro"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:image="@drawable/ic_gyroscope"
|
||||
app:readingType="gyro"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
41
android/app/src/main/res/layout/activity_reset_password.xml
Normal file
@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/primary"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context="com.wolkabout.hexiwear.activity.ResetPasswordActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="32dp"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<com.wolkabout.hexiwear.view.Input_
|
||||
android:id="@+id/email"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:actionLabel="@string/reset_password_button"
|
||||
app:hint="@string/reset_password_email"
|
||||
app:textColor="@color/white"
|
||||
app:type="email"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/resetPassword"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:gravity="center"
|
||||
android:text="@string/reset_password_button"
|
||||
android:textColor="@color/white"/>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
32
android/app/src/main/res/layout/activity_settings.xml
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".activity.SettingsActivity">
|
||||
|
||||
<android.support.design.widget.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:title="Settings"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/fragment"
|
||||
android:name="com.wolkabout.hexiwear.fragment.HexiwearSettingsFragment_"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
121
android/app/src/main/res/layout/activity_sign_up.xml
Normal file
@ -0,0 +1,121 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/primary"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
tools:context=".activity.SignUpActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/signUpElements"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_marginLeft="24dp"
|
||||
android:layout_marginRight="24dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageReadingSensor"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:src="@drawable/logo"/>
|
||||
|
||||
<com.wolkabout.hexiwear.view.Input_
|
||||
android:id="@+id/emailField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:hint="@string/registration_email_hint"
|
||||
app:textColor="@color/white"
|
||||
app:type="email"/>
|
||||
|
||||
<com.wolkabout.hexiwear.view.Input_
|
||||
android:id="@+id/passwordField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:hint="@string/registration_password_hint"
|
||||
app:textColor="@color/white"
|
||||
app:type="password"/>
|
||||
|
||||
<com.wolkabout.hexiwear.view.Input_
|
||||
android:id="@+id/firstNameField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:hint="@string/registration_first_name"
|
||||
app:textColor="@color/white"
|
||||
app:type="name"/>
|
||||
|
||||
<com.wolkabout.hexiwear.view.Input_
|
||||
android:id="@+id/lastNameField"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:hint="@string/registration_last_name"
|
||||
app:textColor="@color/white"
|
||||
app:type="name"/>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/termsAndConditions"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/registration_terms_and_conditions"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/signUpButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginTop="@dimen/min_margin"
|
||||
android:background="@android:color/transparent"
|
||||
android:gravity="end"
|
||||
android:text="@string/login_registration"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/signingInElements"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageReadingTrend"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/logo"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/signingInLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:elevation="25dp"
|
||||
android:text="@string/login_signing_in"
|
||||
android:textAppearance="?android:attr/textAppearanceMediumInverse"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
63
android/app/src/main/res/layout/item_device.xml
Normal file
@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:padding="4dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/deviceName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Medium Text"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/deviceUUID"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Small Text"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/discovery_bonded"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="bottom">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/otapMode"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:text="OTAP"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/signalStrength"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_marginStart="16dp" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
43
android/app/src/main/res/layout/item_firmware.xml
Normal file
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/fileName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/primary"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/version"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="0.4"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="@color/secondary_text"
|
||||
android:textStyle="italic"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/type"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_weight="0.6"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="@color/secondary_text"
|
||||
android:textStyle="italic"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
20
android/app/src/main/res/layout/item_hexiwear.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:padding="4dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/deviceName"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
14
android/app/src/main/res/layout/view_input.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/min_margin">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/min_margin"
|
||||
android:singleLine="true"
|
||||
android:ems="10" />
|
||||
</merge>
|