diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2b75303
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,13 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000..681f41a
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ xmlns:android
+
+ ^$
+
+
+
+
+
+
+
+
+ xmlns:.*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*:id
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ .*:name
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ name
+
+ ^$
+
+
+
+
+
+
+
+
+ style
+
+ ^$
+
+
+
+
+
+
+
+
+ .*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*
+
+ http://schemas.android.com/apk/res/android
+
+
+ ANDROID_ATTRIBUTE_ORDER
+
+
+
+
+
+
+ .*
+
+ .*
+
+
+ BY_NAME
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/dictionaries/masinell.xml b/.idea/dictionaries/masinell.xml
new file mode 100644
index 0000000..2dee1b5
--- /dev/null
+++ b/.idea/dictionaries/masinell.xml
@@ -0,0 +1,10 @@
+
+
+
+ elevatedmonitor
+ empatica
+ epfl
+ prodromal
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..15a15b2
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..2996d53
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..b6ea2b1
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/App.pdf b/App.pdf
new file mode 100644
index 0000000..fed95dd
Binary files /dev/null and b/App.pdf differ
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..2fb2e74
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,675 @@
+### GNU GENERAL PUBLIC LICENSE
+
+Version 3, 29 June 2007
+
+Copyright (C) 2007 Free Software Foundation, Inc.
+
+
+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.
+
+
+ Copyright (C)
+
+ 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 .
+
+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:
+
+ Copyright (C)
+ 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 .
+
+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 .
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..17ae4d4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,121 @@
+# Android app for multimodal data acquisition
+
+This repository contains an app for acquiring and storing data from multiple sensors.
+Currently, can be used with the following devices:
+
+- Empatica E4
+- Tablet/Smartphone built-in sensors
+- MetaMotion R
+
+## Usage
+
+In order to improve reliability, a bipartite structure has been implemented.
+In particular, the Main Activity acts as an interface between the user and the main service that constitutes the principal actor.
+The latter performs scans, handles the user's requests to connect remote devices,
+all the unexpected disconnection that may happen and receives the data from the wireless sensors.
+
+See App.pdf for the complete block diagram of the App including three accessory activities (in light blue).
+
+
+## Class DeviceManager
+
+DeviceManager is designed to abstract the devices’ individual APIs by providing standardized methods for the interaction
+with the devices themselves.
+
+Each device usually comes with its own API providing methods for authentication, connection, receiving the device’s data and parsing it.
+To be able to standardize the main operations that have to be performed on different devices, an interface called InterfaceManager has been defined.
+For every device, the methods of the said interface will be implemented according to the device's API.
+In this way, we will have as many objects implementing the InterfaceManager as the devices we are going to include in our setup.
+The interface contains the following method signatures:
+
+```java
+public interface InterfaceManager {
+ void scan();
+ void isDevice(String name, String address);
+ void stopScan();
+ void connect(String id);
+ void disconnect(String id);
+ void readBatteryLevel(String id);
+}
+```
+
+In particular, scan() is called whenever the user requires a scan. It has to be implemented according to the wireless technology the device uses.
+isDevice() is called every time the previous scan returns a result and it has to be implemented to detect if the device that has been scanned is the one we are interested in.
+Indeed, it can happen that the scan returns several devices the user is not concerned about.
+stopScan() is called to interrupt a scan.
+connect() is called whenever it is required to connect a device that has been scanned and has to be implemented with the method provided by the device-specific API to connect.
+disconnect() has to be implemented with the method provided by the device-specific API to disconnect.
+Finally, readBatteryLevel() is called in order to update the battery level meter on the GUI and has to be implemented according to the device-specific API method to get the battery level.
+
+Thus, for every device we have a deviceManager object that implements the previous interface.
+The advantage is that, to perform the same operation on different devices (for example, to connect or to require the battery level),
+we have just to call the corresponding method of the deviceManager corresponging to the device we are considering.
+
+## Class Device
+
+The architecture of the high-level description of every device consists of three levels;
+the first of them concerns the device itself.
+The abstract class Device represents this first level. It includes the methods for setting the name of the device and including all the sensors the device embeds in.
+The second level concerns each embedded sensor, represented by the abstract class Sensor (the accelerometer, the PPG sensor, the EDA sensor and the SKT sensor, in case of the Empatica E4).
+To add a sensor to a device, the setSensors() method has to be implemented and a Sensor object has to be added to the sensorArrayList list (field of Device).
+The method setSensors() is called by the constructor of the class Device.
+The third level represents the measurement the sensor provides, represented by the abstract class Data.
+For the Empatica E4, the accelerometer data is made up of three Data objects: acceleration in the three orthogonal axis X, Y, and Z.
+To add a Data to a specific sensor, the method addData() has to be called in the constructor with a Data object as a parameter.
+
+## Service Manager
+The Service ServiceManager constitutes the main actor of the entire application (see Figure App.pdf for the complete block diagram of the App).
+In particular, in its onCreate() method, it supplies a persistent notification to avoid the system killing it when reclaiming more memory
+(such as to display a large page in a web browser) and it instantiates the device-specific DeviceMangers for the devices the user has chosen.
+To do so, the service has a local list of the devices that have been chosen by the user. Let chosenDevices be this list.
+At this point, whatever application component binds to it will be able to:
+
+
+ - replace chosenDevices with the user’s new preferences;
+ - launch a Bluethoot scan;
+ - stop the Bluethoot scan;
+ - connect a specific device;
+ - disconnect a specific device;
+ - read the battery level of a specific device;
+ - specify if storing or rejecting the data of a specific device;
+ - change the text of the persistent notification or display a new temporary notification.
+
+At the same time, the client will be notified about:
+
+ - the list of the scanned devices (if among the required ones specified in chosenDevices);
+ - the list of the connected devices;
+ - the list of the devices whose data is currently stored in the phone memory;
+ - unexpected disconnections (if the device cannot provide automatic reconnection and requires the user interaction).
+
+## MainActivity
+
+As stated, the main role of the Main Activity is to provide the user with information related to the available devices and serve as an interface to the ServiceManager
+that takes care of all the important tasks such as launching Bluetooth scans, receiving data and data storage.
+
+In the onCreate(), we recollect the preferences expressed from the user in the SettingsActivity regarding which device have to be taken
+into account and starts the ServiceManager service if not already running. The activity then generates a local list of the devices (instances of the class Device) chosen by the user.
+In the onResume(), the Main Activity binds to ServiceManager and communicate to it the list of the chosen devices collected from the user preferences for details about service binding).
+Then, the local list of devices is updated with the information from ServiceManager. In particular, for every device in the list, the activity asks the service if:
+
+
+ - The device has been detected;
+ - The device is connected (only if the previous inquiry returned true);
+ - The battery level (only if the previous inquiry returned true);
+ - The device’s data is actually stored in the smartphone’s memory.
+
+The Floating Action Button with the pill icon is used to pop out an alertDialog containing a check box list containing several drugs. The user can select one or more.
+After dissmissing the alertDialog, the data is saved in the directory Documents/LogFromUser of the tablet/smartphone (Documents is a standard directory Android has in which to place documents that have been created by the user).
+
+The two switches allow the user to log their prodromal phase and pain. After toggling off the pain switch, a questionnaire is displayed.
+The json containing the answers of the user is then saved in Documents/Questionnaires.
+
+## SettingsActivity
+ The Settings Activity contains a list of checkboxes the user can use to express his preference in terms of devices to use.
+ It also includes a switch to enable or disable the automatic data storage and a textbox to insert the API key needed for some devices.
+ To access it, the user is asked to enter a password. This password is ciao. (Only meant to avoid the final user to mess up with the settings, nothing secret here).
+
+## Monitor Activity
+The Monitor Activity allows the user to visualize a plot in real-time of the data streamed by the connected devices. It is mainly used during the set-up to verify if the devices are correctly worn.
+
+
+
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..b08cb0b
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,58 @@
+apply plugin: 'com.android.application'
+apply plugin: 'com.google.gms.google-services'
+
+android {
+ compileSdkVersion 28
+ defaultConfig {
+ applicationId "ch.epfl.esl.elevatedmonitor"
+ minSdkVersion 22
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ ndk {
+ abiFilters "armeabi", "armeabi-v7a"
+ }
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+}
+
+repositories {
+ flatDir {
+ dirs 'libs'
+ }
+}
+
+dependencies {
+ implementation fileTree(include: ['*.jar'], dir: 'libs')
+ implementation 'com.android.support:appcompat-v7:28.0.0'
+ implementation 'com.android.support.constraint:constraint-layout:1.1.3'
+ implementation 'com.android.support:design:28.0.0'
+ implementation 'com.android.support:support-v4:28.0.0'
+ implementation 'com.android.support:support-annotations:28.0.0'
+ implementation 'android.arch.lifecycle:extensions:1.1.1'
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'com.android.support.test:runner:1.0.2'
+ androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+ implementation project(':empalink-2.2')
+ implementation 'com.mbientlab:metawear:3.7.1'
+ implementation 'com.squareup.okhttp:okhttp:2.7.2'
+ implementation 'com.jjoe64:graphview:4.2.2'
+ implementation 'iam.thevoid.batteryview:batteryview:0.1'
+ implementation 'com.google.firebase:firebase-database:17.0.0'
+ implementation 'com.alibaba:fastjson:1.2.47'
+ apply plugin: 'com.google.gms.google-services'
+ implementation "com.mikhaellopez:circularprogressbar:2.0.0"
+ implementation(name: 'hsapi_android-release', ext: 'aar')
+
+
+}
diff --git a/app/google-services.json b/app/google-services.json
new file mode 100644
index 0000000..aecc819
--- /dev/null
+++ b/app/google-services.json
@@ -0,0 +1,48 @@
+{
+ "project_info": {
+ "project_number": "459729699608",
+ "firebase_url": "https://elevatedmonitor.firebaseio.com",
+ "project_id": "elevatedmonitor",
+ "storage_bucket": "elevatedmonitor.appspot.com"
+ },
+ "client": [
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:459729699608:android:c7c7eb1a82aa9a41",
+ "android_client_info": {
+ "package_name": "ch.epfl.esl.elevatedmonitor"
+ }
+ },
+ "oauth_client": [
+ {
+ "client_id": "459729699608-3kuem7v37rroakqnljrr6he877k0v58v.apps.googleusercontent.com",
+ "client_type": 1,
+ "android_info": {
+ "package_name": "ch.epfl.esl.elevatedmonitor",
+ "certificate_hash": "f46d18171e37e82053e5712484a8ebd278da97cf"
+ }
+ },
+ {
+ "client_id": "459729699608-j49dfvs8a4gavq4aofe4gah7hqo83vn1.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ],
+ "api_key": [
+ {
+ "current_key": "AIzaSyA16nvSkeTMXy9TzO_K2tmKqP2XHbZ60-4"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": [
+ {
+ "client_id": "459729699608-j49dfvs8a4gavq4aofe4gah7hqo83vn1.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "configuration_version": "1"
+}
\ No newline at end of file
diff --git a/app/libs/hsapi_android-release.aar b/app/libs/hsapi_android-release.aar
new file mode 100644
index 0000000..54a8f1b
Binary files /dev/null and b/app/libs/hsapi_android-release.aar differ
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/app/release/ElevatedMonitor.apk b/app/release/ElevatedMonitor.apk
new file mode 100644
index 0000000..448a257
Binary files /dev/null and b/app/release/ElevatedMonitor.apk differ
diff --git a/app/release/ElevatedMonitor.zip b/app/release/ElevatedMonitor.zip
new file mode 100644
index 0000000..18b3f12
Binary files /dev/null and b/app/release/ElevatedMonitor.zip differ
diff --git a/app/release/output.json b/app/release/output.json
new file mode 100644
index 0000000..c429e31
--- /dev/null
+++ b/app/release/output.json
@@ -0,0 +1 @@
+[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
\ No newline at end of file
diff --git a/app/src/androidTest/java/ch/epfl/esl/elevatedmonitor/ExampleInstrumentedTest.java b/app/src/androidTest/java/ch/epfl/esl/elevatedmonitor/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..22acbec
--- /dev/null
+++ b/app/src/androidTest/java/ch/epfl/esl/elevatedmonitor/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package ch.epfl.esl.elevatedmonitor;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("ch.epfl.esl.elevatedmonitor", appContext.getPackageName());
+ }
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..c2a0669
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png
new file mode 100644
index 0000000..d306e41
Binary files /dev/null and b/app/src/main/ic_launcher-web.png differ
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/AddMonitorFragment.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/AddMonitorFragment.java
new file mode 100644
index 0000000..d1f2d2c
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/AddMonitorFragment.java
@@ -0,0 +1,372 @@
+package ch.epfl.esl.elevatedmonitor;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.support.annotation.NonNull;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.DialogFragment;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
+import android.widget.SpinnerAdapter;
+import android.widget.TextView;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.util.ArrayList;
+
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Device;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+
+
+public class AddMonitorFragment extends DialogFragment {
+ private static final int MARGIN = 16;
+ private static final int TEXT_SIZE = 14;
+ private static final String TAG = "ADD MONITOR";
+
+ public static AddMonitorFragment newInstance() {
+ return new AddMonitorFragment();
+ }
+
+ ArrayList listDeviceConnected = new ArrayList<>();
+ ArrayList listSensorsDevice = new ArrayList<>();
+
+ Device selectedDevice = null;
+ SensorDevice selectedSensor = null;
+
+ //--- Service Manager
+ private ServiceManagerConnection serviceManagerConnection = null;
+ // private ConnectedStateReceiver connectedStateReceiver = null;
+ private Boolean serviceManagerBound = false;
+
+ //--------------------------------------------------------------------------------------------//
+ // Override methods //
+ //--------------------------------------------------------------------------------------------//
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.dialog_add_monitor, container, false);
+ final DeviceAdapter deviceAdapter = new DeviceAdapter();
+ final SensorAdapter sensorAdapter = new SensorAdapter();
+ final Spinner spinnerDevice = v.findViewById(R.id.spinnerDeviceSession);
+ final Spinner spinnerSensor = v.findViewById(R.id.spinnerSensorMonitoring);
+ /*----------------------------------------------------------------------------------------//
+ //--- Initialize first spinner
+ //----------------------------------------------------------------------------------------*/
+ listDeviceConnected.addAll(ServiceManager.getConnectedDevices());
+
+ // Apply the adapter to the spinner
+ spinnerDevice.setAdapter(deviceAdapter);
+
+ class SpinnerDeviceOnItemSelectedListener implements AdapterView.OnItemSelectedListener {
+ public void onItemSelected(AdapterView> parent, View view, int pos, long id) {
+ Spinner spinnerSensor = parent.getRootView().findViewById(R.id.spinnerSensorMonitoring);
+ //Check if option is not the first one
+ if (pos == 0) {
+ //Snackbar.make(parent.getRootView(), R.string.select_device, Snackbar.LENGTH_SHORT).show();
+ listSensorsDevice.clear();
+ sensorAdapter.notifyDataSetChanged();
+ spinnerSensor.setEnabled(false);
+ } else {
+ selectedDevice = (Device) spinnerDevice.getSelectedItem();
+ listSensorsDevice.clear();
+ Device device = ((Device) parent.getAdapter().getItem(pos));
+ listSensorsDevice.addAll(device.getSensors());
+ sensorAdapter.notifyDataSetChanged();
+ spinnerSensor.setEnabled(true);
+ // Snackbar.make(parent.getRootView(), R.string.select_sensor, Snackbar.LENGTH_SHORT).show();
+ }
+ }
+
+ public void onNothingSelected(AdapterView parent) {
+ }
+ }
+ AdapterView.OnItemSelectedListener spinnerDeviceListener = new SpinnerDeviceOnItemSelectedListener();
+ spinnerDevice.setOnItemSelectedListener(spinnerDeviceListener);
+
+ /*----------------------------------------------------------------------------------------//
+ //--- Initialize second spinner.
+ //----------------------------------------------------------------------------------------*/
+ // Apply the adapter to the spinner
+
+ spinnerSensor.setAdapter(sensorAdapter);
+
+ class SpinnerSensorOnItemSelectedListener implements AdapterView.OnItemSelectedListener {
+ public void onItemSelected(AdapterView> parent, View view, int pos, long id) {
+ //Check if option is not the first one
+ if (listSensorsDevice.size() > 0) {
+ if (pos == 0) {
+ Snackbar.make(parent.getRootView(), R.string.select_sensor, Snackbar.LENGTH_SHORT).show();
+ } else {
+ selectedSensor = (SensorDevice) spinnerSensor.getSelectedItem();
+ }
+ }
+ }
+
+ public void onNothingSelected(AdapterView parent) {
+ }
+ }
+ AdapterView.OnItemSelectedListener spinnerSensorListener = new SpinnerSensorOnItemSelectedListener();
+ spinnerSensor.setOnItemSelectedListener(spinnerSensorListener);
+ spinnerSensor.setEnabled(false);
+
+ /*----------------------------------------------------------------------------------------//
+ // Watch for cancel button click.
+ //----------------------------------------------------------------------------------------*/
+ Button buttonCancel = v.findViewById(R.id.buttonCancelMonitoring);
+ buttonCancel.setOnClickListener(v12 -> {
+ // When button is clicked, call up to owning activity.
+ dismiss();
+ });
+
+ /*----------------------------------------------------------------------------------------//
+ // Watch for add button click.
+ //----------------------------------------------------------------------------------------*/
+ Button buttonAdd = v.findViewById(R.id.buttonAddMonitoring);
+ // When button is clicked, call up to owning activity.
+ buttonAdd.setOnClickListener(v1 -> {
+ if (spinnerDevice.getSelectedItemPosition() != 0 && spinnerSensor.getSelectedItemPosition() != 0) {
+ //--- First get selected device from Service Manager.
+ Device selectedDevice = ((Device) spinnerDevice.getSelectedItem());
+
+ //--- Then check inside connected devices.
+ boolean flagConnected = false;
+ for (Device device : ServiceManager.getConnectedDevices()) {
+ if (device.getId().equals(selectedDevice.getId()) && device.isConnected()) {
+ addMonitor(device, selectedSensor);
+ flagConnected = true;
+ break;
+
+ }
+ }
+
+ //--- If not initiate a connection.
+ if (!flagConnected) {
+ Log.d(TAG, "Device not connected, start a connection");
+ ServiceManager.startActionConnect(v1.getContext(), selectedDevice.getId(), selectedDevice.getManagerName());
+ Snackbar.make(v1.getRootView(), R.string.monitor_add_device, Snackbar.LENGTH_LONG).show();
+ }
+ } else if (spinnerDevice.getSelectedItemPosition() == 0) {
+ Snackbar.make(v1, R.string.select_device, Snackbar.LENGTH_SHORT).show();
+ } else {
+ Snackbar.make(v1, R.string.select_sensor, Snackbar.LENGTH_SHORT).show();
+ }
+ });
+
+ return v;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ /*-------------------------- Register the BroadCast Receiver -----------------------------*/
+ //--- The filter's action is BROADCAST_STATUS
+ // IntentFilter statusIntentFilter = new IntentFilter(Constants.BROADCAST_STATUS);
+ //--- Instantiates a new ScannedStateReceiver
+ // connectedStateReceiver = new ConnectedStateReceiver();
+ //--- Registers the ScannedStateReceiver and its intent filters
+ // LocalBroadcastManager.getInstance(this.getContext()).registerReceiver(connectedStateReceiver, statusIntentFilter);
+
+ //--- Start a new connection
+
+ if (serviceManagerConnection == null) {
+ serviceManagerConnection = new ServiceManagerConnection();
+ }
+
+ //------------------------------ Bind to LocalService ------------------------------------//
+ Intent serviceManagerIntent = new Intent(this.getActivity(), ServiceManager.class);
+ if (!serviceManagerBound) {
+ this.getActivity().bindService(serviceManagerIntent, serviceManagerConnection, Context.BIND_AUTO_CREATE);
+ }
+ }
+
+ @Override
+ public void onStop() {
+ if (serviceManagerBound) {
+ this.getActivity().unbindService(serviceManagerConnection);
+ }
+ super.onStop();
+ }
+
+ /**
+ * Return list of devices which were already connected to the app or that are currently connected.
+ * Reading into files.
+ */
+ private ArrayList getListDeviceSaved() {
+ //Variable declaration
+ ArrayList deviceList = new ArrayList<>();
+
+ //Get File
+ File path = this.getContext().getFilesDir();
+ File file = new File(path, Constants.FILE_DEVICES);
+
+ //Read file
+ if (file.exists()) {
+ try {
+ //---Read device list
+ FileInputStream fis = new FileInputStream(file);
+ ObjectInputStream ois = new ObjectInputStream(fis);
+ deviceList = (ArrayList) ois.readObject();
+ ois.close();
+ fis.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+ return deviceList;
+ }
+
+ private void addMonitor(Device device, SensorDevice sensor) {
+ Log.d(TAG, "A monitor will be added.");
+ MonitoringActivity.addMonitor(new Monitor(device, device.getSensor(sensor.getId()), this.getContext()));
+ MonitoringActivity.notifyAdapterDataSetChanged();
+ DialogFragment df = this;
+ df.dismiss();
+ }
+
+ class SensorAdapter extends BaseAdapter implements SpinnerAdapter {
+
+ @Override
+ public int getCount() {
+ return (listSensorsDevice.size() + 1);
+ }
+
+ @Override
+ public Object getItem(int position) {
+ if (position == 0) {
+ return null;
+ } else {
+ return listSensorsDevice.get(--position);
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return 0;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ LinearLayout ll = new LinearLayout(parent.getContext());
+ ll.setOrientation(LinearLayout.VERTICAL);
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
+ int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, MARGIN, parent.getResources().getDisplayMetrics());
+ params.setMargins(px, px, px, px);
+
+ TextView text = new TextView(parent.getContext());
+ text.setTextSize(TEXT_SIZE);
+ if (position == 0) {
+ text.setText(getResources().getString(R.string.select_sensor));
+ } else {
+ text.setText(listSensorsDevice.get(--position).getName());
+ }
+ ll.addView(text, params);
+ return ll;
+ }
+ }
+
+ class DeviceAdapter extends BaseAdapter implements SpinnerAdapter {
+
+ @Override
+ public int getCount() {
+ return (listDeviceConnected.size() + 1);
+ }
+
+ @Override
+ public Object getItem(int position) {
+ if (position == 0) {
+ return null;
+ } else {
+ return listDeviceConnected.get(--position);
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return 0;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ LinearLayout ll = new LinearLayout(parent.getContext());
+ ll.setOrientation(LinearLayout.VERTICAL);
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
+ int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, MARGIN, parent.getResources().getDisplayMetrics());
+ params.setMargins(px, px, px, px);
+
+ TextView text = new TextView(parent.getContext());
+ text.setTextSize(TEXT_SIZE);
+ if (position == 0) {
+ text.setText(getResources().getString(R.string.select_device));
+ } else {
+ text.setText(listDeviceConnected.get(--position).getName());
+ }
+ ll.addView(text, params);
+ return ll;
+ }
+ }
+
+
+ //--------------------------------------------------------------------------------------------//
+
+ /**
+ * Broadcast receiver for receiving status updates from the Devices Services to update list
+ */
+ /*
+ private class ConnectedStateReceiver extends BroadcastReceiver {
+ // Called when the BroadcastReceiver gets an Intent it's registered to receive
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String status = intent.getAction();
+ final String statusType = intent.getStringExtra(Constants.EXTENDED_DATA_STATUS);
+ if (Constants.BROADCAST_STATUS.equals(status)) {
+ if (ServiceManager.STATUS_CONNECTED.equals(statusType)) {
+ Log.d(TAG, "New connection");
+ for (Device device : ServiceManager.getConnectedDevices()) {
+ if (device != null && device.isConnected() && device.getId().equals(selectedDevice.getId())) {
+ addMonitor(device, selectedSensor);
+ }
+ }
+ }
+ }
+ }
+ }
+ */
+
+ private class ServiceManagerConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ // We've bound to LocalService, cast the IBinder and get LocalService instance
+ ServiceManager.ServiceManagerBinder binder = (ServiceManager.ServiceManagerBinder) service;
+ ServiceManager.serviceManager = binder.getService();
+ serviceManagerBound = true;
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName arg0) {
+ serviceManagerBound = false;
+ }
+ }
+
+
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/AddSessionFragment.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/AddSessionFragment.java
new file mode 100644
index 0000000..76e2906
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/AddSessionFragment.java
@@ -0,0 +1,236 @@
+package ch.epfl.esl.elevatedmonitor;
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.DialogFragment;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
+import android.widget.SpinnerAdapter;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Device;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+
+public class AddSessionFragment extends DialogFragment {
+ private static final int MARGIN = 16;
+ private static final int TEXT_SIZE = 14;
+ private static final String TAG = "ADD SESSION";
+
+ Device selectedDevice = null;
+ SensorDevice selectedSensor = null;
+
+ ArrayList listSensorsDevice = new ArrayList<>();
+
+ public static AddSessionFragment newInstance() {
+ return new AddSessionFragment();
+ }
+
+ //--------------------------------------------------------------------------------------------//
+ // Override methods //
+ //--------------------------------------------------------------------------------------------//
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.dialog_add_session, container, false);
+
+ final Spinner spinnerDevice = v.findViewById(R.id.spinnerDeviceSession);
+ final Spinner spinnerSensor = v.findViewById(R.id.spinnerSensorSession);
+ /*----------------------------------------------------------------------------------------//
+ //--- Initialize first spinner
+ //----------------------------------------------------------------------------------------*/
+
+ final DeviceAdapter deviceAdapter = new DeviceAdapter();
+ final SensorAdapter sensorAdapter = new SensorAdapter();
+
+ // Apply the adapter to the spinner
+ spinnerDevice.setAdapter(deviceAdapter);
+ spinnerSensor.setAdapter(sensorAdapter);
+
+ class SpinnerDeviceOnItemSelectedListener implements AdapterView.OnItemSelectedListener {
+ public void onItemSelected(AdapterView> parent, View view, int pos, long id) {
+ //Check if option is not the first one
+ if (pos == 0) {
+ Snackbar.make(parent.getRootView(), R.string.select_device, Snackbar.LENGTH_SHORT)
+ .show();
+
+ selectedDevice = null;
+ listSensorsDevice.clear();
+ sensorAdapter.notifyDataSetChanged();
+ spinnerSensor.setEnabled(false);
+ } else {
+ selectedDevice = (Device) spinnerDevice.getSelectedItem();
+
+ spinnerSensor.setEnabled(true);
+
+ //Set all sensors possibilities
+ listSensorsDevice = selectedDevice.getSensors();
+ sensorAdapter.notifyDataSetChanged();
+ }
+ }
+
+ public void onNothingSelected(AdapterView parent) {
+ }
+ }
+
+ spinnerDevice.setOnItemSelectedListener(new SpinnerDeviceOnItemSelectedListener());
+
+ //------------------------------ SENSOR SPINNER ------------------------------------------//
+
+ class SpinnerSensorOnItemSelectedListener implements AdapterView.OnItemSelectedListener {
+ public void onItemSelected(AdapterView> parent, View view, int pos, long id) {
+ //Check if option is not the first one
+ if (selectedDevice != null) {
+ if (pos == 0) {
+ selectedSensor = null;
+ } else {
+ selectedSensor = (SensorDevice) spinnerSensor.getSelectedItem();
+ }
+ }
+ }
+
+ public void onNothingSelected(AdapterView parent) {
+ }
+ }
+
+ spinnerSensor.setOnItemSelectedListener(new SpinnerSensorOnItemSelectedListener());
+
+ /*----------------------------------------------------------------------------------------//
+ // Watch for cancel button click.
+ //----------------------------------------------------------------------------------------*/
+ Button buttonCancel = v.findViewById(R.id.buttonCancelAddSession);
+ buttonCancel.setOnClickListener(v12 -> {
+ // When button is clicked, call up to owning activity.
+ dismiss();
+ });
+
+ /*----------------------------------------------------------------------------------------//
+ // Watch for add button click.
+ //----------------------------------------------------------------------------------------*/
+ Button buttonAdd = v.findViewById(R.id.buttonAddSession);
+ buttonAdd.setOnClickListener(v1 -> {
+ if (selectedDevice != null) {
+ Session session = new Session(selectedDevice, selectedSensor);
+ SavingActivity.sessionsList.add(session);
+ SavingActivity.sessionAdapter.notifyDataSetChanged();
+ dismiss();
+ } else {
+ Snackbar.make(v1.getRootView(), R.string.select_device, Snackbar.LENGTH_SHORT)
+ .show();
+ }
+ });
+
+
+ return v;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ }
+
+
+ //--- Adapter
+ class DeviceAdapter extends BaseAdapter implements SpinnerAdapter {
+
+ @Override
+ public int getCount() {
+ return (ServiceManager.getConnectedDevices().size() + 1);
+ }
+
+ @Override
+ public Object getItem(int position) {
+ if (position == 0) {
+ return null;
+ } else {
+ return ServiceManager.getConnectedDevices().get(--position);
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return 0;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ LinearLayout ll = new LinearLayout(parent.getContext());
+ ll.setOrientation(LinearLayout.VERTICAL);
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
+ int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, MARGIN, parent.getResources()
+ .getDisplayMetrics());
+ params.setMargins(px, px, px, px);
+
+ TextView text = new TextView(parent.getContext());
+ text.setTextSize(TEXT_SIZE);
+ if (position == 0) {
+ text.setText(getResources().getString(R.string.select_device));
+ } else {
+ text.setText(ServiceManager.getConnectedDevices().get(--position).getName());
+ }
+ ll.addView(text, params);
+ return ll;
+ }
+ }
+
+
+ class SensorAdapter extends BaseAdapter implements SpinnerAdapter {
+
+ @Override
+ public int getCount() {
+ return (listSensorsDevice.size() + 1);
+ }
+
+ @Override
+ public Object getItem(int position) {
+ if (position == 0) {
+ return null;
+ } else {
+ return listSensorsDevice.get(--position);
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return 0;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ LinearLayout ll = new LinearLayout(parent.getContext());
+ ll.setOrientation(LinearLayout.VERTICAL);
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
+ int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, MARGIN, parent.getResources()
+ .getDisplayMetrics());
+ params.setMargins(px, px, px, px);
+
+ TextView text = new TextView(parent.getContext());
+ text.setTextSize(TEXT_SIZE);
+ if (position == 0) {
+ text.setText(getResources().getString(R.string.all_sensor));
+ } else {
+ text.setText(listSensorsDevice.get(--position).getName());
+ }
+ ll.addView(text, params);
+ return ll;
+ }
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Constants.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Constants.java
new file mode 100644
index 0000000..0dd68b8
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Constants.java
@@ -0,0 +1,89 @@
+package ch.epfl.esl.elevatedmonitor;
+
+import android.graphics.Color;
+
+public class Constants {
+ //public static final
+ public static final int IBI_ID = 0;
+
+ // Add here your new class for devices only main class extending Device
+ public static final String[] DEVICE_CLASSES = {
+ //"ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Empatica",
+ "ch.epfl.esl.elevatedmonitor.Devices.Microsoft_Band_2.Band"};
+ //"ch.epfl.esl.elevatedmonitor.Devices.Phone.Phone"};
+ //File to store data
+ public static final String FILE_DEVICES = "ch.epfl.esl.elevatedmonitor.Constants.FILE_DEVICES";
+ static final String FILE_KEY = "ch.epfl.esl.elevatedmonitor.Constants.FILE_KEY";
+ // Defines a custom Intent action
+ static final String BROADCAST_STATUS = "ch.epfl.esl.elevatedmonitor.Constants.BROADCAST";
+ // Defines the key for the status "extra" in an Intent
+ static final String EXTENDED_DATA_STATUS = "ch.epfl.esl.elevatedmonitor.Constants.STATUS";
+ public static final String REQUEST_ENABLE_BT = "ch.epfl.esl.elevatedmonitor.Constants.REQUEST_ENABLE_BT";
+ static final String MISSING_KEY = "ch.epfl.esl.elevatedmonitor.Constants.MISSING_KEY";
+ static final String WRONG_KEY = "ch.epfl.esl.elevatedmonitor.Constants.WRONG_KEY";
+
+ static final String[] MANAGER_CLASSES = {
+ "ch.epfl.esl.elevatedmonitor.Devices.Hexoskin_smart.HexoskinManager",
+ "ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.EmpaticaManager",
+ "ch.epfl.esl.elevatedmonitor.Devices.Phone.PhoneManager",
+ "ch.epfl.esl.elevatedmonitor.Devices.MetaMotionR.MetaMotionManager"};
+ static final String[] DEVICES_NAME = {"Hexoskin", "Empatica", "Phone", "MetaWear 0", "MetaWear 1", "MetaWear 2"};
+ static final String[] DEVICES_CLASSES = {
+ "ch.epfl.esl.elevatedmonitor.Devices.Hexoskin_smart.Hexoskin",
+ "ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Empatica",
+ "ch.epfl.esl.elevatedmonitor.Devices.Phone.Phone",
+ "ch.epfl.esl.elevatedmonitor.Devices.MetaMotionR.MetaWear",
+ "ch.epfl.esl.elevatedmonitor.Devices.MetaMotionR.MetaWear",
+ "ch.epfl.esl.elevatedmonitor.Devices.MetaMotionR.MetaWear"};
+ static final String EMPATICA_CLASS = "ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Empatica";
+ static final String START_ACTIVITY_STATUS = "ch.epfl.esl.elevatedmonitor.Constants.START_ACTIVITY";
+ static final String REQUEST_PERMISSION_STATUS = "ch.epfl.esl.elevatedmonitor.Constants.REQUEST_PERMISSION";
+ static final String EXTENDED_DATA_INFOS = "ch.epfl.esl.elevatedmonitor.Constants.INFOS";
+ static final String START_ACTIVITY_INTENT = "ch.epfl.esl.elevatedmonitor.Constants.INTENT";
+ static final String START_ACTIVITY_CALLBACK = "ch.epfl.esl.elevatedmonitor.Constants.CALLBACK";
+ static final String REQUEST_PERMISSION_NAME = "ch.epfl.esl.elevatedmonitor.Constants.NAME";
+
+ //Constants for permissions
+ static final int PERMISSION_READ_EXTERNAL_STORAGE = 1;
+ static final int PERMISSION_WRITE_EXTERNAL_STORAGE = 2;
+ static final String REQUEST_PERMISSION_CALLBACK = "ch.epfl.esl.elevatedmonitor.Constants.CALLBACK";
+
+ static final int MILLI_SECONDS = 1000;
+
+ static class COLOR {
+ static final int JADE = Color.rgb(0, 177, 106);
+ }
+
+ public interface ACTION {
+ String START = "action.start";
+ String STOP = "action.stop";
+ //String KILL = "action.kill";
+ }
+
+ public enum TileMgmtAction {
+ IsTilePresent, AddTile, RemoveTile, UpdateTile,
+ }
+
+ public enum STATE {
+ ENABLED, CHECKED
+ }
+
+ public enum UI_UPDATE {
+ SWITCH_CONSENT, SWITCH_TILE, SNACKBAR, BAND_CONNECTION, RECORDER_STATE,
+ }
+
+ public static final int BTN_BAND_START = 1;
+ public static final int BTN_BAND_STOP = 2;
+ public static final int TEXT_BAND_TITLE = 3;
+ public static final int TEXT_BAND_HELP = 4;
+
+ static final String APP_SETTINGS = "app_settings";
+ static final String REMOTE_CONFIGURATION = "ciao";
+ static final String REMOTE_CONFIGURATION_TWO = "remote_configuration_two";
+ static final String DRUGS_NAMES = "drugsNames";
+ public static final String UNIQUE_INSTALLATION_ID = "unique_installation_id";
+ public static final String SNACKBAR_MESSAGE = "snackbar_message";
+ public static final String CONNECTION_STATUS = "connection_status";
+ public static final String RECORDING_STATE = "recording_state";
+
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/ESL_ECG/Ecg.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/ESL_ECG/Ecg.java
new file mode 100644
index 0000000..eab9d15
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/ESL_ECG/Ecg.java
@@ -0,0 +1,52 @@
+package ch.epfl.esl.elevatedmonitor.Devices.ESL_ECG;
+
+
+import android.content.Context;
+
+import java.util.ArrayList;
+
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceDevice;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Device;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+
+public class Ecg extends Device implements InterfaceDevice {
+ private static final String MANAGER_NAME = "ch.epfl.esl.elevatedmonitor.Devices.ESL_ECG.EcgManager";
+ public static final int ECG = 0;
+
+
+ public Ecg(Context context) {
+ super(context);
+ setSensors();
+ }
+
+ public Ecg(Context context, String name, String id) {
+ super(context);
+ setSensors();
+ setName(name);
+ setId(id);
+ }
+
+ @Override
+ public void setSensors() {
+ //--- Set different sensors of the phone
+ if (sensorArrayList == null) {
+ sensorArrayList = new ArrayList();
+ }
+ //sensorArrayList.add(new Ecg(deviceContext));
+ }
+
+ @Override
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public void setManagerName() {
+ this.managerName = MANAGER_NAME;
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/ESL_ECG/EcgManager.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/ESL_ECG/EcgManager.java
new file mode 100644
index 0000000..d106d34
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/ESL_ECG/EcgManager.java
@@ -0,0 +1,173 @@
+package ch.epfl.esl.elevatedmonitor.Devices.ESL_ECG;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
+
+import ch.epfl.esl.elevatedmonitor.Constants;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceManager;
+import ch.epfl.esl.elevatedmonitor.ServiceManager;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Device;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Manager;
+
+public class EcgManager extends Manager implements InterfaceManager {
+
+ //--- Variable declaration
+ //------ Private
+ private BluetoothAdapter mBluetoothAdapter;
+ private BluetoothBroadcast bluetoothBroadcast = new BluetoothBroadcast();
+
+
+ //--------- Statics
+ private static final String TAG = "ECG Manager";
+
+ private static final String[] mac_address = {"76:B7:B5:C7:7B:55"};
+
+ //--------- Flags
+ private Boolean scanning = false;
+ private Boolean connecting = false;
+
+ //--- Constructor
+ public EcgManager(Context context) {
+ super(context, TAG);
+
+ IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
+
+ context.registerReceiver(bluetoothBroadcast, filter);
+ }
+
+ //--- Functions to handle events from update status
+ private void connected() {
+ Log.d(TAG, "Device Connected");
+
+ //Stop searching for connection
+ connecting = false;
+ stopScan();
+
+ //Insert into file memory connected device
+ saveConnectedFile();
+
+ // Notify the Service Manager that there is a new connection
+ ServiceManager.startActionConnected(managerContext);
+ }
+
+ private void disconnected() {
+ for (Device device : getConnectedDevice()) {
+ if (device.isConnected()) {
+ device.setConnected(false);
+ ServiceManager.startActionDisconnect(managerContext, device.getName());
+ managerContext.unregisterReceiver(bluetoothBroadcast);
+ }
+ }
+
+ deleteDisconnectedDevice();
+ }
+
+ //--- Interface Manager
+ @Override
+ public void scan() {
+ if (!scanning) {
+
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ if (mBluetoothAdapter != null) {
+ if (!mBluetoothAdapter.isEnabled()) {
+ //--- Request Bluetooth right
+ Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+ ServiceManager.startActivityForResult(enableBtIntent, Constants.REQUEST_ENABLE_BT);
+ } else {
+ mBluetoothAdapter.startDiscovery();
+ }
+ }
+
+
+ scanning = true;
+ Log.d(TAG, "Scan started");
+
+
+ }
+ }
+
+ @Override
+ public void isDevice(String name, String address) {
+
+ }
+
+ @Override
+ public void stopScan() {
+ if (scanning) {
+ //Put flags false
+ scanning = false;
+ //Stop the process
+ Log.d(TAG, "Scan stopped");
+ } else {
+ Log.d(TAG, "Nothing to stop.");
+ }
+ }
+
+ @Override
+ public void connect(String id) {
+ if (!connecting) {
+ //Inform that we have to connect.
+ connecting = true;
+
+ Log.d(TAG, "Start the scan to connect.");
+ scan();
+ }
+
+ }
+
+ @Override
+ public void disconnect(String id) {
+
+ }
+
+ @Override
+ public void onActivityResult(String requestCode, int resultCode) {
+ /*
+ if (requestCode.equals(BluetoothAdapter.ACTION_REQUEST_ENABLE)) {
+ if (resultCode == Activity.RESULT_OK) {
+ if (scanning) {
+ scan();
+ }
+ }
+ }*/
+
+ }
+
+ @Override
+ public void readBatteryLevel(String id) {
+
+ }
+
+
+ // Create a BroadcastReceiver for ACTION_FOUND.
+ public class BluetoothBroadcast extends BroadcastReceiver {
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (BluetoothDevice.ACTION_FOUND.equals(action)) {
+ // Discovery has found a device. Get the BluetoothDevice
+ // object and its info from the Intent.
+ BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ String deviceName = device.getName();
+ String deviceHardwareAddress = device.getAddress();
+ Log.d(TAG, "found device: " + deviceName + " " + deviceHardwareAddress);
+
+ for (String mac : mac_address) {
+ if (deviceHardwareAddress.equals(mac)) {
+ Ecg ecg = new Ecg(managerContext, deviceName, deviceHardwareAddress);
+ addScannedDevice(ecg);
+ ServiceManager.startActionScanned(managerContext);
+ }
+ }
+ }
+ }
+ }
+
+ ;
+
+
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/Empatica.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/Empatica.java
new file mode 100644
index 0000000..ce64701
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/Empatica.java
@@ -0,0 +1,73 @@
+package ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4;
+
+
+import android.content.Context;
+
+import java.util.ArrayList;
+
+import ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Sensors.Accelerometer;
+import ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Sensors.BloodVolumePulse;
+import ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Sensors.ElectroDermalActivity;
+import ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Sensors.InterBeatInterval;
+import ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Sensors.Temperature;
+import ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Sensors.UserButton;
+import ch.epfl.esl.elevatedmonitor.Features.SignalFeatures;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceDevice;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Device;
+
+public class Empatica extends Device implements InterfaceDevice {
+ public static final int BUTTON = 5;
+ public static final int TEMPERATURE = 0;
+ public static final int PPG = 1;
+ public static final int EDA = 2;
+ public static final int ACCELEROMETER = 3;
+ public static final int IBI = 4;
+ private static final String MANAGER_NAME = "ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.EmpaticaManager";
+ static final int IPI = 6;
+
+
+ public Empatica(Context context) {
+ super(context);
+ setSensors();
+ setName("Empatica");
+ setId("N.D.");
+
+ }
+
+ public Empatica(Context context, String name, String id) {
+ super(context);
+ setSensors();
+ setName(name);
+ setId(id);
+ }
+
+ @Override
+ public void setSensors() {
+ //--- Set different sensors of the phone
+ if (sensorArrayList == null) {
+ sensorArrayList = new ArrayList<>();
+ }
+ sensorArrayList.add(new Temperature(deviceContext));
+ sensorArrayList.add(new BloodVolumePulse(deviceContext));
+ sensorArrayList.add(new ElectroDermalActivity(deviceContext));
+ sensorArrayList.add(new Accelerometer(deviceContext));
+ sensorArrayList.add(new InterBeatInterval(deviceContext));
+ sensorArrayList.add(new UserButton(deviceContext));
+ sensorArrayList.add(new SignalFeatures(deviceContext, IPI));
+ }
+
+ @Override
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public void setManagerName() {
+ this.managerName = MANAGER_NAME;
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/EmpaticaManager.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/EmpaticaManager.java
new file mode 100644
index 0000000..df87eea
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/EmpaticaManager.java
@@ -0,0 +1,457 @@
+package ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.empatica.empalink.ConnectionNotAllowedException;
+import com.empatica.empalink.EmpaDeviceManager;
+import com.empatica.empalink.EmpaticaDevice;
+import com.empatica.empalink.config.EmpaSensorType;
+import com.empatica.empalink.config.EmpaStatus;
+import com.empatica.empalink.delegate.EmpaDataDelegate;
+import com.empatica.empalink.delegate.EmpaStatusDelegate;
+
+import java.util.Calendar;
+
+import ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Sensors.Accelerometer;
+import ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Sensors.BloodVolumePulse;
+import ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Sensors.ElectroDermalActivity;
+import ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Sensors.InterBeatInterval;
+import ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Sensors.Temperature;
+import ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Sensors.UserButton;
+import ch.epfl.esl.elevatedmonitor.Features.SignalFeatures;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceManager;
+import ch.epfl.esl.elevatedmonitor.R;
+import ch.epfl.esl.elevatedmonitor.ServiceManager;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Device;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Manager;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+
+public class EmpaticaManager extends Manager implements InterfaceManager, EmpaStatusDelegate, EmpaDataDelegate {
+
+ //--- Variable declaration
+ public EmpaDeviceManager empaManager;
+ private EmpaStatus oldStatus = null;
+ private Empatica empatica = null;
+ private EmpaticaDevice bluetoothDevice_;
+ private Calendar time = Calendar.getInstance();
+
+ //--------- Statics
+ private static final String TAG = "Empatica Man SQUINCI";
+
+ //--------- Flags
+ private boolean scanning = false;
+ private boolean lookingForDevice = false;
+ private boolean connecting = false;
+ private boolean userButton = false;
+ private boolean unexpectedDisconnect = true;
+ private boolean batteryRequested = true;
+ private boolean disconnecting = false;
+ private boolean scanningAllowed = false;
+ public static boolean permissionGiven = false;
+ private boolean deviceFounded = false;
+ private static boolean authenticated = false;
+
+
+ //--- Constructor
+ public EmpaticaManager(Context context) {
+ super(context, TAG);
+
+ // Create a new EmpaDeviceManager. this will be both its data and status delegate.
+ empaManager = new EmpaDeviceManager(managerContext, this, this);
+
+ }
+
+ public static void deAuthenticate() {
+ authenticated = false;
+ }
+
+ private void authenticate() {
+ if (!authenticated){
+ if (ServiceManager.EMPATICA_API_KEY == null || ServiceManager.EMPATICA_API_KEY.isEmpty())
+ ServiceManager.notifyMissingKey();
+ else {
+ // Initialize the Device Manager using your API key. You need to have Internet access at this point.
+ empaManager.authenticateWithAPIKey(ServiceManager.EMPATICA_API_KEY);
+ authenticated = true;
+ }
+ }
+ }
+
+ //--- Functions to handle events from update status
+ private void connected() {
+ Log.d(TAG, "Device Connected");
+
+ //Stop searching for connection
+ connecting = false;
+ stopScan();
+ empatica.setConnected(true);
+
+ for (Device device : getScannedDevices()) {
+ if (empatica.getId().equals(device.getId())) {
+ device.setConnected(true);
+ break;
+ }
+ }
+
+ addConnectedDevice(empatica);
+
+ // Insert into file memory connected device
+ //saveConnectedFile();
+
+ // Notify the Service Manager that there is a new connection
+ ServiceManager.startActionConnected(managerContext);
+
+ batteryRequested = true;
+
+ }
+
+ private void disconnected() {
+ connecting = false;
+ if (empatica != null) {
+ for (Device device : getConnectedDevice()) {
+ if (device.getId().equals(empatica.getId())) {
+ device.setConnected(false);
+ device.setScanned(false);
+ break;
+ }
+ }
+ for (Device device : getScannedDevices()) {
+ if (device.getId().equals(empatica.getId())) {
+ device.setConnected(false);
+ device.setScanned(false);
+ break;
+ }
+ }
+ deleteDisconnectedDevice();
+ disconnecting = false;
+ ServiceManager.startActionDisconnectEmpatica(managerContext, unexpectedDisconnect);
+ Log.d(TAG, "Device has been disconnected, unexpected: " + unexpectedDisconnect);
+
+ if (ServiceManager.saving())
+ ServiceManager.startActionStopOneSave(managerContext, empatica.getId(), empatica.getName());
+ if (unexpectedDisconnect) {
+ try {
+ // Connect
+ empaManager.connectDevice(bluetoothDevice_);
+ boolean newDevice = addScannedDevice(empatica);
+ if (newDevice) {
+ // Notify the Service Manager that there is a new device.
+ ServiceManager.startActionScanned(managerContext);
+ }
+ Log.d(TAG, "I'm trying to reconnect since I have experienced an unexpected disconnection");
+
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ } else {
+ deviceFounded = false;
+ scanningAllowed = true;
+ }
+ unexpectedDisconnect = true;
+ }
+
+ }
+ //--- Interface Manager
+ @Override
+ public void scan() {
+ authenticate();
+ if (authenticated) {
+ lookingForDevice = true;
+ if (scanningAllowed && permissionGiven) {
+ // Start the scanning process
+ try {
+ empaManager.startScanning();
+ scanning = true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ scanning = false;
+ }
+
+ } else if (deviceFounded) {
+ Log.d(TAG, "I have already founded the device");
+ // Add it to scanned devices
+ boolean newDevice = addScannedDevice(empatica);
+ if (newDevice) {
+ // Notify the Service Manager that there is a new device.
+ ServiceManager.startActionScanned(managerContext);
+ }
+
+ } else {
+
+ Log.d(TAG, "Scanning not allowed");
+ }
+ }
+ }
+
+ @Override
+ public void isDevice(String name, String address) {
+
+ }
+
+ @Override
+ public void stopScan() {
+ scanningAllowed = false;
+ if (scanning) {
+ // Stop the process
+ empaManager.stopScanning();
+ // Put flags false
+ scanning = false;
+ Log.d(TAG, "Scan stopped");
+ } else {
+ Log.d(TAG, "Nothing to stop.");
+ }
+ }
+
+ @Override
+ public void connect(String id) {
+ if (deviceFounded){
+ try {
+ // Connect
+ empaManager.connectDevice(bluetoothDevice_);
+ } catch (Exception e){
+ Log.d(TAG, "Connection failed.");
+
+ }
+
+ } else if (!connecting) {
+ // Inform that we have to connect.
+ connecting = true;
+ Log.d(TAG, "Connect called.");
+ scan();
+ }
+
+ }
+
+ @Override
+ public void disconnect(String id) {
+ connecting = false;
+ unexpectedDisconnect = false;
+ this.stopScan();
+ scanningAllowed = true;
+ if (empaManager != null && empatica != null && empatica.isConnected() && !disconnecting) {
+ disconnecting = true;
+ try {
+ this.empaManager.disconnect();
+ }
+ catch (Exception e){
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public void onActivityResult(String requestCode, int resultCode) {
+ }
+
+ @Override
+ public void readBatteryLevel(String id) {
+ this.batteryRequested = true;
+ }
+
+ //--- Empatica Status
+ @Override
+ public void didUpdateStatus(EmpaStatus actualStatus) {
+
+ Log.d(TAG, "Update status: " + actualStatus);
+
+ if (oldStatus == null || !oldStatus.equals(actualStatus)) {
+ oldStatus = actualStatus;
+ if (actualStatus.equals(EmpaStatus.CONNECTED)) {
+ connected();
+ unexpectedDisconnect = true;
+ } else if (actualStatus.equals(EmpaStatus.READY)) {
+ scanningAllowed = true;
+ if (!scanning) {
+ scan();
+ }
+ } else if (actualStatus.equals(EmpaStatus.DISCONNECTED)) {
+ disconnected();
+ }
+ }
+ }
+
+ @Override
+ public void didEstablishConnection() {
+
+ }
+
+ @Override
+ public void didUpdateSensorStatus(int status, EmpaSensorType type) {
+
+ }
+
+ @Override
+ public void didDiscoverDevice(EmpaticaDevice bluetoothDevice, String deviceLabel, int rssi, boolean allowed) {
+ // Check if the discovered device can be used with your API key. If allowed is always false,
+ // the device is not linked with your API key. Please check your developer area at
+ // https://www.empatica.com/connect/developer.php
+ if (allowed) {
+ if (connecting) {
+ Log.d(TAG, "I'm trying to connect");
+ for (Device device : getScannedDevices()) {
+ if (!device.isConnected()) {
+ if (bluetoothDevice.hardwareId.equals(device.getId())) {
+ try {
+ bluetoothDevice_ = bluetoothDevice;
+ deviceFounded = true;
+ // Connect
+ empaManager.connectDevice(bluetoothDevice_);
+ connecting = false;
+ } catch (ConnectionNotAllowedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ } else if (lookingForDevice) {
+ lookingForDevice = false;
+ // Create Empatica object
+ empatica = new Empatica(managerContext);
+ empatica.setId(bluetoothDevice.hardwareId);
+ empatica.setName(bluetoothDevice.name);
+ // Add it to scanned devices
+ boolean newDevice = addScannedDevice(empatica);
+ if (newDevice) {
+ // Notify the Service Manager that there is a new device.
+ ServiceManager.startActionScanned(managerContext);
+ }
+
+ }
+ } else {
+ Log.d(TAG, "Device not allowed to be used with this API Key");
+ ServiceManager.notifyWrongKey();
+ }
+ }
+
+
+ @Override
+ public void didRequestEnableBluetooth() {
+ }
+
+ @Override
+ public void didUpdateOnWristStatus(int status) {
+ //TODO: If bracelet is on or not.
+ }
+
+ //--- Empatica Data Delegate
+ @Override
+ public void didReceiveGSR(float gsr, double timestamp) {
+ for (Device device : getConnectedDevice()) {
+ if (device.isConnected()) {
+ SensorDevice EDA = device.getSensor(Empatica.EDA);
+ EDA.getData(ElectroDermalActivity.EDA).set(gsr);
+ time.setTimeInMillis((long) timestamp * 1000L);
+ EDA.getTimeStamp().set(time);
+ ServiceManager.save(device.getId());
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void didReceiveBVP(float bvp, double timestamp) {
+ for (Device device : getConnectedDevice()) {
+ if (device.isConnected()) {
+ device.getSensor(Empatica.PPG).getData(BloodVolumePulse.PPG).set(bvp);
+ time.setTimeInMillis((long) timestamp * 1000L);
+ device.getSensor(Empatica.PPG).getTimeStamp().set(time);
+ //Get inter peak intervals
+ ((SignalFeatures) device.getSensor(Empatica.IPI)).insertData(bvp);
+ ServiceManager.save(device.getId());
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void didReceiveIBI(float ibi, double timestamp) {
+ for (Device device : getConnectedDevice()) {
+ if (device.isConnected()) {
+ device.getSensor(Empatica.IBI).getData(InterBeatInterval.IBI).set(ibi);
+ time.setTimeInMillis((long) timestamp * 1000L);
+ device.getSensor(Empatica.IBI).getTimeStamp().set(time);
+ ServiceManager.save(device.getId());
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void didReceiveTemperature(float t, double timestamp) {
+ for (Device device : getConnectedDevice()) {
+ if (device.isConnected()) {
+ device.getSensor(Empatica.TEMPERATURE).getData(Temperature.TEMP).set(t);
+ time.setTimeInMillis((long) timestamp * 1000L);
+ device.getSensor(Empatica.TEMPERATURE).getTimeStamp().set(time);
+ ServiceManager.save(device.getId());
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void didReceiveAcceleration(int x, int y, int z, double timestamp) {
+ for (Device device : getConnectedDevice()) {
+ if (device.isConnected()) {
+ SensorDevice accelerometer = device.getSensor(Empatica.ACCELEROMETER);
+ accelerometer.getData(Accelerometer.ACC_X).set((double) x);
+ accelerometer.getData(Accelerometer.ACC_Y).set((double) y);
+ accelerometer.getData(Accelerometer.ACC_Z).set((double) z);
+ time.setTimeInMillis((long) (timestamp * (double) 1000));
+ accelerometer.getTimeStamp().set(time);
+ ServiceManager.save(device.getId());
+ break;
+ }
+ }
+
+ }
+
+
+ @Override
+ public void didReceiveBatteryLevel(float level, double timestamp) {
+ float batteryLevel = level * 100;
+ Log.d(TAG, "BATTERY LEVEL: " + batteryLevel);
+ for (Device cd : getConnectedDevice()) {
+ if (cd.getId().equals(empatica.getId())) {
+ cd.setBatteryLevel((int) batteryLevel);
+ break;
+ }
+ }
+ if (batteryRequested) {
+ ServiceManager.startActionBatteryRead(managerContext);
+ batteryRequested = false;
+ }
+ }
+
+ @Override
+ public void didReceiveTag(double timestamp) {
+ Log.d(TAG, "Button pressed");
+
+ if (userButton) {
+ userButton = false;
+ ServiceManager.startActionNotify(managerContext, managerContext.getResources()
+ .getString(R.string.notif_no_button_empatica), ServiceManager.CHANNEL_DEFAULT);
+ } else {
+ userButton = true;
+ ServiceManager.startActionNotify(managerContext, managerContext.getResources()
+ .getString(R.string.notif_button_empatica), ServiceManager.CHANNEL_DEFAULT);
+ }
+
+ for (Device device : getConnectedDevice()) {
+ if (device.isConnected()) {
+ if (userButton) {
+ device.getSensor(Empatica.BUTTON).getData(UserButton.BUTTON).set(1.0);
+ } else {
+ device.getSensor(Empatica.BUTTON).getData(UserButton.BUTTON).set(0.0);
+ }
+ time.setTimeInMillis((long) timestamp * 1000L);
+ device.getSensor(Empatica.BUTTON).getTimeStamp().set(time);
+ ServiceManager.save(device.getId());
+ break;
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/Sensors/Accelerometer.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/Sensors/Accelerometer.java
new file mode 100644
index 0000000..bba9e75
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/Sensors/Accelerometer.java
@@ -0,0 +1,43 @@
+package ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Sensors;
+
+import android.content.Context;
+
+import ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Empatica;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceData;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceSensor;
+import ch.epfl.esl.elevatedmonitor.R;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Data;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.TimeStamp;
+
+public class Accelerometer extends SensorDevice implements InterfaceSensor {
+
+ public final static int ACC_X = 0;
+ public final static int ACC_Y = 1;
+ public final static int ACC_Z = 2;
+
+ private class AccData extends Data implements InterfaceData {
+ }
+
+ private class Time extends TimeStamp {
+ }
+
+ public Accelerometer(Context context) {
+ super(context);
+ addData(new AccData()); //X
+ addData(new AccData()); //Y
+ addData(new AccData()); //Z
+ addTime(new Time());
+
+ }
+
+ @Override
+ public void setId() {
+ this.id = Empatica.ACCELEROMETER;
+ }
+
+ @Override
+ public void setName() {
+ this.name = sensorContext.getResources().getString(R.string.accelerometer_empatica);
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/Sensors/BloodVolumePulse.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/Sensors/BloodVolumePulse.java
new file mode 100644
index 0000000..215a323
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/Sensors/BloodVolumePulse.java
@@ -0,0 +1,39 @@
+package ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Sensors;
+
+import android.content.Context;
+
+import ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Empatica;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceData;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceSensor;
+import ch.epfl.esl.elevatedmonitor.R;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Data;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.TimeStamp;
+
+public class BloodVolumePulse extends SensorDevice implements InterfaceSensor {
+
+ public static final int PPG = 0;
+
+ private class PPGData extends Data implements InterfaceData {
+ }
+
+ private class Time extends TimeStamp {
+
+ }
+
+ public BloodVolumePulse(Context context) {
+ super(context);
+ addData(new PPGData());
+ addTime(new Time());
+ }
+
+ @Override
+ public void setId() {
+ this.id = Empatica.PPG;
+ }
+
+ @Override
+ public void setName() {
+ this.name = sensorContext.getResources().getString(R.string.PPG_empatica);
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/Sensors/ElectroDermalActivity.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/Sensors/ElectroDermalActivity.java
new file mode 100644
index 0000000..e9d57a7
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/Sensors/ElectroDermalActivity.java
@@ -0,0 +1,38 @@
+package ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Sensors;
+
+import android.content.Context;
+
+import ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Empatica;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceData;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceSensor;
+import ch.epfl.esl.elevatedmonitor.R;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Data;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.TimeStamp;
+
+public class ElectroDermalActivity extends SensorDevice implements InterfaceSensor {
+ public static final int EDA = 0;
+
+ private class EDAData extends Data implements InterfaceData {
+ }
+
+ private class Time extends TimeStamp {
+ // Ciao mamma guarda come mi diverto
+ }
+
+ public ElectroDermalActivity(Context context) {
+ super(context);
+ addData(new EDAData());
+ addTime(new Time());
+ }
+
+ @Override
+ public void setId() {
+ this.id = Empatica.EDA;
+ }
+
+ @Override
+ public void setName() {
+ this.name = sensorContext.getResources().getString(R.string.EDA_empatica);
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/Sensors/InterBeatInterval.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/Sensors/InterBeatInterval.java
new file mode 100644
index 0000000..48ba937
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/Sensors/InterBeatInterval.java
@@ -0,0 +1,42 @@
+package ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Sensors;
+
+import android.content.Context;
+
+import ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Empatica;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceData;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceSensor;
+import ch.epfl.esl.elevatedmonitor.R;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Data;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.TimeStamp;
+
+public class InterBeatInterval extends SensorDevice implements InterfaceSensor {
+ public static final int IBI = 0;
+
+ private class IBIData extends Data implements InterfaceData {
+ }
+
+ private class Time extends TimeStamp {
+ }
+
+ public InterBeatInterval(Context context) {
+ super(context);
+ addData(new InterBeatInterval.IBIData());
+ addTime(new Time());
+ }
+
+ @Override
+ public void setId() {
+ this.id = Empatica.IBI;
+ }
+
+ @Override
+ public int getId() {
+ return this.id;
+ }
+
+ @Override
+ public void setName() {
+ this.name = sensorContext.getResources().getString(R.string.IBI_empatica);
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/Sensors/Temperature.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/Sensors/Temperature.java
new file mode 100644
index 0000000..8bf65dc
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/Sensors/Temperature.java
@@ -0,0 +1,42 @@
+package ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Sensors;
+
+import android.content.Context;
+
+import ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Empatica;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceData;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceSensor;
+import ch.epfl.esl.elevatedmonitor.R;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Data;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.TimeStamp;
+
+public class Temperature extends SensorDevice implements InterfaceSensor {
+ public static final int TEMP = 0;
+
+ private class TemperatureData extends Data implements InterfaceData {
+ }
+
+ public Temperature(Context context) {
+ super(context);
+ addData(new TemperatureData());
+ addTime(new Time());
+ }
+
+ private class Time extends TimeStamp {
+ }
+
+ @Override
+ public void setId() {
+ this.id = Empatica.TEMPERATURE;
+ }
+
+ @Override
+ public int getId() {
+ return this.id;
+ }
+
+ @Override
+ public void setName() {
+ this.name = sensorContext.getResources().getString(R.string.temperature_empatica);
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/Sensors/UserButton.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/Sensors/UserButton.java
new file mode 100644
index 0000000..c5ff94b
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Empatica_E4/Sensors/UserButton.java
@@ -0,0 +1,43 @@
+package ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Sensors;
+
+import android.content.Context;
+
+import ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Empatica;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceData;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceSensor;
+import ch.epfl.esl.elevatedmonitor.R;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Data;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.TimeStamp;
+
+public class UserButton extends SensorDevice implements InterfaceSensor {
+ public static final int BUTTON = 0;
+
+ private class Time extends TimeStamp {
+
+ }
+
+ public UserButton(Context context) {
+ super(context);
+ addData(new ButtonData());
+ addTime(new Time());
+ }
+
+ @Override
+ public void setId() {
+ this.id = Empatica.BUTTON;
+ }
+
+ @Override
+ public void setName() {
+ this.name = sensorContext.getResources().getString(R.string.button_empatica);
+ }
+
+ @Override
+ public int getId() {
+ return this.id;
+ }
+
+ private class ButtonData extends Data implements InterfaceData {
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/MetaMotionR/MetaMotionManager.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/MetaMotionR/MetaMotionManager.java
new file mode 100644
index 0000000..4d84639
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/MetaMotionR/MetaMotionManager.java
@@ -0,0 +1,552 @@
+package ch.epfl.esl.elevatedmonitor.Devices.MetaMotionR;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.os.Build;
+import android.support.v4.util.Pair;
+import android.util.Log;
+
+import com.mbientlab.metawear.MetaWearBoard;
+import com.mbientlab.metawear.Route;
+import com.mbientlab.metawear.Subscriber;
+import com.mbientlab.metawear.data.Quaternion;
+import com.mbientlab.metawear.module.Settings;
+import com.mbientlab.metawear.module.Timer;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import bolts.Continuation;
+import bolts.Task;
+import ch.epfl.esl.elevatedmonitor.Devices.MetaMotionR.Sensors.SensorFusion;
+import ch.epfl.esl.elevatedmonitor.Devices.MetaMotionR.Sensors.Temperature;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceManager;
+import ch.epfl.esl.elevatedmonitor.ServiceManager;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Device;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Manager;
+
+
+public class MetaMotionManager extends Manager implements InterfaceManager{
+
+ private static final String TAG = "MetaWear Man SQUINCI";
+
+ //--------- Flags
+ private Boolean scanAllowed = true;
+
+ private ArrayList connectings = new ArrayList<>();
+ private ArrayList disconnectings = new ArrayList<>();
+
+ private ArrayList boards = new ArrayList<>();
+ private ArrayList metaWears = new ArrayList<>();
+ private ArrayList> scheduledTasks = new ArrayList<>();
+
+
+ public MetaMotionManager(Context context) {
+ super(context, TAG);
+ }
+
+ //--- Interface Manager
+ @Override
+ public void scan() {
+ if (scanAllowed) {
+ ServiceManager.scanBluetooth();
+ }
+ }
+
+ private class ConnBool {
+ private String id;
+ private Boolean bool;
+
+ ConnBool(String id, Boolean bool){
+ this.id = id;
+ this.bool = bool;
+ }
+
+ Boolean getBool(){
+ return bool;
+ }
+
+ void setBool(Boolean bool){
+ this.bool = bool;
+ }
+
+ public String getId(){
+ return id;
+ }
+ }
+
+ private class DisconBool {
+ private String id;
+ private Boolean bool;
+
+ DisconBool(String id, Boolean bool){
+ this.id = id;
+ this.bool = bool;
+ }
+
+ Boolean getBool(){
+ return bool;
+ }
+
+ void setBool(Boolean bool){
+ this.bool = bool;
+ }
+
+ public String getId(){
+ return id;
+ }
+ }
+ @Override
+ public void isDevice(String name, String address) {
+ boolean alreadyThere = false;
+ ArrayList deviceNames = new ArrayList<>(Arrays.asList("MetaWear 0",
+ "MetaWear 1",
+ "MetaWear 2"));
+
+ for (Device device : getScannedDevices()) {
+ if (device.getId().equals(address)) {
+ alreadyThere = true;
+ }
+ deviceNames.remove(device.getName());
+
+ }
+ if(!deviceNames.isEmpty() && !alreadyThere) {
+ if (name != null && name.equals(MetaWear.NAME)) {
+ Log.d(TAG, "found device: " + name + " " + address);
+
+ MetaWear metaWear = new MetaWear(managerContext, deviceNames.get(0), address);
+ Log.d(TAG, "Found this one: " + deviceNames.get(0) + ": " + address);
+ addScannedDevice(metaWear);
+ ServiceManager.startActionScanned(managerContext);
+ connectings.add(new ConnBool(address, false));
+ disconnectings.add(new DisconBool(address, false));
+ }
+ }
+ }
+
+ @Override
+ public void stopScan() {
+ scanAllowed = false;
+ ServiceManager.stopScanBluetooth();
+ //Stop the process
+ Log.d(TAG, "Scan stopped");
+
+ }
+
+ private void connected(String deviceID) {
+ for (ConnBool conn : connectings){
+ if (conn.getId().equals(deviceID)){
+ conn.setBool(false);
+
+ }
+ }
+
+ boolean alreadyThere = false;
+ for (Device cd : getConnectedDevice()) {
+ if (cd.getId().equals(deviceID)) {
+ alreadyThere = true;
+ }
+ }
+ if(!alreadyThere) {
+ for (Device device : getScannedDevices()) {
+ if (deviceID.equals(device.getId())) {
+ for (MetaWearBoard board : boards) {
+ if (board.getMacAddress().equals(deviceID)) {
+ stopScan();
+ device.setConnected(true);
+ MetaWear metaWear = new MetaWear(managerContext, device.getName(), device.getId(), board);
+ metaWear.setConnected(true);
+ metaWears.add(metaWear);
+
+ addConnectedDevice(metaWear);
+ //Insert into file memory connected device
+ // saveConnectedFile();
+
+ Log.d(TAG, "Device Connected");
+
+ // Notify the Service Manager that there is a new connection
+ ServiceManager.startActionConnected(managerContext);
+
+ startStreaming(deviceID);
+
+ configureBatteryRoute(deviceID);
+ break;
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+ private void configureBatteryRoute(String id){
+ for (MetaWear metaWear : metaWears) {
+ if (metaWear.getId().equals(id)) {
+ Settings settings = metaWear.getSettings();
+ settings.battery().addRouteAsync(source -> source.limit(1).stream((Subscriber) (data, env) -> {
+ for (Device cd : getConnectedDevice()) {
+ if (cd.getId().equals(id)) {
+ cd.setBatteryLevel((int) data.value(Settings.BatteryState.class).charge);
+ break;
+ }
+ }
+ ServiceManager.startActionBatteryRead(managerContext);
+ Log.d(TAG, "battery state = " + data.value(Settings.BatteryState.class));
+ })).continueWith((Continuation) task -> {
+ settings.battery().read();
+ return null;
+ });
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void readBatteryLevel(String id) {
+ for (MetaWear metaWear : metaWears) {
+ if (metaWear.getId().equals(id)) {
+ Settings settings = metaWear.getSettings();
+ settings.battery().read();
+ break;
+ }
+ }
+ }
+
+ private void startStreaming(String id){
+ for (MetaWear metaWear : metaWears) {
+ if (metaWear.getId().equals(id)) {
+ com.mbientlab.metawear.module.SensorFusionBosch sensorFusionBosch = metaWear.getSensorFusion();
+ sensorFusionBosch.quaternion().addRouteAsync(source -> source.limit(33).stream((Subscriber) (data, env) -> {
+ for (Device device : getConnectedDevice()) {
+ if (id.equals(device.getId())) {
+ device.getSensor(MetaWear.SENSOR_FUSION)
+ .getData(SensorFusion.W)
+ .set(data.value(Quaternion.class).w());
+ device.getSensor(MetaWear.SENSOR_FUSION)
+ .getData(SensorFusion.X)
+ .set(data.value(Quaternion.class).x());
+ device.getSensor(MetaWear.SENSOR_FUSION)
+ .getData(SensorFusion.Y)
+ .set(data.value(Quaternion.class).y());
+ device.getSensor(MetaWear.SENSOR_FUSION)
+ .getData(SensorFusion.Z)
+ .set(data.value(Quaternion.class).z());
+ device.getSensor(MetaWear.SENSOR_FUSION)
+ .getTimeStamp()
+ .set(data.timestamp());
+ ServiceManager.save(device.getId());
+ // Log.d("TIME", data.formattedTimestamp());
+ }
+ }
+ })).continueWith((Continuation) ignored -> {
+ sensorFusionBosch.quaternion().start();
+ sensorFusionBosch.start();
+ return null;
+ });
+ }
+ }
+ }
+
+ private void scheduleTask(String id){
+ for (MetaWear metaWear : metaWears) {
+ if (metaWear.getId().equals(id)) {
+ metaWear.getTimer().scheduleAsync(60000, false, () ->
+ metaWear.getTemperatureSensor().read()).continueWith(task -> {
+ Timer.ScheduledTask scheduledTask;
+ scheduledTask = task.getResult();
+ scheduledTask.start();
+ scheduledTasks.add(new Pair<>(id,scheduledTask));
+ return null;
+ });
+ }
+ }
+
+ }
+
+ private void setTimer(String id){
+ for (MetaWear metaWear : metaWears) {
+ if (metaWear.getId().equals(id)) {
+ metaWear.getTemperatureSensor().addRouteAsync(source -> source.limit(1).stream((Subscriber) (data, env) -> {
+ Log.i(TAG, metaWear.getName() + " Temperature (C) = " + data.value(Float.class));
+ for (Device device : getConnectedDevice()) {
+ if (id.equals(device.getId())) {
+ device.getSensor(MetaWear.TEMPERATURE)
+ .getData(Temperature.TEMP)
+ .set(data.value(Float.class));
+ device.getSensor(MetaWear.TEMPERATURE)
+ .getTimeStamp()
+ .set(data.timestamp());
+ ServiceManager.save(device.getId());
+ break;
+ }
+ }
+ })).continueWith(task -> {
+ scheduleTask(id);
+ return null;
+ });
+ }
+ }
+ }
+
+ @Override
+ public void connect(String id) {
+ // Look if we are already connected to it
+ boolean connecting = false;
+ boolean alreadyThere = false;
+ for (Device cd : getConnectedDevice()) {
+ if (cd.getId().equals(id)) {
+ alreadyThere = true;
+ }
+ }
+ for (ConnBool conn : connectings){
+ if (conn.getId().equals(id)){
+ connecting = conn.getBool();
+ }
+ }
+
+ if(!alreadyThere && !connecting) {
+ // Inform that we have to connect.
+ for (ConnBool conn : connectings){
+ if (conn.getId().equals(id)){
+ conn.setBool(true);
+ }
+ }
+ for (Device device : getScannedDevices()) {
+ if (device.getId().equals(id)) {
+ // get BLE device
+ BluetoothDevice _device = ServiceManager.getBLEDevice(id);
+ if (ServiceManager.getMetawearBinder() != null) {
+ // Create a MetaWear board object for the Bluetooth Device
+ MetaWearBoard board;
+ board = ServiceManager.getMetawearBinder().getMetaWearBoard(_device);
+ boards.add(board);
+ board.onUnexpectedDisconnect(status -> {
+ Log.d(TAG, "Unexpected disconnection");
+ for (MetaWear metaWear : metaWears) {
+ if (metaWear.getId().equals(id)) {
+ if (board != null) {
+ for (Device cd : getConnectedDevice()) {
+ if (cd.getId().equals(metaWear.getId())) {
+ cd.setConnected(false);
+ cd.setScanned(false);
+ break;
+ }
+ }
+ for (Device sc : getScannedDevices()) {
+ if (sc.getId().equals(metaWear.getId())) {
+ sc.setConnected(false);
+ sc.setScanned(false);
+ break;
+ }
+ }
+ deleteDisconnectedDevice();
+ board.tearDown();
+ ServiceManager.startActionDisconnect(managerContext, metaWear.getName());
+ // If a saving has already been started, stop that saving
+ if (ServiceManager.saving()) {
+ ServiceManager.startActionStopOneSave(managerContext, metaWear.getId(), metaWear.getName());
+ }
+ board.connectAsync().continueWithTask(task -> {
+ if (task.isCancelled()) {
+ return task;
+ }
+ return task.isFaulted() ? reconnect(board) : task;
+ }).continueWith(task -> {
+ if (!task.isCancelled()) {
+ addScannedDevice(metaWear);
+ connected(id);
+ } else {
+ board.tearDown();
+ disconnected(id);
+ Log.d(TAG, "MetaWear Disconnected");
+ }
+ return null;
+ });
+
+ }
+ }
+ }
+ });
+ board.connectAsync().continueWithTask(task -> {
+ if (task.isCancelled()) {
+ return task;
+ }
+ return task.isFaulted() ? reconnect(board) : task;
+ }).continueWith(task -> {
+ if (!task.isCancelled()) {
+ board.getModule(Settings.class).editBleConnParams()
+ .maxConnectionInterval(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? 11.25f : 7.5f)
+ .commit();
+ connected(id);
+ setTimer(id);
+
+ }
+ return null;
+ });
+
+ } else {
+ ServiceManager.startActionScanned(managerContext);
+ Log.d(TAG, "I have to notify another action scan because I was not ready to connect yet, the service has not given me the reference to the board");
+ for (ConnBool conn : connectings){
+ if (conn.getId().equals(id)){
+ conn.setBool(false);
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ private void disconnected(String id){
+ for (MetaWear metaWear : metaWears) {
+ if (metaWear.getId().equals(id)) {
+
+ for (Device device : getConnectedDevice()) {
+ if (device.getId().equals(id)) {
+ device.setConnected(false);
+ device.setScanned(false);
+ break;
+ }
+ }
+ for (Device device : getScannedDevices()) {
+ if (device.getId().equals(id)) {
+ device.setConnected(false);
+ device.setScanned(false);
+ break;
+ }
+ }
+ ServiceManager.stopScanBluetooth();
+ for (DisconBool discon : disconnectings){
+ if (discon.getId().equals(id)){
+ discon.setBool(false);
+ }
+ }
+ deleteDisconnectedDevice();
+ // If a saving has already been started, stop that saving
+ if (ServiceManager.saving()) {
+ ServiceManager.startActionStopOneSave(managerContext, metaWear.getId(), metaWear.getName());
+ }
+ ServiceManager.startActionDisconnect(managerContext, metaWear.getName());
+ scanAllowed = true;
+
+
+ }
+ }
+
+ }
+
+ @Override
+ public void disconnect(String id) {
+ boolean disconnecting = false;
+ for (DisconBool discon : disconnectings){
+ if (discon.getId().equals(id)){
+ disconnecting = discon.getBool();
+ }
+ }
+ for (MetaWear metaWear : metaWears) {
+ if (metaWear.getId().equals(id)) {
+ for (MetaWearBoard board : boards) {
+ if (board.getMacAddress().equals(id)) {
+
+ if (metaWear.isConnected() && !disconnecting) {
+ for (DisconBool discon : disconnectings){
+ if (discon.getId().equals(id)){
+ discon.setBool(true);
+ }
+ }
+ metaWear.getSensorFusion().stop();
+ metaWear.getSensorFusion().quaternion().stop();
+ ArrayList> iterScheduledTasks = new ArrayList<>(scheduledTasks);
+ for (Pair pair : iterScheduledTasks){
+ if (pair.first != null && pair.first.equals(id)){
+ if (pair.second != null){
+ pair.second.stop();
+ pair.second.remove();
+ }
+ }
+ }
+ board.tearDown();
+ // When you are done using your board, call tearDown to remove resources allocated by the firmware and API such as routes,
+ // loggers, and data processors. this method does not reset the board so any configuration changes are preserved.
+ board.disconnectAsync().continueWithTask(task -> task.isCancelled() || !task.isFaulted() ? task : disconnect(board))
+ .continueWith((Continuation) task -> {
+ if (!task.isCancelled()) {
+ disconnected(id);
+ removeResources(id);
+ Log.d(TAG, "MetaWear Disconnected");
+ }
+ return null;
+ });
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ }
+
+ private void removeResources(String id) {
+ ArrayList iterConnectings = new ArrayList<>(connectings);
+ ArrayList iterDisconnectings = new ArrayList<>(disconnectings);
+ ArrayList iterBoards = new ArrayList<>(boards);
+ ArrayList iterMetaWears = new ArrayList<>(metaWears);
+ ArrayList> iterscheduledTasks = new ArrayList<>(scheduledTasks);
+
+ for (MetaWearBoard board : iterBoards) {
+ if (board.getMacAddress().equals(id)){
+ boards.remove(board);
+ }
+ }
+ for (MetaWear metaWear : iterMetaWears) {
+ if (metaWear.getId().equals(id)){
+ metaWears.remove(metaWear);
+ }
+ }
+ for (Pair pair : iterscheduledTasks) {
+ if (pair.first != null && pair.first.equals(id)){
+ scheduledTasks.remove(pair);
+ }
+ }
+ for (ConnBool connBool : iterConnectings){
+ if (connBool.getId().equals(id)){
+ connectings.remove(connBool);
+ }
+ }
+ for (DisconBool disconBool : iterDisconnectings){
+ if (disconBool.getId().equals(id)){
+ disconnectings.remove(disconBool);
+ }
+ }
+
+ }
+
+ @Override
+ public void onActivityResult(String requestCode, int resultCode) {
+
+ }
+
+ private static Task disconnect(final MetaWearBoard board) {
+ Log.d(TAG, "I'm trying to disconnect");
+ return board.disconnectAsync().continueWithTask(task -> task.isFaulted() ? disconnect(board) : task);
+ }
+
+
+ private static Task reconnect(final MetaWearBoard board) {
+
+ return board.connectAsync()
+ .continueWithTask(task -> {
+ if (task.isFaulted()) {
+ Log.d(TAG, "I'm trying to reconnect, task faulted");
+ return reconnect(board);
+ } else if (task.isCancelled()) {
+ Log.d(TAG, "Task cancelled.");
+ return task;
+ }
+ return Task.forResult(null);
+ });
+ }
+
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/MetaMotionR/MetaWear.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/MetaMotionR/MetaWear.java
new file mode 100644
index 0000000..0a7c2b9
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/MetaMotionR/MetaWear.java
@@ -0,0 +1,136 @@
+package ch.epfl.esl.elevatedmonitor.Devices.MetaMotionR;
+
+import android.content.Context;
+
+import com.mbientlab.metawear.MetaWearBoard;
+import com.mbientlab.metawear.module.SensorFusionBosch;
+import com.mbientlab.metawear.module.Settings;
+import com.mbientlab.metawear.module.Temperature;
+import com.mbientlab.metawear.module.Temperature.SensorType;
+import com.mbientlab.metawear.module.Timer;
+
+import java.util.ArrayList;
+
+import ch.epfl.esl.elevatedmonitor.Devices.MetaMotionR.Sensors.SensorFusion;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceDevice;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Device;
+
+
+
+
+public class MetaWear extends Device implements InterfaceDevice {
+ private static final String MANAGER_NAME = "ch.epfl.esl.elevatedmonitor.Devices.MetaMotionR.MetaMotionManager";
+ public static final int SENSOR_FUSION = 0;
+ public static final int TEMPERATURE = 1;
+ private com.mbientlab.metawear.module.SensorFusionBosch sensorFusionBosch;
+ private Temperature.Sensor tempSensor;
+ private Settings settings = null;
+ static final String NAME = "MetaWear";
+ public static Integer i = 0;
+ private Timer timer;
+
+
+ private boolean init = false;
+
+
+ MetaWear(Context context, String name, String id, MetaWearBoard board) {
+ super(context);
+ setSensors();
+ setName(name);
+ setId(id);
+ initSensors(board);
+ }
+
+
+ MetaWear(Context context, String name, String id) {
+ super(context);
+ setSensors();
+ setName(name);
+ setId(id);
+ }
+
+ public MetaWear(Context context) {
+ super(context);
+ setSensors();
+ setName("MetaWear " + i.toString());
+ setId("N.D.");
+ i++;
+ i = i%3;
+
+ }
+
+ private void initSensors(MetaWearBoard board){
+ // Get a reference to the sensors
+ sensorFusionBosch = board.getModule(SensorFusionBosch.class);
+ Temperature temperature = board.getModule(Temperature.class);
+ timer = board.getModule(Timer.class);
+ settings = board.getModule(Settings.class);
+
+ // Configure the sensors
+ sensorFusionBosch.configure()
+ .mode(SensorFusionBosch.Mode.NDOF) // Can choose between NDoF that calculates absolute orientation from accelerometer, gyro, and magnetometer
+ // Or IMUPlus that calculates relative orientation in space from accelerometer and gyro data
+ .accRange(SensorFusionBosch.AccRange.AR_2G)
+ .gyroRange(SensorFusionBosch.GyroRange.GR_250DPS)
+ .commit();
+ tempSensor = temperature.findSensors(SensorType.PRESET_THERMISTOR)[0];
+
+ init = true;
+
+ }
+
+ Temperature.Sensor getTemperatureSensor(){
+ if (init)
+ return tempSensor;
+ else
+ return null;
+ }
+
+ Timer getTimer(){
+ if (init)
+ return timer;
+ else
+ return null;
+ }
+
+ SensorFusionBosch getSensorFusion(){
+ if(init)
+ return sensorFusionBosch;
+ else
+ return null;
+ }
+
+ Settings getSettings(){
+ return settings;
+ }
+
+ @Override
+ public void setSensors() {
+ //--- Set different sensors
+ if (sensorArrayList == null) {
+ sensorArrayList = new ArrayList<>();
+ }
+ sensorArrayList.add(new SensorFusion(deviceContext));
+ sensorArrayList.add(new ch.epfl.esl.elevatedmonitor.Devices.MetaMotionR.Sensors.Temperature(deviceContext));
+
+ }
+
+
+ @Override
+ public void setName(String name) {
+ this.name = name;
+
+ }
+
+ @Override
+ public void setId(String id) {
+ this.id = id;
+
+ }
+
+ @Override
+ public void setManagerName() {
+ this.managerName = MANAGER_NAME;
+
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/MetaMotionR/Sensors/SensorFusion.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/MetaMotionR/Sensors/SensorFusion.java
new file mode 100644
index 0000000..e556961
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/MetaMotionR/Sensors/SensorFusion.java
@@ -0,0 +1,47 @@
+package ch.epfl.esl.elevatedmonitor.Devices.MetaMotionR.Sensors;
+
+import android.content.Context;
+
+import ch.epfl.esl.elevatedmonitor.Devices.MetaMotionR.MetaWear;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceData;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceSensor;
+import ch.epfl.esl.elevatedmonitor.R;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Data;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.TimeStamp;
+
+public class SensorFusion extends SensorDevice implements InterfaceSensor {
+
+ public final static int W = 0;
+ public final static int X = 1;
+ public final static int Y = 2;
+ public final static int Z = 3;
+
+
+ private class FusionData extends Data implements InterfaceData {
+
+ }
+
+ private class Time extends TimeStamp {
+ }
+
+ public SensorFusion(Context context) {
+ super(context);
+ addData(new FusionData()); //w
+ addData(new FusionData()); //x
+ addData(new FusionData()); //y
+ addData(new FusionData()); //z
+ addTime(new Time()); // Time stamp
+ }
+
+ @Override
+ public void setId() {
+ this.id = MetaWear.SENSOR_FUSION;
+ }
+
+ @Override
+ public void setName() {
+ this.name = sensorContext.getResources().getString(R.string.sensor_fusion_metawear);
+
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/MetaMotionR/Sensors/Temperature.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/MetaMotionR/Sensors/Temperature.java
new file mode 100644
index 0000000..4875a26
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/MetaMotionR/Sensors/Temperature.java
@@ -0,0 +1,39 @@
+package ch.epfl.esl.elevatedmonitor.Devices.MetaMotionR.Sensors;
+
+import android.content.Context;
+
+import ch.epfl.esl.elevatedmonitor.Devices.MetaMotionR.MetaWear;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceData;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceSensor;
+import ch.epfl.esl.elevatedmonitor.R;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Data;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.TimeStamp;
+
+public class Temperature extends SensorDevice implements InterfaceSensor {
+
+ public final static int TEMP = 0;
+
+ private class AccData extends Data implements InterfaceData {
+ }
+
+ private class Time extends TimeStamp {
+ }
+
+ public Temperature(Context context) {
+ super(context);
+ addData(new AccData());
+ addTime(new Time()); // Time stamp
+
+ }
+
+ @Override
+ public void setId() {
+ this.id = MetaWear.TEMPERATURE;
+ }
+
+ @Override
+ public void setName() {
+ this.name = sensorContext.getResources().getString(R.string.temperature_metamotion);
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Phone/Phone.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Phone/Phone.java
new file mode 100644
index 0000000..8a98a58
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Phone/Phone.java
@@ -0,0 +1,71 @@
+package ch.epfl.esl.elevatedmonitor.Devices.Phone;
+
+import android.content.Context;
+import android.os.Build;
+
+import java.util.ArrayList;
+
+import ch.epfl.esl.elevatedmonitor.Devices.Phone.Sensors.Accelerometer;
+import ch.epfl.esl.elevatedmonitor.Devices.Phone.Sensors.Light;
+import ch.epfl.esl.elevatedmonitor.Devices.Phone.Sensors.Proximity;
+import ch.epfl.esl.elevatedmonitor.Devices.Phone.Sensors.Temperature;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceDevice;
+import ch.epfl.esl.elevatedmonitor.R;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Device;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+
+public class Phone extends Device implements InterfaceDevice {
+ private static final String MANAGER_NAME = "ch.epfl.esl.elevatedmonitor.Devices.Phone.PhoneManager";
+
+ public static final int ACCELEROMETER = 0;
+ public static final int TEMPERATURE = 1;
+ public static final int PROXIMITY = 2;
+ public static final int LIGHT = 3;
+
+ //--- Constructor
+ public Phone(Context context) {
+ super(context);
+ setName(context.getResources().getString(R.string.my_phone));
+ setId(Build.MODEL);
+ setSensors();
+ }
+
+
+ @Override
+ public ArrayList getSensors() {
+ return sensorArrayList;
+ }
+
+ @Override
+ public SensorDevice getSensor(int position) {
+ return sensorArrayList.get(position);
+ }
+
+ @Override
+ public void setSensors() {
+ //--- Set the different sensors of the phone
+ //sensorArrayList.add(new Gyroscope(mContext));
+ if (sensorArrayList == null) {
+ sensorArrayList = new ArrayList<>();
+ }
+ sensorArrayList.add(new Accelerometer(deviceContext));
+ sensorArrayList.add(new Temperature(deviceContext));
+ sensorArrayList.add(new Proximity(deviceContext));
+ sensorArrayList.add(new Light(deviceContext));
+ }
+
+ @Override
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public void setManagerName() {
+ this.managerName = MANAGER_NAME;
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Phone/PhoneManager.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Phone/PhoneManager.java
new file mode 100644
index 0000000..96b4a1c
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Phone/PhoneManager.java
@@ -0,0 +1,222 @@
+package ch.epfl.esl.elevatedmonitor.Devices.Phone;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.BatteryManager;
+import android.os.Build;
+import android.util.Log;
+
+import java.util.Calendar;
+
+import ch.epfl.esl.elevatedmonitor.Devices.Phone.Sensors.Accelerometer;
+import ch.epfl.esl.elevatedmonitor.Devices.Phone.Sensors.Light;
+import ch.epfl.esl.elevatedmonitor.Devices.Phone.Sensors.Proximity;
+import ch.epfl.esl.elevatedmonitor.Devices.Phone.Sensors.Temperature;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceManager;
+import ch.epfl.esl.elevatedmonitor.ServiceManager;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Device;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Manager;
+
+import static android.content.Context.BATTERY_SERVICE;
+
+public class PhoneManager extends Manager implements InterfaceManager, SensorEventListener {
+
+
+ private SensorManager mSensorManager;
+
+ private static String TAG = "Phone Man SQUINCI";
+
+ private Phone myPhone = null;
+
+ //--- Constructor
+ public PhoneManager(Context context) {
+ super(context, TAG);
+ }
+
+
+ @Override
+ public void scan() {
+
+ boolean alreadyThere = false;
+ for (Device device : getScannedDevices()) {
+ if (device.getId().equals(Build.MODEL)) {
+ alreadyThere = true;
+ break;
+ }
+ }
+ if(!alreadyThere) {
+ myPhone = new Phone(managerContext);
+ addScannedDevice(myPhone);
+ ServiceManager.startActionScanned(managerContext);
+ Log.d(TAG, "Phone has been scanned");
+
+ }
+
+
+ }
+
+ @Override
+ public void isDevice(String name, String address) {
+
+ }
+
+ @Override
+ public void stopScan() {
+
+ }
+
+ @Override
+ public void connect(String id) {
+ boolean alreadyThere = false;
+ for (Device cd : getConnectedDevice()) {
+ if (cd.getId().equals(id)) {
+ alreadyThere = true;
+ break;
+ }
+ }
+ if(!alreadyThere) {
+ for (Device device : getScannedDevices()) {
+ if (device.getId().equals(id)) {
+ //--- Add connected device to list
+ myPhone.setConnected(true);
+ addConnectedDevice(myPhone);
+
+ //--- Set sensors
+ mSensorManager = (SensorManager) managerContext.getSystemService(Context.SENSOR_SERVICE);
+ if (mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION) != null) {
+ Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
+ mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
+ }
+ if (mSensorManager.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE) != null) {
+ Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE);
+ mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
+ }
+ if (mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT) != null) {
+ Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
+ mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
+ }
+ if (mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY) != null) {
+ Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
+ }
+
+ //--- save device
+ saveConnectedFile();
+
+ //--- Send info to UI
+ ServiceManager.startActionConnected(managerContext);
+
+ Log.d(TAG, "Phone has been connected");
+
+ readBatteryLevel(id);
+
+ }
+ }
+ }
+ }
+
+ @Override
+ public void disconnect(String id) {
+ if (mSensorManager != null && myPhone != null && myPhone.isConnected()) {
+ for (Device device : getConnectedDevice()){
+ if(device.getId().equals(myPhone.getId())){
+ device.setConnected(false);
+ device.setScanned(false);
+ break;
+ }
+ }
+ for (Device device : getScannedDevices()){
+ if(device.getId().equals(myPhone.getId())){
+ device.setConnected(false);
+ device.setScanned(false);
+ break;
+ }
+ }
+ mSensorManager.unregisterListener(this);
+ deleteDisconnectedDevice();
+ ServiceManager.startActionDisconnect(managerContext, myPhone.getName());
+ Log.d(TAG, "Phone has been disconnected");
+ // If a saving has already been started, stop that session
+ if (ServiceManager.saving()) {
+ ServiceManager.startActionStopOneSave(managerContext, myPhone.getId(), myPhone.getName());
+ }
+
+
+ }
+ }
+
+ @Override
+ public void onActivityResult(String requestCode, int resultCode) {
+
+ }
+
+ @Override
+ public void readBatteryLevel(String id) {
+ BatteryManager bm = (BatteryManager)managerContext.getSystemService(BATTERY_SERVICE);
+ int batLevel = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
+ for (Device cd : getConnectedDevice()) {
+ if (cd.getId().equals(id)) {
+ cd.setBatteryLevel(batLevel);
+ break;
+ }
+ }
+ ServiceManager.startActionBatteryRead(managerContext);
+ Log.d(TAG, "battery level = " + batLevel);
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ switch (event.sensor.getType()) {
+ case Sensor.TYPE_LINEAR_ACCELERATION:
+ for (Device device : getConnectedDevice()) {
+ if (device.isConnected()) {
+ device.getSensor(Phone.ACCELEROMETER).getData(Accelerometer.ACC_X).set(event.values[0]);
+ device.getSensor(Phone.ACCELEROMETER).getData(Accelerometer.ACC_Y).set(event.values[1]);
+ device.getSensor(Phone.ACCELEROMETER).getData(Accelerometer.ACC_Z).set(event.values[2]);
+ device.getSensor(Phone.ACCELEROMETER).getTimeStamp().set(Calendar.getInstance());
+ ServiceManager.save(device.getId());
+ break;
+ }
+ }
+ break;
+ case Sensor.TYPE_LIGHT:
+ for (Device device : getConnectedDevice()) {
+ if (device.isConnected()) {
+ device.getSensor(Phone.LIGHT).getData(Light.LIGHT).set(event.values[0]);
+ device.getSensor(Phone.LIGHT).getTimeStamp().set(Calendar.getInstance());
+ ServiceManager.save(device.getId());
+ break;
+ }
+ }
+ break;
+ case Sensor.TYPE_PROXIMITY:
+ for (Device device : getConnectedDevice()) {
+ if (device.isConnected()) {
+ device.getSensor(Phone.PROXIMITY).getData(Proximity.PROX).set(event.values[0]);
+ device.getSensor(Phone.PROXIMITY).getTimeStamp().set(Calendar.getInstance());
+ ServiceManager.save(device.getId());
+ break;
+ }
+ }
+ break;
+ case Sensor.TYPE_AMBIENT_TEMPERATURE:
+ for (Device device : getConnectedDevice()) {
+ if (device.isConnected()) {
+ device.getSensor(Phone.TEMPERATURE).getData(Temperature.TEMP).set(event.values[0]);
+ ServiceManager.save(device.getId());
+ break;
+ }
+ }
+ break;
+ default:
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Phone/Sensors/Accelerometer.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Phone/Sensors/Accelerometer.java
new file mode 100644
index 0000000..3b7d309
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Phone/Sensors/Accelerometer.java
@@ -0,0 +1,67 @@
+package ch.epfl.esl.elevatedmonitor.Devices.Phone.Sensors;
+
+import android.content.Context;
+
+import ch.epfl.esl.elevatedmonitor.Devices.Phone.Phone;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceData;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceSensor;
+import ch.epfl.esl.elevatedmonitor.R;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Data;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.TimeStamp;
+
+public class Accelerometer extends SensorDevice implements InterfaceSensor {
+ private final static int MICRO_SECONDS = 10000;
+ public final static int ACC_X = 0;
+ public final static int ACC_Y = 1;
+ public final static int ACC_Z = 2;
+
+ private class AccelerometerData extends Data implements InterfaceData {
+ }
+
+ private class Time extends TimeStamp {
+ }
+
+
+ //--- Constructor
+ public Accelerometer(Context context) {
+ super(context);
+
+ addData(new AccelerometerData()); // X
+ addData(new AccelerometerData()); // Y
+ addData(new AccelerometerData()); // Z
+ addTime(new Time()); // Time-stamp
+ }
+
+ /*
+ private class AccelerometerListener implements SensorEventListener{
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ data.get(ACC_X).set(event.values[ACC_X]);
+ data.get(ACC_Y).set(event.values[ACC_Y]);
+ data.get(ACC_Z).set(event.values[ACC_Z]);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+
+ }
+ }
+ */
+
+
+ @Override
+ public void setId() {
+ this.id = Phone.ACCELEROMETER;
+ }
+
+ @Override
+ public int getId() {
+ return this.id;
+ }
+
+ @Override
+ public void setName() {
+ this.name = sensorContext.getResources().getString(R.string.accelerometer_phone);
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Phone/Sensors/Light.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Phone/Sensors/Light.java
new file mode 100644
index 0000000..f87023b
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Phone/Sensors/Light.java
@@ -0,0 +1,46 @@
+package ch.epfl.esl.elevatedmonitor.Devices.Phone.Sensors;
+
+import android.content.Context;
+
+import ch.epfl.esl.elevatedmonitor.Devices.Phone.Phone;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceData;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceSensor;
+import ch.epfl.esl.elevatedmonitor.R;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Data;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.TimeStamp;
+
+public class Light extends SensorDevice implements InterfaceSensor {
+ private final static int MICRO_SECONDS = 10000;
+ public final static int LIGHT = 0;
+
+ private class LightData extends Data implements InterfaceData {
+ }
+
+ private class Time extends TimeStamp {
+ }
+
+ //--- Constructor
+ public Light(Context context) {
+ super(context);
+ addData(new Light.LightData());
+ addTime(new Time()); // Time-stamp
+ }
+
+ @Override
+ public void setId() {
+ this.id = Phone.LIGHT;
+ }
+
+ @Override
+ public int getId() {
+ return this.id;
+ }
+
+ @Override
+ public void setName() {
+ this.name = sensorContext.getResources().getString(R.string.light_phone);
+ }
+}
+
+
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Phone/Sensors/Proximity.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Phone/Sensors/Proximity.java
new file mode 100644
index 0000000..9a00a3c
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Phone/Sensors/Proximity.java
@@ -0,0 +1,45 @@
+package ch.epfl.esl.elevatedmonitor.Devices.Phone.Sensors;
+
+import android.content.Context;
+
+import ch.epfl.esl.elevatedmonitor.Devices.Phone.Phone;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceData;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceSensor;
+import ch.epfl.esl.elevatedmonitor.R;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Data;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.TimeStamp;
+
+public class Proximity extends SensorDevice implements InterfaceSensor {
+ private final static int MICRO_SECONDS = 10000;
+ public final static int PROX = 0;
+
+ private class ProximityData extends Data implements InterfaceData {
+ }
+
+ private class Time extends TimeStamp {
+ }
+
+ //--- Constructor
+ public Proximity(Context context) {
+ super(context);
+ addData(new Proximity.ProximityData());
+ addTime(new Time()); // Time-stamp
+ }
+
+ @Override
+ public void setId() {
+ this.id = Phone.PROXIMITY;
+ }
+
+ @Override
+ public int getId() {
+ return this.id;
+ }
+
+ @Override
+ public void setName() {
+ this.name = sensorContext.getResources().getString(R.string.proximity_phone);
+ }
+}
+
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Phone/Sensors/Temperature.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Phone/Sensors/Temperature.java
new file mode 100644
index 0000000..227a00f
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Devices/Phone/Sensors/Temperature.java
@@ -0,0 +1,41 @@
+package ch.epfl.esl.elevatedmonitor.Devices.Phone.Sensors;
+
+import android.content.Context;
+
+import ch.epfl.esl.elevatedmonitor.Devices.Phone.Phone;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceData;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceSensor;
+import ch.epfl.esl.elevatedmonitor.R;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Data;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+
+public class Temperature extends SensorDevice implements InterfaceSensor {
+ private final static int MICRO_SECONDS = 10000;
+ public final static int TEMP = 0;
+
+ private class TemperatureData extends Data implements InterfaceData {
+ }
+
+ //--- Constructor
+ public Temperature(Context context) {
+ super(context);
+ addData(new Temperature.TemperatureData());
+ }
+
+ @Override
+ public void setId() {
+ this.id = Phone.TEMPERATURE;
+ }
+
+ @Override
+ public int getId() {
+ return this.id;
+ }
+
+ @Override
+ public void setName() {
+ this.name = sensorContext.getResources().getString(R.string.temperature_phone);
+ }
+}
+
+
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/ExperimentActivity.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/ExperimentActivity.java
new file mode 100644
index 0000000..ae7289f
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/ExperimentActivity.java
@@ -0,0 +1,755 @@
+package ch.epfl.esl.elevatedmonitor;
+
+import android.graphics.Color;
+import android.media.MediaPlayer;
+import android.os.Bundle;
+import android.os.CountDownTimer;
+import android.os.Environment;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.text.Html;
+import android.view.View;
+import android.widget.Button;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.mikhaellopez.circularprogressbar.CircularProgressBar;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.Calendar;
+import java.util.Timer;
+import java.util.TimerTask;
+
+public class ExperimentActivity extends AppCompatActivity {
+ // private static final String TAG = "Experiment SQUINCI";
+ private TextView tvProgressLabel;
+ private TextView tvStressLabel;
+ private TextView tvPhaseLabel;
+ private TextView tvScrewRemaing;
+ private TextView tvNumber;
+ private CircularProgressBar circularProgressBar;
+ private TextView description;
+ private SeekBar seekBar;
+ private Button button;
+ private int phase = 1;
+ private String[] phaseOneText;
+ private String[] phaseTwoText;
+ private String[] phaseThreeText;
+ private String[] phaseFourText;
+ private String[] phaseFiveText;
+ private String[] phaseSixText;
+ private int[] minuends = {1547, 1530, 1513, 1496, 1479, 1462};
+ private int[] subtrahends = {17, 17, 17, 17, 17, 17};
+ private int[] minuends2 = {1022, 1009, 996, 983, 970, 957};
+ private int[] subtrahends2 = {13, 13, 13, 13, 13, 13};
+ private int index = 0;
+ private final int duration = 60; //60
+ private final int secScrew = 30; //30
+ private final int secScrewHard = 30; //30
+ private boolean stressInserted = false;
+ private String experimentName;
+ private Timer t;
+ private int screwRemaining = 6;
+ private MediaPlayer mediaPlayer;
+ private MediaPlayer alarm;
+ private int outOfTime = 0;
+ private int woodBlock = -1;
+ private CountDownTimer countDownTimer;
+ private int j = 0;
+ private boolean firstTime = true;
+ Long init;
+ Long end;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_experiment);
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ mediaPlayer = MediaPlayer.create(this, R.raw.relaxing);
+ alarm = MediaPlayer.create(this, R.raw.alarm);
+
+ phaseOneText = getResources().getStringArray(R.array.phase_one);
+ phaseTwoText = getResources().getStringArray(R.array.phase_two);
+ phaseThreeText = getResources().getStringArray(R.array.phase_three);
+ phaseFourText = getResources().getStringArray(R.array.phase_four);
+ phaseFiveText = getResources().getStringArray(R.array.phase_five);
+ phaseSixText = getResources().getStringArray(R.array.phase_six);
+ circularProgressBar = findViewById(R.id.circular_bar);
+ circularProgressBar.setProgress(100);
+ tvProgressLabel = findViewById(R.id.textView3);
+ tvStressLabel = findViewById(R.id.textView);
+ tvPhaseLabel = findViewById(R.id.textViewPhase);
+ tvScrewRemaing = findViewById(R.id.textViewScrew);
+ tvNumber = findViewById(R.id.editText);
+ tvNumber.setEnabled(false);
+ tvNumber.setHint("");
+ tvNumber.setBackgroundResource(android.R.color.transparent);
+ // set a change listener on the SeekBar
+ seekBar = findViewById(R.id.seekBar);
+ seekBar.setEnabled(false);
+ seekBar.setOnSeekBarChangeListener(seekBarChangeListener);
+ description = findViewById(R.id.textView2);
+ button = findViewById(R.id.button);
+ button.setOnClickListener(buttonListener);
+ circularProgressBar.setOnClickListener(buttonListener);
+ }
+
+ View.OnClickListener clickListener2 = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if ((!tvNumber.getText().toString().equals("")) && Integer.valueOf(tvNumber.getText().toString()) == (minuends2[j]-subtrahends2[j])) {
+ tvNumber.setText("");
+ j++;
+ countDownTimer.cancel();
+ if (alarm.isPlaying()) {
+ alarm.pause();
+ }
+ t.cancel();
+ runOnUiThread(() -> {
+ circularProgressBar.enableIndeterminateMode(false);
+ circularProgressBar.setColor(Color.BLUE);
+ circularProgressBar.setBackgroundColor(adjustAlpha(Color.BLUE));
+ circularProgressBar.setProgressWithAnimation(100, 10);
+ });
+
+ if (screwRemaining > 1) {
+ screwRemaining--;
+ tvScrewRemaing.setText(String.format("\n\n%s%s\n%s", getString(R.string.wood_block), String.valueOf((++woodBlock)%3 +1), "Now subtract 13\nfrom the previous result"));
+ countDownTimer.start();
+ } else {
+ description.setText(Html.fromHtml(phaseFourText[index++]));
+ seekBar.setEnabled(true);
+ phase++;
+ index = 0;
+ button.setEnabled(true);
+ tvScrewRemaing.setText(String.format("\n%s", getString(R.string.phase_four_completed)));
+ end = Calendar.getInstance().getTimeInMillis();
+ tvProgressLabel.setText("");
+ circularProgressBar.setOnClickListener(null);
+ tvNumber.setOnClickListener(null);
+ tvNumber.setHint("");
+ tvNumber.setEnabled(false);
+ tvNumber.setBackgroundResource(android.R.color.transparent);
+ }
+ } else if (!tvNumber.getText().toString().equals("")) {
+ Toast.makeText(getApplicationContext(), "The value you've entered is wrong! ", Toast.LENGTH_LONG).show();
+ tvScrewRemaing.setText(String.format("\n\n%s%s\n%s - %s = ", getString(R.string.wood_block), String.valueOf((woodBlock)%3 +1), String.valueOf(minuends2[j]), String.valueOf(subtrahends2[j])));
+ tvNumber.setText("");
+ countDownTimer.cancel();
+ t.cancel();
+ runOnUiThread(() -> {
+ circularProgressBar.setColor(Color.RED);
+ circularProgressBar.setBackgroundColor(adjustAlpha(Color.RED));
+ circularProgressBar.enableIndeterminateMode(true);
+ });
+ if (!alarm.isPlaying()) {
+ alarm.start();
+ }
+ outOfTime++;
+ t = new Timer();
+ //Set the schedule function and rate
+ t.scheduleAtFixedRate(new TimerTask() {
+ int milliElapsed = 0;
+
+ @Override
+ public void run() {
+ runOnUiThread(() -> tvProgressLabel.setText(MessageFormat.format("{0}{1}", getString(R.string.seconds_elapsed), milliElapsed / 1000)));
+ milliElapsed = milliElapsed + 100;
+ }
+ },
+ //Set how long before to start calling the TimerTask (in milliseconds)
+ 0,
+ //Set the amount of time between each execution (in milliseconds)
+ 100);
+
+ }
+ }
+ };
+
+ View.OnClickListener buttonListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ {
+ //90
+ int secRelax = 90;
+ switch (phase) {
+ case 1:
+ // Phase one
+ setExperimentHeader();
+ init = Calendar.getInstance().getTimeInMillis();
+ tvPhaseLabel.setText(getString(R.string.phase_one));
+ circularProgressBar.setOnClickListener(null);
+ button.setEnabled(false);
+ button.setText(getString(R.string.next));
+ mediaPlayer.start();
+ CharSequence styledText =Html.fromHtml(phaseOneText[index++]);
+ description.setText(styledText);
+ startTimer(duration, phaseOneText);
+ break;
+
+ case 2:
+ // Phase two
+ if (firstTime) {
+ if (!stressInserted) {
+ Toast.makeText(getApplicationContext(), "Please insert your stress level! ", Toast.LENGTH_LONG).show();
+ } else {
+ firstTime = false;
+ Integer stressLevel = seekBar.getProgress();
+ //String stressInfo = "The inserted stress Level is: " + stressLevel + "\n";
+ //saveDataExperiment(stressInfo);
+// String string = "Phase 1 completed, Phase 2 started at: " +
+// Calendar.getInstance().getTime().toString();
+ String string = "One " + init.toString() + " " + stressLevel.toString() + " " + end.toString() + "\n";
+ saveDataExperiment(string);
+ stressInserted = false;
+ tvPhaseLabel.setText(getString(R.string.phase_two));
+ seekBar.setEnabled(false);
+ tvStressLabel.setText("");
+ button.setEnabled(true);
+ button.setText(getString(R.string.start));
+ description.setText(Html.fromHtml(phaseTwoText[index++]));
+ countDownTimer = new CountDownTimer((long) duration * 4 * 1000, 1000) {
+ @Override
+ public void onTick(long millisUntilFinished) {
+ float seconds = (float) millisUntilFinished / 1000;
+ tvScrewRemaing.setText(MessageFormat.format("\n{0}{1}", getString(R.string.seconds_remaining), (int) seconds));
+ circularProgressBar.setProgressWithAnimation(seconds / (duration * 4) * 100, 1000);
+ }
+
+ @Override
+ public void onFinish() {
+ circularProgressBar.setProgressWithAnimation(0, 1000);
+ tvScrewRemaing.setText("");
+ tvProgressLabel.setText(Html.fromHtml(phaseTwoText[index]));
+ seekBar.setEnabled(true);
+ phase++;
+ index = 0;
+ button.setText(R.string.next);
+ button.setEnabled(true);
+ description.setText("");
+ end = Calendar.getInstance().getTimeInMillis();
+
+ }
+ };
+ }
+ }
+ else {
+ init = Calendar.getInstance().getTimeInMillis();
+ button.setEnabled(false);
+ tvProgressLabel.setText(Html.fromHtml(phaseTwoText[index++]));
+ countDownTimer.start();
+ }
+ break;
+
+ case 3:
+ // Phase three
+ if (!stressInserted) {
+ Toast.makeText(getApplicationContext(), "Please insert your stress level! ", Toast.LENGTH_LONG).show();
+ } else {
+ tvScrewRemaing.setText("");
+ stressInserted = false;
+ index = 0;
+ Integer stressLevel = seekBar.getProgress();
+ String strings = "Two " + init.toString() + " " + stressLevel.toString() + " " + end.toString() + "\n";
+ saveDataExperiment(strings);
+ tvPhaseLabel.setText(getString(R.string.phase_three));
+ seekBar.setEnabled(false);
+ tvStressLabel.setText("");
+ button.setEnabled(false);
+ button.setText(getString(R.string.next));
+ description.setText(Html.fromHtml(phaseThreeText[index++]));
+ init = Calendar.getInstance().getTimeInMillis();
+ startTimer(secRelax, phaseThreeText);
+ }
+ break;
+
+ case 4:
+ // Phase four
+ if (!stressInserted) {
+ Toast.makeText(getApplicationContext(), "Please insert your stress level! ", Toast.LENGTH_LONG).show();
+ } else {
+ if (mediaPlayer.isPlaying()) {
+ mediaPlayer.pause();
+ }
+ stressInserted = false;
+ index = 0;
+ Integer stressLevel = seekBar.getProgress();
+ String stressInfo = "Three " + init.toString() + " " + stressLevel.toString() + " " + end.toString() + "\n";
+ saveDataExperiment(stressInfo);
+ tvPhaseLabel.setText(getString(R.string.phase_four));
+ seekBar.setEnabled(false);
+ tvStressLabel.setText("");
+ button.setEnabled(true);
+ button.setText(getString(R.string.start));
+ description.setText(Html.fromHtml(phaseFourText[index++]));
+ phase++;
+ }
+ break;
+
+ case 5:
+ // Phase four
+ init = Calendar.getInstance().getTimeInMillis();
+ button.setEnabled(false);
+ button.setText(getString(R.string.next));
+ tvNumber.setHint("Insert solution");
+ tvNumber.setEnabled(true);
+ tvScrewRemaing.setText(String.format("\n\n%s%s\n%s - %s = ", getString(R.string.wood_block), String.valueOf((++woodBlock)%3+1), String.valueOf(minuends2[j]), String.valueOf(subtrahends2[j])));
+ t = new Timer();
+ countDownTimer = new CountDownTimer(secScrewHard * 1000, 100) {
+ // 500 means, onTick function will be called at every 500 milliseconds
+
+ @Override
+ public void onTick(long leftTimeInMilliseconds) {
+ float seconds = (float) leftTimeInMilliseconds / 1000;
+ tvProgressLabel.setText(MessageFormat.format("{0}{1}", getString(R.string.seconds_remaining), (int) seconds));
+ circularProgressBar.setProgressWithAnimation(seconds / secScrewHard * 100, 100);
+ if (seconds / secScrewHard * 100 < 20) {
+ circularProgressBar.setColor(Color.YELLOW);
+ circularProgressBar.setBackgroundColor(adjustAlpha(Color.YELLOW));
+ }
+ }
+
+ @Override
+ public void onFinish() {
+ runOnUiThread(() -> {
+ circularProgressBar.setColor(Color.RED);
+ circularProgressBar.setBackgroundColor(adjustAlpha(Color.RED));
+ circularProgressBar.enableIndeterminateMode(true);
+ });
+ alarm.start();
+ outOfTime++;
+ t = new Timer();
+ //Set the schedule function and rate
+ t.scheduleAtFixedRate(new TimerTask() {
+ int milliElapsed = 0;
+
+ @Override
+ public void run() {
+ runOnUiThread(() -> tvProgressLabel.setText(MessageFormat.format("{0}{1}", getString(R.string.seconds_elapsed), milliElapsed / 1000)));
+ milliElapsed = milliElapsed + 100;
+ }
+
+ },
+ //Set how long before to start calling the TimerTask (in milliseconds)
+ 0,
+ //Set the amount of time between each execution (in milliseconds)
+ 100);
+ }
+ };
+ countDownTimer.start();
+ circularProgressBar.setOnClickListener(clickListener2);
+ tvNumber.setOnClickListener(clickListener2);
+ break;
+
+ case 6:
+ // Phase five
+ if (!stressInserted) {
+ Toast.makeText(getApplicationContext(), "Please insert your stress level! ", Toast.LENGTH_LONG).show();
+ } else {
+ mediaPlayer.start();
+ tvScrewRemaing.setText("");
+ stressInserted = false;
+ index = 0;
+ Integer StressLevel = seekBar.getProgress();
+ String stressInfo = "Four " + init.toString() + " " + StressLevel.toString() + " " + end.toString() + "\n";
+ saveDataExperiment(stressInfo);
+ tvPhaseLabel.setText(getString(R.string.phase_five));
+ seekBar.setEnabled(false);
+ tvStressLabel.setText("");
+ button.setEnabled(false);
+ button.setText(getString(R.string.next));
+ description.setText(Html.fromHtml(phaseFiveText[index++]));
+ init = Calendar.getInstance().getTimeInMillis();
+ startTimer(secRelax, phaseFiveText);
+ }
+ break;
+
+ case 7:
+ // Phase six
+ if (!stressInserted) {
+ Toast.makeText(getApplicationContext(), "Please insert your stress level! ", Toast.LENGTH_LONG).show();
+ } else {
+ stressInserted = false;
+ index = 0;
+ screwRemaining = 6;
+ Integer stressLevel = seekBar.getProgress();
+// String stressInfo = "The inserted stress Level is: " + stressLevel + "\n";
+ String stressInfo = "Five " + init.toString() + " " + stressLevel.toString() + " " + end.toString() + "\n";
+ saveDataExperiment(stressInfo);
+ tvPhaseLabel.setText(getString(R.string.phase_six));
+ seekBar.setEnabled(false);
+ tvStressLabel.setText("");
+ tvProgressLabel.setText("");
+ button.setText(getString(R.string.start));
+ description.setText(Html.fromHtml(phaseSixText[index++]));
+ phase++;
+ }
+ break;
+
+ case 8:
+ // Phase six
+ init = Calendar.getInstance().getTimeInMillis();
+// String string = "Phase 3 completed, Phase 4 started at: " +
+// Calendar.getInstance().getTime().toString();
+ if (mediaPlayer.isPlaying()) {
+ mediaPlayer.stop();
+ mediaPlayer.release();
+ }
+ // saveDataExperiment(string);
+ tvNumber.setHint("Insert solution");
+ tvNumber.setEnabled(true);
+ circularProgressBar.setColor(Color.BLUE);
+ circularProgressBar.setBackgroundColor(adjustAlpha(Color.BLUE));
+ button.setEnabled(false);
+ button.setText(getString(R.string.next));
+ j = 0;
+ tvScrewRemaing.setText(String.format("%s%s\n%s - %s = ", getString(R.string.remaining_screw), String.valueOf(screwRemaining), String.valueOf(minuends[j]), String.valueOf(subtrahends[j])));
+ t = new Timer();
+ countDownTimer = new CountDownTimer(secScrew * 1000, 100) {
+ // 500 means, onTick function will be called at every 500 milliseconds
+
+ @Override
+ public void onTick(long leftTimeInMilliseconds) {
+ float seconds = (float) leftTimeInMilliseconds / 1000;
+ tvProgressLabel.setText(MessageFormat.format("{0}{1}", getString(R.string.seconds_remaining), (int) seconds));
+ circularProgressBar.setProgressWithAnimation(seconds / secScrew * 100, 100);
+ if (seconds / secScrew * 100 < 20) {
+ circularProgressBar.setColor(Color.YELLOW);
+ circularProgressBar.setBackgroundColor(adjustAlpha(Color.YELLOW));
+ }
+ }
+
+ @Override
+ public void onFinish() {
+
+ runOnUiThread(() -> {
+ circularProgressBar.setColor(Color.RED);
+ circularProgressBar.setBackgroundColor(adjustAlpha(Color.RED));
+ circularProgressBar.enableIndeterminateMode(true);
+ });
+ alarm.start();
+ outOfTime++;
+ t = new Timer();
+ //Set the schedule function and rate
+ t.scheduleAtFixedRate(new TimerTask() {
+ int milliElapsed = 0;
+
+ @Override
+ public void run() {
+ runOnUiThread(() -> tvProgressLabel.setText(MessageFormat.format("{0}{1}", getString(R.string.seconds_elapsed), milliElapsed / 1000)));
+ milliElapsed = milliElapsed + 100;
+ }
+
+ },
+ //Set how long before to start calling the TimerTask (in milliseconds)
+ 0,
+ //Set the amount of time between each execution (in milliseconds)
+ 100);
+
+ }
+ };
+ countDownTimer.start();
+ tvNumber.setOnClickListener(clickListener);
+ circularProgressBar.setOnClickListener(clickListener);
+ break;
+
+ case 9:
+ if (!stressInserted) {
+ Toast.makeText(getApplicationContext(), "Please insert your stress level! ", Toast.LENGTH_LONG).show();
+ } else {
+ stressInserted = false;
+ index = 0;
+ Integer stressLevel = seekBar.getProgress();
+ String stressInfo = "Six " + init.toString() + " " + stressLevel.toString() + " " + end.toString() + "\n";
+ saveDataExperiment(stressInfo);
+ alarm.release();
+ finish();
+ }
+ break;
+ }
+ }
+ }
+ };
+
+ View.OnClickListener clickListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if ((!tvNumber.getText().toString().equals("")) && Integer.valueOf(tvNumber.getText().toString()) == (minuends[j] - subtrahends[j])) {
+ tvNumber.setText("");
+ j++;
+ countDownTimer.cancel();
+ if (alarm.isPlaying()) {
+ alarm.pause();
+ }
+ t.cancel();
+ runOnUiThread(() -> {
+ circularProgressBar.enableIndeterminateMode(false);
+ circularProgressBar.setColor(Color.BLUE);
+ circularProgressBar.setBackgroundColor(adjustAlpha(Color.BLUE));
+ circularProgressBar.setProgressWithAnimation(100);
+ });
+
+ if (screwRemaining > 1) {
+ tvScrewRemaing.setText(String.format("\n\n%s%s\n%s", getString(R.string.remaining_screw), String.valueOf(--screwRemaining), "Now subtract 17\nfrom the previous result"));
+ countDownTimer.start();
+ } else {
+ description.setText(Html.fromHtml(phaseSixText[index++]));
+ seekBar.setEnabled(true);
+ phase++;
+ index = 0;
+ button.setEnabled(true);
+ tvScrewRemaing.setText(String.format("\n%s", getString(R.string.phase_six_completed)));
+ tvProgressLabel.setText("");
+ tvNumber.setHint("");
+ tvNumber.setEnabled(false);
+ tvNumber.setBackgroundResource(android.R.color.transparent);
+ circularProgressBar.setOnClickListener(null);
+ tvNumber.setOnClickListener(null);
+ end = Calendar.getInstance().getTimeInMillis();
+ }
+ } else if ((!tvNumber.getText().toString().equals(""))) {
+ Toast.makeText(getApplicationContext(), "The value you've entered is wrong! ", Toast.LENGTH_LONG).show();
+ tvScrewRemaing.setText(String.format("%s%s\n%s - %s = ", getString(R.string.remaining_screw), String.valueOf(screwRemaining), String.valueOf(minuends[j]), String.valueOf(subtrahends[j])));
+ tvNumber.setText("");
+ countDownTimer.cancel();
+ t.cancel();
+ runOnUiThread(() -> {
+ circularProgressBar.setColor(Color.RED);
+ circularProgressBar.setBackgroundColor(adjustAlpha(Color.RED));
+ circularProgressBar.enableIndeterminateMode(true);
+ });
+ if (!alarm.isPlaying()) {
+ alarm.start();
+ }
+ outOfTime++;
+ t = new Timer();
+ //Set the schedule function and rate
+ t.scheduleAtFixedRate(new TimerTask() {
+ int milliElapsed = 0;
+
+ @Override
+ public void run() {
+ runOnUiThread(() -> tvProgressLabel.setText(MessageFormat.format("{0}{1}", getString(R.string.seconds_elapsed), milliElapsed / 1000)));
+ milliElapsed = milliElapsed + 100;
+ }
+ },
+ //Set how long before to start calling the TimerTask (in milliseconds)
+ 0,
+ //Set the amount of time between each execution (in milliseconds)
+ 100);
+ }
+ }
+ };
+
+
+ SeekBar.OnSeekBarChangeListener seekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ // updated continuously as the user slides the thumb
+ tvStressLabel.setText(MessageFormat.format("{0}{1}", getString(R.string.stress_level), progress));
+ stressInserted = true;
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ // called when the user first touches the SeekBar
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ // called after the user finishes moving the SeekBar
+ }
+ };
+
+ private void startTimer(float duration, String[] textPhase) {
+
+ CountDownTimer countDownTimer = new CountDownTimer((int) duration * 1000, 1000) {
+ // 500 means, onTick function will be called at every 500 milliseconds
+
+ @Override
+ public void onTick(long leftTimeInMilliseconds) {
+ float seconds = (float)leftTimeInMilliseconds / 1000;
+ tvProgressLabel.setText(MessageFormat.format("{0}{1}", getString(R.string.seconds_remaining), (int) seconds));
+ circularProgressBar.setProgressWithAnimation(seconds/duration*100, 1000);
+ }
+ @Override
+ public void onFinish() {
+ circularProgressBar.setProgressWithAnimation(0, 1000);
+ if (index < textPhase.length-1) {
+ description.setText(textPhase[index++]);
+ startTimer(duration, textPhase);
+ } else {
+ description.setText(textPhase[index]);
+ seekBar.setEnabled(true);
+ phase++;
+ index = 0;
+ button.setEnabled(true);
+ tvProgressLabel.setText("");
+ end = Calendar.getInstance().getTimeInMillis();
+
+ }
+ }
+ };
+ countDownTimer.start();
+
+ }
+
+ protected void setExperimentHeader() {
+ File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS + File.separator
+ +"Experiments");
+ boolean pathIsThere;
+ if (!path.isDirectory()) {
+ pathIsThere = path.mkdirs();
+ } else {
+ pathIsThere = true;
+ }
+ if (pathIsThere) {
+ boolean fileExist = false;
+ String timeStamp = Calendar.getInstance().getTime().toString().replace(":", "_");
+ this.experimentName = "Experiment_" + timeStamp;
+ String header = "Phase start[ms] stressLevel[0,100] end[ms]" + "\n";
+ File file = new File(path, this.experimentName + ".txt");
+ if (!file.exists()) {
+ //Create file
+ try {
+ fileExist = file.createNewFile();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } else {
+ fileExist = true;
+ }
+ if (fileExist) {
+ try {
+ FileOutputStream stream = new FileOutputStream(file);
+ try {
+ stream.write(header.getBytes());
+ stream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private void saveDataExperiment(String data) {
+ //--- Check if writable
+ if (isExternalStorageWritable()) {
+ File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS + File.separator
+ +"Experiments");
+ if (path.isDirectory()) {
+ File file = new File(path, this.experimentName + ".txt");
+
+ //--- check if it exist
+ if (file.exists()) {
+ FileOutputStream outputStream;
+ StringBuilder string = new StringBuilder();
+ //--- Start to print
+ try {
+ outputStream = new FileOutputStream(file, true);
+ string.append(data);
+ String endLine = "\n";
+ string.append(endLine);
+ byte[] strToBytes = string.toString().getBytes();
+ outputStream.write(strToBytes);
+ outputStream.close();
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ }
+
+ //--- Function to export data to CSV file
+ /* Checks if external storage is available for read and write */
+ private boolean isExternalStorageWritable() {
+ String state = Environment.getExternalStorageState();
+ return Environment.MEDIA_MOUNTED.equals(state);
+ }
+
+ /**
+ * Transparent the given color by the factor
+ * The more the factor closer to zero the more the color gets transparent
+ *
+ * @param color The color to transparent
+ * @return int - A transplanted color
+ */
+ private int adjustAlpha(int color) {
+ int alpha = Math.round(Color.alpha(color) * (float) 0.3);
+ int red = Color.red(color);
+ int green = Color.green(color);
+ int blue = Color.blue(color);
+ return Color.argb(alpha, red, green, blue);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ switch (phase) {
+ case 1:
+ if (mediaPlayer.isPlaying()) {
+ mediaPlayer.stop();
+ mediaPlayer.release();
+ }
+ break;
+ case 2:
+ if (mediaPlayer.isPlaying()) {
+ mediaPlayer.stop();
+ mediaPlayer.release();
+ }
+ countDownTimer.cancel();
+ break;
+ case 3:
+ if (mediaPlayer.isPlaying()) {
+ mediaPlayer.stop();
+ mediaPlayer.release();
+ }
+ break;
+ case 4:
+ mediaPlayer.stop();
+ mediaPlayer.release();
+ break;
+ case 5:
+ if (alarm.isPlaying()) {
+ alarm.stop();
+ alarm.release();
+ }
+ mediaPlayer.release();
+ countDownTimer.cancel();
+ t.cancel();
+ break;
+ case 6:
+ if (mediaPlayer.isPlaying()) {
+ mediaPlayer.stop();
+ mediaPlayer.release();
+ }
+ break;
+ case 7:
+ if (mediaPlayer.isPlaying()) {
+ mediaPlayer.stop();
+ mediaPlayer.release();
+ }
+ case 8:
+ countDownTimer.cancel();
+ t.cancel();
+ if (alarm.isPlaying()) {
+ alarm.stop();
+ alarm.release();
+ }
+ break;
+ }
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Features/SignalFeatures.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Features/SignalFeatures.java
new file mode 100644
index 0000000..12291fe
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Features/SignalFeatures.java
@@ -0,0 +1,117 @@
+package ch.epfl.esl.elevatedmonitor.Features;
+
+import android.content.Context;
+
+import ch.epfl.esl.elevatedmonitor.Constants;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceData;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceSensor;
+import ch.epfl.esl.elevatedmonitor.R;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Data;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+
+public class SignalFeatures extends SensorDevice implements InterfaceSensor {
+
+ public final static int IPI_MAXI = 0;
+ public final static int IPI_MINI = 1;
+ public final static int IPI_DIFF = 2;
+ public final static int RISING_TIME = 3;
+ public final static int DECREASING_TIME = 4;
+
+ //var declaration
+ private double a = 0.05;
+ private double threshold = 0;
+ private boolean maximum_found = false;
+ private boolean minimum_found = false;
+ private boolean rising_flag = false;
+ private long maximum = System.currentTimeMillis();
+ private long minimum = System.currentTimeMillis();
+ private long previous_maximum = System.currentTimeMillis();
+ private long previous_minimum = System.currentTimeMillis();
+ private float previousData = 0;
+
+ private class IPIData extends Data implements InterfaceData {
+ }
+
+ public SignalFeatures(Context context, int id) {
+ super(context);
+ addData(new IPIData()); //IPI_MAXI
+ addData(new IPIData()); //IPI_MINI
+ addData(new IPIData()); //IPI_DIFF
+ addData(new IPIData()); //RISING_TIME
+ addData(new IPIData()); //DECREASING_TIME
+ setId(id);
+ }
+
+ public void insertData(float data) {
+ threshold = threshold * (1 - a) + data * a;
+
+ //Find maximum when value is above threshold
+ if (data > threshold) {
+ //If maximum is already found discard it
+ if (!maximum_found) {
+ // Set a maximum when value is going down
+ if (data < previousData) {
+ maximum = System.currentTimeMillis();
+ // If new interval is less than 20% than previous one accept this value
+ if ((maximum - previous_maximum) > (this.getData(IPI_MAXI).alwaysGet()) * 0.6) {
+ maximum_found = true;
+ this.getData(IPI_MAXI).set((maximum - previous_maximum) / 1000.0);
+ this.getData(IPI_DIFF).set(Math.abs(this.getData(IPI_MINI).alwaysGet() - this.getData(IPI_MAXI).alwaysGet()));
+ previous_maximum = maximum;
+ }
+ }
+ }
+ } else {
+ maximum_found = false;
+ }
+
+ //Find minimum when value is under threshold
+ if (data < threshold) {
+ //If minimum is already found discard it
+ if (!minimum_found) {
+ // Set a minimum when value is going down
+ if (data > previousData) {
+ minimum = System.currentTimeMillis();
+ // If new interval is less than 20% than privous one accept this value
+ if ((minimum - previous_minimum) > (this.getData(IPI_MINI).alwaysGet()) * 0.6) {
+ minimum_found = true;
+ this.getData(IPI_MINI).set((minimum - previous_minimum) / 1000.0);
+ this.getData(IPI_DIFF).set(Math.abs(this.getData(IPI_MINI).alwaysGet() - this.getData(IPI_MAXI).alwaysGet()));
+ previous_minimum = minimum;
+ }
+ }
+ }
+ } else {
+ minimum_found = false;
+ }
+
+ //Calculate rising and decreasing time
+ if (rising_flag) {
+ if (maximum > minimum) {
+ rising_flag = false;
+ this.getData(RISING_TIME).set((maximum - minimum) / 1000.0);
+ }
+ } else {
+ if (minimum > maximum) {
+ rising_flag = true;
+ this.getData(DECREASING_TIME).set((minimum - maximum) / 1000.0);
+ }
+ }
+
+ previousData = data;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ @Override
+ public void setId() {
+ this.id = Constants.IBI_ID;
+ }
+
+ @Override
+ public void setName() {
+ this.name = sensorContext.getResources().getString(R.string.signal_features);
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Interfaces/InterfaceData.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Interfaces/InterfaceData.java
new file mode 100644
index 0000000..9fe6810
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Interfaces/InterfaceData.java
@@ -0,0 +1,5 @@
+package ch.epfl.esl.elevatedmonitor.Interfaces;
+
+public interface InterfaceData {
+ double get();
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Interfaces/InterfaceDevice.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Interfaces/InterfaceDevice.java
new file mode 100644
index 0000000..2bc4987
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Interfaces/InterfaceDevice.java
@@ -0,0 +1,37 @@
+package ch.epfl.esl.elevatedmonitor.Interfaces;
+
+import java.util.ArrayList;
+
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+
+public interface InterfaceDevice {
+ /**
+ * This function create connection with the device and start the service, to know if the device
+ * is still connected and online for exemple.
+ */
+ ArrayList getSensors();
+
+ SensorDevice getSensor(int position);
+
+ void setSensors();
+
+ String getName();
+
+ void setName(String name);
+
+ String getId();
+
+ void setId(String id);
+
+ String getManagerName();
+
+ void setManagerName();
+
+ boolean isConnected();
+
+ void setConnected(Boolean connected);
+
+ void setBatteryLevel(int percentage);
+
+ int getBatteryLevel();
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Interfaces/InterfaceManager.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Interfaces/InterfaceManager.java
new file mode 100644
index 0000000..bc58bfd
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Interfaces/InterfaceManager.java
@@ -0,0 +1,17 @@
+package ch.epfl.esl.elevatedmonitor.Interfaces;
+
+public interface InterfaceManager {
+ void scan();
+
+ void isDevice(String name, String address);
+
+ void stopScan();
+
+ void connect(String id);
+
+ void disconnect(String id);
+
+ void onActivityResult(String requestCode, int resultCode);
+
+ void readBatteryLevel(String id);
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Interfaces/InterfaceSensor.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Interfaces/InterfaceSensor.java
new file mode 100644
index 0000000..7c5530c
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Interfaces/InterfaceSensor.java
@@ -0,0 +1,7 @@
+package ch.epfl.esl.elevatedmonitor.Interfaces;
+
+public interface InterfaceSensor {
+ void setId();
+
+ void setName();
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/ListViewItemCheckboxBaseAdapter.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/ListViewItemCheckboxBaseAdapter.java
new file mode 100644
index 0000000..f57ca90
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/ListViewItemCheckboxBaseAdapter.java
@@ -0,0 +1,78 @@
+package ch.epfl.esl.elevatedmonitor;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+import java.util.List;
+
+public class ListViewItemCheckboxBaseAdapter extends BaseAdapter {
+
+ private List listViewItemDtoList;
+
+ private Context ctx;
+
+ ListViewItemCheckboxBaseAdapter(Context ctx, List listViewItemDtoList) {
+ this.ctx = ctx;
+ this.listViewItemDtoList = listViewItemDtoList;
+ }
+
+ @Override
+ public int getCount() {
+ int ret = 0;
+ if(listViewItemDtoList!=null)
+ {
+ ret = listViewItemDtoList.size();
+ }
+ return ret;
+ }
+
+ @Override
+ public Object getItem(int itemIndex) {
+ Object ret = null;
+ if(listViewItemDtoList!=null) {
+ ret = listViewItemDtoList.get(itemIndex);
+ }
+ return ret;
+ }
+
+ @Override
+ public long getItemId(int itemIndex) {
+ return itemIndex;
+ }
+
+ @Override
+ public View getView(int itemIndex, View convertView, ViewGroup viewGroup) {
+
+ ListViewItemViewHolder viewHolder;
+
+ if(convertView!=null)
+ {
+ viewHolder = (ListViewItemViewHolder) convertView.getTag();
+ }else
+ {
+ convertView = View.inflate(ctx, R.layout.activity_list_view_with_checkbox_item, null);
+
+ CheckBox listItemCheckbox = convertView.findViewById(R.id.list_view_item_checkbox);
+
+ TextView listItemText = convertView.findViewById(R.id.list_view_item_text);
+
+ viewHolder = new ListViewItemViewHolder(convertView);
+
+ viewHolder.setItemCheckbox(listItemCheckbox);
+
+ viewHolder.setItemTextView(listItemText);
+
+ convertView.setTag(viewHolder);
+ }
+
+ ListViewItemDTO listViewItemDto = listViewItemDtoList.get(itemIndex);
+ viewHolder.getItemCheckbox().setChecked(listViewItemDto.isChecked());
+ viewHolder.getItemTextView().setText(listViewItemDto.getItemText());
+
+ return convertView;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/ListViewItemDTO.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/ListViewItemDTO.java
new file mode 100644
index 0000000..a150834
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/ListViewItemDTO.java
@@ -0,0 +1,24 @@
+package ch.epfl.esl.elevatedmonitor;
+
+class ListViewItemDTO {
+
+ private boolean checked = false;
+
+ private String itemText = "";
+
+ boolean isChecked() {
+ return checked;
+ }
+
+ void setChecked(boolean checked) {
+ this.checked = checked;
+ }
+
+ String getItemText() {
+ return itemText;
+ }
+
+ void setItemText(String itemText) {
+ this.itemText = itemText;
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/ListViewItemViewHolder.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/ListViewItemViewHolder.java
new file mode 100644
index 0000000..1c41ffc
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/ListViewItemViewHolder.java
@@ -0,0 +1,34 @@
+package ch.epfl.esl.elevatedmonitor;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+class ListViewItemViewHolder extends RecyclerView.ViewHolder {
+
+ private CheckBox itemCheckbox;
+
+ private TextView itemTextView;
+
+ ListViewItemViewHolder(View itemView) {
+ super(itemView);
+ }
+
+ CheckBox getItemCheckbox() {
+ return itemCheckbox;
+ }
+
+ void setItemCheckbox(CheckBox itemCheckbox) {
+ this.itemCheckbox = itemCheckbox;
+ }
+
+ TextView getItemTextView() {
+ return itemTextView;
+ }
+
+ void setItemTextView(TextView itemTextView) {
+ this.itemTextView = itemTextView;
+ }
+}
+
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/MainActivity.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/MainActivity.java
new file mode 100644
index 0000000..7a8e762
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/MainActivity.java
@@ -0,0 +1,1339 @@
+package ch.epfl.esl.elevatedmonitor;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.AlarmManager;
+import android.app.Dialog;
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.graphics.Color;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.support.annotation.NonNull;
+import android.support.design.widget.FloatingActionButton;
+import android.support.design.widget.NavigationView;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.content.LocalBroadcastManager;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.Switch;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.google.firebase.database.DataSnapshot;
+import com.google.firebase.database.DatabaseError;
+import com.google.firebase.database.DatabaseReference;
+import com.google.firebase.database.FirebaseDatabase;
+import com.google.firebase.database.ValueEventListener;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.List;
+import java.util.Locale;
+
+import ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.EmpaticaManager;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Device;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+import iam.thevoid.batteryview.BatteryView;
+
+
+public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener{
+ // TAG
+ private static String TAG = "Main Activity SQUINCI";
+
+ // Receivers
+ public static DeviceStateReceiver deviceStateReceiver;
+
+ // Adapters
+ private static DeviceAdapter deviceAdapter;
+
+ // Managers
+ private ServiceManagerConnection serviceManagerConnection = null;
+ private Boolean serviceManagerBound = false;
+
+
+ private static final int REQUEST_PERMISSION_ACCESS_COARSE_LOCATION = 2;
+ private static final int REQUEST_PERMISSION_SERVICE = 1;
+ private static String requestBroadcastCallback;
+ private final static int REQUEST_ENABLE_BT = 1;
+ private final static int REQUEST_ACTIVITY_BROADCAST = 2;
+
+ private static String requestBroadcastAction;
+ private boolean isInForeground = false;
+ private FloatingActionButton fab2;
+ private NavigationView navigationView;
+
+ Dialog savingDialog;
+ public static boolean showingPopout = false;
+ private boolean showingAlert = false;
+ private boolean showingAlertWrong = false;
+ private boolean kill = false;
+
+ private String experimentName = "Timings";
+ private String initPro;
+ private String endPro;
+ private String initPain;
+ private String endPain;
+
+ private SharedPreferences.Editor editor;
+ SharedPreferences sharedPref;
+ DateFormat dateFormat = new SimpleDateFormat("dd-MM-YYY HH:mm:ss", Locale.FRENCH);
+ String selectedDevices;
+
+
+ @SuppressLint("ApplySharedPref")
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ savingDialog = new Dialog(this);
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+ setTitle(R.string.title_activity_auto_connect);
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
+ drawer.addDrawerListener(toggle);
+ toggle.syncState();
+
+ sharedPref = this.getSharedPreferences(
+ getString(R.string.preference_file_key), Context.MODE_PRIVATE);
+ editor = sharedPref.edit();
+ String[] allDevices = Constants.DEVICES_NAME;
+ ArrayList chosenDevices = new ArrayList<>();
+ selectedDevices = sharedPref.getString(getString(R.string.selected_devices), "Empatica");
+ if (selectedDevices == null || selectedDevices.equals("") || selectedDevices.equals("[]")) {
+ AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this).create();
+ alertDialog.setTitle(getString(R.string.no_devices));
+ alertDialog.setMessage(getString(R.string.please_setting));
+
+ alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, getString(R.string.dismiss),
+ (dialog, which) -> {
+ dialog.dismiss();
+ showingAlert = false;
+ });
+ alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.insert_empatica),
+ (dialog, which) -> {
+ dialog.dismiss();
+ selectedDevices = "Empatica";
+ editor.putString(getString(R.string.selected_devices), selectedDevices);
+ editor.apply();
+ showingAlert = false;
+ for (String device : Constants.DEVICES_NAME){
+ if (selectedDevices != null && selectedDevices.contains(device)){
+ chosenDevices.add(device);
+ }
+ }
+ ServiceManager.updateChosenDevices(getApplicationContext(), getDeviceClasses(chosenDevices.toString()));
+ if (deviceAdapter != null) {
+ deviceAdapter.notifyDataSetChanged();
+ }
+ });
+ alertDialog.show();
+
+ }
+
+
+ for (String device : Constants.DEVICES_NAME){
+ if (selectedDevices != null && selectedDevices.contains(device)){
+ chosenDevices.add(device);
+ }
+ }
+ ServiceManager.updateChosenDevices(getApplicationContext(), getDeviceClasses(chosenDevices.toString()));
+ ServiceManager.setAutoSaving(sharedPref.getBoolean(getString(R.string.auto_saving), true));
+ ServiceManager.setUsage(sharedPref.getString(getString(R.string.selected_profile), "One"));
+
+ setLoggingHeader();
+
+
+ navigationView = findViewById(R.id.nav_view);
+ navigationView.setNavigationItemSelectedListener(this);
+
+ Switch switchPro = findViewById(R.id.switch_Pro);
+ switchPro.setChecked(sharedPref.getBoolean(getString(R.string.pro_checked), false));
+ switchPro.setOnCheckedChangeListener((view, isChecked) ->{
+ initPro = sharedPref.getString(getString(R.string.init_pro),"Now");
+ if (isChecked){
+ initPro = dateFormat.format(Calendar.getInstance().getTime());
+ editor.putBoolean(getString(R.string.pro_checked), true);
+ editor.putString(getString(R.string.init_pro), initPro);
+ editor.apply();
+ }
+ else {
+ endPro = dateFormat.format(Calendar.getInstance().getTime());
+ editor.putBoolean(getString(R.string.pro_checked), false);
+ editor.apply();
+ String string = "Prodromal, " + initPro + ", " + endPro + "\n";
+ saveDataLogging(string);
+ Toast.makeText(this, getString(R.string.EntrySaved), Toast.LENGTH_LONG).show();
+
+ }
+ });
+
+ Switch switchPain = findViewById(R.id.switch_Pain);
+ switchPain.setChecked(sharedPref.getBoolean(getString(R.string.pain_checked), false));
+ switchPain.setOnCheckedChangeListener((view, isChecked) ->{
+ initPain = sharedPref.getString(getString(R.string.init_pain),"Now");
+
+ if (isChecked){
+ initPain = dateFormat.format(Calendar.getInstance().getTime());
+ editor.putBoolean(getString(R.string.pain_checked), true);
+ editor.putString(getString(R.string.init_pain), initPain);
+ editor.apply();
+ }
+ else {
+ endPain = dateFormat.format(Calendar.getInstance().getTime());
+ editor.putBoolean(getString(R.string.pain_checked), false);
+ editor.apply();
+ String string = "Pain, " + initPain + ", " + endPain + "\n";
+ saveDataLogging(string);
+ if (switchPro.isChecked()){
+ endPro = dateFormat.format(Calendar.getInstance().getTime());
+ String strings = "Prodromal, " + initPro + ", " + endPro + "\n";
+ saveDataLogging(strings);
+ switchPro.setChecked(false);
+ Toast.makeText(this, getString(R.string.EntrySaved), Toast.LENGTH_LONG).show();
+ }
+
+ Intent i = new Intent(this, SurveyActivity.class);
+ i.putExtra("recording_id", "recording ID");
+ i.putExtra("identifier", Calendar.getInstance().getTime().toString());
+ this.startActivity(i);
+
+
+ }
+ });
+
+ fab2 = findViewById(R.id.fab_save);
+ fab2.setOnClickListener(view -> {
+
+ if (ServiceManager.saving()) {
+ stopSaving();
+ Log.d(TAG, "Saving stopped because the user re-pressed the left FAB");
+ }
+ else {
+ startSaving();
+ Log.d(TAG, "Saving started because the user pressed the left FAB");
+ }
+
+
+ });
+
+ deviceAdapter = new DeviceAdapter();
+
+ ListView deviceListView = findViewById(R.id.list_view);
+ deviceListView.setAdapter(deviceAdapter);
+
+ //-------------------------- Register the BroadCast Receiver -----------------------------//
+ // The filter's action is BROADCAST_STATUS, START_ACTIVITY_STATUS
+ IntentFilter statusIntentFilter = new IntentFilter();
+ statusIntentFilter.addAction(Constants.BROADCAST_STATUS);
+ statusIntentFilter.addAction(Constants.START_ACTIVITY_STATUS);
+ statusIntentFilter.addAction(Constants.REQUEST_PERMISSION_STATUS);
+ statusIntentFilter.addAction(Constants.MISSING_KEY);
+ statusIntentFilter.addAction(Constants.WRONG_KEY);
+
+ // Instantiates a new ScannedStateReceiver
+ deviceStateReceiver = new DeviceStateReceiver();
+ // Registers the ScannedStateReceiver and its intent filters
+ LocalBroadcastManager.getInstance(this).registerReceiver(deviceStateReceiver, statusIntentFilter);
+
+ Thread.currentThread().setUncaughtExceptionHandler((myThread, e) -> {
+ Log.d(TAG, myThread.getName() + " throws exception: " + e);
+ SharedPreferences.Editor editor = sharedPref.edit();
+ Log.d(TAG, myThread.getName() + " throws exception: " + e);
+ String selDevices = sharedPref.getString(getString(R.string.selected_devices), TextUtils.join("", allDevices));
+ if (selDevices!= null && selDevices.contains(getString(R.string.empatica)))
+ selDevices = selDevices.replaceAll(getString(R.string.empatica), "");
+ editor.putString(getString(R.string.selected_devices), selDevices);
+ editor.commit();
+
+ Intent intent = new Intent(this, MainActivity.class);
+ intent.putExtra("crash", true);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_ACTIVITY_NEW_TASK);
+ PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+ AlarmManager mgr = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
+ mgr.set(AlarmManager.RTC, SystemClock.elapsedRealtime(), pendingIntent);
+ finish();
+ System.exit(2);
+ });
+
+ // Start a new connection
+ if (serviceManagerConnection == null) {
+ serviceManagerConnection = new ServiceManagerConnection();
+ }
+
+ // Scan devices to update status.
+ ServiceManager.startActionScan(this);
+ Log.d(TAG, "Asked for a scan from the mainActivity onCreate.");
+
+ // Ask the permission to write
+ initExternalWriteAuthorizations();
+ // Connect to Firebase
+ loadQuestions();
+
+ final SharedPreferences prefs = this.getSharedPreferences(Constants.APP_SETTINGS, MODE_PRIVATE);
+
+
+ FloatingActionButton fabQuest = findViewById(R.id.fab_questionnaire);
+ fabQuest.setOnClickListener(view -> {
+ final String[] items;
+ String value = prefs.getString(Constants.DRUGS_NAMES, "médicaments 1@médicaments 2");
+
+ if (value != null)
+ items = value.split("@");
+ else{
+ value = "médicaments 1@médicaments 2";
+ items = value.split("@");
+ }
+ //final CharSequence[] items = {" Ciccio "," Pasticcio "," Trucco "," Babbione "};
+ final ArrayList selectedItems = new ArrayList<>();
+
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
+ builder.setTitle(R.string.drug_type);
+
+ builder.setMultiChoiceItems(items, null, (dialog, which, isChecked) -> {
+ //Here you add or remove the items from the list selectedItems. That list will be the result of the user selection.
+ if (isChecked) {
+ selectedItems.add(items[which]);
+ } else selectedItems.remove(items[which]);
+ });
+
+ builder.setPositiveButton(getString(R.string.done), (dialog, which) -> {
+
+ if (!selectedItems.isEmpty()) {
+ //Do something when the user closes the dialog by pressing the Done button
+ String timePills = dateFormat.format(Calendar.getInstance().getTime());
+ String strings = "Pills, " + timePills + ", " + timePills + "\n";
+ strings = strings.concat("Drugs selected: " + selectedItems.toString() + "\n");
+ saveDataLogging(strings);
+ Toast.makeText(this, getString(R.string.EntrySaved), Toast.LENGTH_LONG).show();
+
+ }else {
+ Toast.makeText(this, getString(R.string.Choose), Toast.LENGTH_LONG).show();
+
+ }
+ });
+
+ builder.setNegativeButton(getString(R.string.dismiss), (dialog, which) -> {
+ dialog.dismiss();
+ //Do something else if you want
+ Toast.makeText(this, getString(R.string.EntryRejected), Toast.LENGTH_LONG).show();
+
+ });
+
+ builder.create();
+ builder.show();
+ });
+
+ if (getIntent().getBooleanExtra("crash", false)) {
+ AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this).create();
+ alertDialog.setTitle(getString(R.string.crash_detected));
+ if (!isNetworkAvailable())
+ alertDialog.setMessage(getString(R.string.device_has_no_internet_access));
+ else
+ alertDialog.setMessage(getString(R.string.wrong_empa_key));
+ alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, getString(R.string.dismiss),
+ (dialog, which) -> {
+ dialog.dismiss();
+ showingAlert = false;
+ });
+ alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.go_to_setting),
+ (dialog, which) -> {
+ dialog.dismiss();
+ Intent settingActivity = new Intent(this, SettingsActivity.class);
+ startActivity(settingActivity);
+ showingAlert = false;
+ });
+ alertDialog.show();
+ getIntent().removeExtra("crash");
+
+ }
+
+ }
+
+ private void loadQuestions() {
+ FirebaseDatabase database = FirebaseDatabase.getInstance();
+ final SharedPreferences prefs = this.getSharedPreferences(Constants.APP_SETTINGS, MODE_PRIVATE);
+
+ final DatabaseReference config = database.getReference(Constants.DRUGS_NAMES);
+ config.addValueEventListener(new ValueEventListener() {
+ @Override
+ public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
+ // This method is called once with the initial value and again
+ // whenever data at this location is updated.
+ String value = dataSnapshot.getValue(String.class);
+ prefs.edit().putString(Constants.DRUGS_NAMES, value).apply();
+ Log.v(TAG, value);
+
+ // We don't want live updates
+ config.removeEventListener(this);
+
+ }
+
+ @Override
+ public void onCancelled(@NonNull DatabaseError databaseError) {
+ // Failed to read value
+ Log.w(TAG, "Failed to read value.", databaseError.toException());
+ }
+ });
+
+
+
+ if (ServiceManager.getUsage().equals("One")){
+ final DatabaseReference configurationRef = database.getReference(Constants.REMOTE_CONFIGURATION);
+
+ configurationRef.addValueEventListener(new ValueEventListener() {
+ @Override
+ public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
+ // This method is called once with the initial value and again
+ // whenever data at this location is updated.
+ String value = dataSnapshot.getValue(String.class);
+ prefs.edit().putString(Constants.REMOTE_CONFIGURATION, value).apply();
+ Log.v(TAG, value);
+ //JSONObject remoteConfig = JSON.parseObject(value);
+
+ // Set version config
+ //TextView versionInfo = findViewById(R.id.versionInfo);
+ //versionInfo.setText(remoteConfig.getString("config_version"));
+
+ // We don't want live updates
+ configurationRef.removeEventListener(this);
+
+ }
+
+ @Override
+ public void onCancelled(@NonNull DatabaseError databaseError) {
+ // Failed to read value
+ Log.w(TAG, "Failed to read value.", databaseError.toException());
+ }
+ });
+ try (InputStream is = getResources().openRawResource(R.raw.app_remote_config)) {
+ Writer writer = new StringWriter();
+ char[] buffer = new char[1024];
+ Reader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
+ int n;
+ while ((n = reader.read(buffer)) != -1) {
+ writer.write(buffer, 0, n);
+ }
+ String jsonString = writer.toString();
+ prefs.edit().putString(Constants.REMOTE_CONFIGURATION, jsonString).apply();
+
+ } catch (Exception e) {
+ Log.d(TAG, e.getMessage());
+ }
+
+ } else if (ServiceManager.getUsage().equals("Two")) {
+ final DatabaseReference configurationRef = database.getReference(Constants.REMOTE_CONFIGURATION_TWO);
+
+ configurationRef.addValueEventListener(new ValueEventListener() {
+ @Override
+ public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
+ // This method is called once with the initial value and again
+ // whenever data at this location is updated.
+ String value = dataSnapshot.getValue(String.class);
+ prefs.edit().putString(Constants.REMOTE_CONFIGURATION, value).apply();
+ Log.v(TAG, value);
+ //JSONObject remoteConfig = JSON.parseObject(value);
+
+ // Set version config
+ //TextView versionInfo = findViewById(R.id.versionInfo);
+ //versionInfo.setText(remoteConfig.getString("config_version"));
+
+ // We don't want live updates
+ configurationRef.removeEventListener(this);
+
+ }
+
+ @Override
+ public void onCancelled(@NonNull DatabaseError databaseError) {
+ // Failed to read value
+ Log.w(TAG, "Failed to read value.", databaseError.toException());
+ }
+ });
+ try (InputStream is = getResources().openRawResource(R.raw.app_remote_config_two)) {
+ Writer writer = new StringWriter();
+ char[] buffer = new char[1024];
+ Reader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
+ int n;
+ while ((n = reader.read(buffer)) != -1) {
+ writer.write(buffer, 0, n);
+ }
+ String jsonString = writer.toString();
+ prefs.edit().putString(Constants.REMOTE_CONFIGURATION, jsonString).apply();
+
+ } catch (Exception e) {
+ Log.d(TAG, e.getMessage());
+ }
+ }
+ }
+
+ private boolean isNetworkAvailable() {
+ ConnectivityManager connectivityManager
+ = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
+ return activeNetworkInfo != null && activeNetworkInfo.isConnected();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ //------------------------------ Bind to LocalService ------------------------------------//
+ Intent serviceManagerIntent = new Intent(this, ServiceManager.class);
+ if (!serviceManagerBound) {
+ this.bindService(serviceManagerIntent, serviceManagerConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_ENABLE_BT) {
+ if (resultCode == Activity.RESULT_OK) {
+ startScanning();
+ Log.d(TAG, "Asked for a scan from the onActivityResult.");
+
+ }
+ if (resultCode == Activity.RESULT_CANCELED) {
+ Snackbar.make(findViewById(R.id.drawer_layout), R.string.error_bluetooth, Snackbar.LENGTH_SHORT).show();
+ }
+ } else if (requestCode == REQUEST_ACTIVITY_BROADCAST) {
+ ServiceManager.onActivityResult(requestBroadcastAction, resultCode, requestBroadcastCallback);
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ isInForeground = true;
+
+ navigationView.setCheckedItem(R.id.auto_connect);
+ //--- Update listChosenDevices
+ for (Device device : ServiceManager.getChosenDevices()) {
+ device.setScanned(false);
+ device.setConnected(false);
+ for (Device cd : ServiceManager.getConnectedDevices()){
+ if(device.getId().equals(cd.getId())){
+ device.setConnected(true);
+ device.setScanned(true);
+ break;
+ }
+ }
+ for (Device scannedDevice : ServiceManager.getScannedDevices()) {
+ if (device.getId().equals(scannedDevice.getId())) {
+ device.setScanned(true);
+ break;
+ }
+ }
+ }
+ updateAndCheck();
+
+ for (Device device : ServiceManager.getConnectedDevices()){
+ boolean isThere = false;
+ for (Device chosenDevice : ServiceManager.getChosenDevices()){
+ if (chosenDevice.getId().equals(device.getId())){
+ isThere = true;
+ }
+ }
+ if (isThere) {
+ ServiceManager.startActionReadBatteryLevel(this, device.getId(), device.getManagerName());
+ Log.d(TAG, "Asked for the battery of " + device.getName() + " from the onResume.");
+ }
+ }
+
+ // Notify to the Adapter that the list has been changed
+ if (deviceAdapter != null) {
+ deviceAdapter.notifyDataSetChanged();
+ }
+ initBluetoothAuthorizations();
+ Log.d(TAG, "Asked for a scan from the onResume.");
+
+
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (deviceStateReceiver != null)
+ LocalBroadcastManager.getInstance(this).unregisterReceiver(deviceStateReceiver);
+ // closing Entire Application
+ if (kill)
+ android.os.Process.killProcess(android.os.Process.myPid());
+ super.onDestroy();
+
+ }
+
+ public void startSaving() {
+
+ for (Device connectedDevice : ServiceManager.getConnectedDevices()) {
+ SensorDevice selectedSensor = null;
+ Session session = new Session(connectedDevice, selectedSensor);
+ ServiceManager.sessionsList.add(session);
+ }
+ if (ServiceManager.saving()) {
+ fab2.setBackgroundTintList(getResources().getColorStateList(R.color.green));
+ // Notify to the Adapter that the list has been changed
+ if (deviceAdapter != null) {
+ deviceAdapter.notifyDataSetChanged();
+ }
+ Toast.makeText(MainActivity.this, R.string.saving_started, Toast.LENGTH_SHORT).show();
+ ServiceManager.startActionNotify(MainActivity.this, getString(R.string.saving_started), ServiceManager.CHANNEL_DEFAULT);
+ Log.d(TAG, getString(R.string.saving_started));
+ }
+ else
+ Toast.makeText(MainActivity.this, R.string.please_connect, Toast.LENGTH_SHORT).show();
+
+
+ }
+
+ public void startSaving(String id) {
+ String deviceName = "";
+ for (Device connectedDevice : ServiceManager.getConnectedDevices()) {
+ if (connectedDevice.getId().equals(id)) {
+ SensorDevice selectedSensor = null;
+ deviceName = connectedDevice.getName();
+ Session session = new Session(connectedDevice, selectedSensor);
+ ServiceManager.sessionsList.add(session);
+ break;
+ }
+ }
+
+ if (ServiceManager.saving()) {
+ fab2.setBackgroundTintList(getResources().getColorStateList(R.color.green));
+ // Notify to the Adapter that the list has been changed
+ if (deviceAdapter != null) {
+ deviceAdapter.notifyDataSetChanged();
+ }
+ ServiceManager.startActionNotify(MainActivity.this, getString(R.string.saving_started), ServiceManager.CHANNEL_DEFAULT);
+ Log.d(TAG, "I'm starting to save " + deviceName + " as well");
+
+ }
+
+ }
+
+ private void stopSaving(){
+ for (Session session : ServiceManager.sessionsList){
+ session.stopSession();
+ }
+ ServiceManager.sessionsList.clear();
+ fab2.setBackgroundTintList(getResources().getColorStateList(R.color.red));
+ Toast.makeText(MainActivity.this, R.string.saving_stopped, Toast.LENGTH_SHORT).show();
+ // Notify to the Adapter that the list has been changed
+ if (deviceAdapter != null) {
+ deviceAdapter.notifyDataSetChanged();
+ }
+ ServiceManager.startActionNotify(MainActivity.this, getString(R.string.saving_stopped), ServiceManager.CHANNEL_DEFAULT);
+ Log.d(TAG, getString(R.string.saving_stopped));
+
+
+ }
+
+ public void ShowPopup() {
+ if (!showingPopout) {
+ // Create and show the dialog.
+ SavingFragment savingFragment = SavingFragment.newInstance();
+ showingPopout = true;
+ savingFragment.show(getSupportFragmentManager(), "SavingFragment");
+ }
+ }
+
+ public void ShowMissingPopup() {
+ if (!showingAlert) {
+ // Create and show the alert.
+ AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this).create();
+ alertDialog.setTitle(getString(R.string.api_key_missing));
+ alertDialog.setMessage(getString(R.string.go_to_setts_and_enter_key));
+ alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "Dismiss",
+ (dialog, which) -> {
+ dialog.dismiss();
+ showingAlert = false;
+ });
+ alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.go_to_settings),
+ (dialog, which) -> {
+ dialog.dismiss();
+ Intent settingActivity = new Intent(this, SettingsActivity.class);
+ startActivity(settingActivity);
+ showingAlert = false;
+ });
+ alertDialog.show();
+ showingAlert = true;
+ }
+
+ }
+
+ public void ShowWrongKeyPopup() {
+ if (!showingAlertWrong) {
+ // Create and show the alert.
+ AlertDialog alertDialog = new AlertDialog.Builder(MainActivity.this).create();
+ alertDialog.setTitle(getString(R.string.wrong_key));
+ alertDialog.setMessage(getString(R.string.go_to_setts_for_valid_key));
+ alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "Dismiss",
+ (dialog, which) -> {
+ dialog.dismiss();
+ showingAlert = false;
+ });
+ alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.go_to_setting),
+ (dialog, which) -> {
+ dialog.dismiss();
+ Intent settingActivity = new Intent(this, SettingsActivity.class);
+ startActivity(settingActivity);
+ showingAlert = false;
+ });
+ alertDialog.show();
+ showingAlertWrong = true;
+ }
+
+ }
+
+ @Override
+ public boolean onNavigationItemSelected(@NonNull MenuItem item) {
+ // Handle navigation view item clicks here.
+ int id = item.getItemId();
+
+ if (id == R.id.nav_settings) {
+ Intent permissionActivity = new Intent(this, PermissionActivity.class);
+ startActivity(permissionActivity);
+ } else if (id == R.id.auto_connect) {
+ initBluetoothAuthorizations();
+ } else if (id == R.id.nav_monitoring) {
+ Intent monitoringActivity = new Intent(this, MonitoringActivity.class);
+ startActivity(monitoringActivity);
+ }
+
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ drawer.closeDrawer(GravityCompat.START);
+ return true;
+ }
+
+ @Override
+ public void onBackPressed() {
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ if (drawer.isDrawerOpen(GravityCompat.START)) {
+ drawer.closeDrawer(GravityCompat.START);
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ public void startScanning(){
+ //-------------------------- Start the ServiceManager scanning -----------------------------//
+ ServiceManager.startActionScan(this);
+ // ServiceManager.startActionNotify(MainActivity.this, getString(R.string.scanning), ServiceManager.CHANNEL_DEFAULT);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+
+ //noinspection SimplifiableIfStatement
+ if (id == R.id.action_settings) {
+ Intent permissionActivity = new Intent(this, PermissionActivity.class);
+ startActivity(permissionActivity);
+ return true;
+ }
+ if (id == R.id.action_disconnect_all) {
+ for (Device device : ServiceManager.getChosenDevices()) {
+ ServiceManager.startActionStartDisconnect(this, device.getId(), device.getManagerName());
+ }
+ Log.d(TAG, "Started action disconnect all since the user asked for it");
+ return true;
+ }
+ if (id == R.id.action_disconnect_and_stop){
+ for (Device device : ServiceManager.getChosenDevices()) {
+ ServiceManager.startActionStartDisconnect(this, device.getId(), device.getManagerName());
+ }
+ Log.d(TAG, "Started action disconnect all since the user asked for it");
+ //ServiceManager.stopService(this);
+ kill = true;
+ this.finish();
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * First ask for Bluetooth authorizations and then init scanning.
+ **/
+ private void initBluetoothAuthorizations() {
+ BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ if (mBluetoothAdapter == null) {
+ // Device doesn't support Bluetooth
+ Toast.makeText(this, "Device does not support Bluetooth", Toast.LENGTH_LONG).show();
+ } else {
+ // Device does support Bluetooth
+ if (!mBluetoothAdapter.isEnabled()) {
+ Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+
+ startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
+ } else {
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, REQUEST_PERMISSION_ACCESS_COARSE_LOCATION);
+ } else {
+ // Everything is ok, then start scanning
+ EmpaticaManager.permissionGiven = true;
+ startScanning();
+ Log.d(TAG, "Asked for a scan from initBlueAuth.");
+ }
+ }
+ }
+ }
+
+ private class ServiceManagerConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ // We've bound to LocalService, cast the IBinder and get LocalService instance
+ ServiceManager.ServiceManagerBinder binder = (ServiceManager.ServiceManagerBinder) service;
+ ServiceManager.serviceManager = binder.getService();
+ serviceManagerBound = true;
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName arg0) {
+ serviceManagerBound = false;
+ }
+
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+
+ if (serviceManagerBound) {
+ this.unbindService(serviceManagerConnection);
+ serviceManagerBound = false;
+ }
+
+ }
+
+ /**
+ * Broadcast receiver for receiving status updates from the Devices Services to update list
+ */
+ private class DeviceStateReceiver extends BroadcastReceiver {
+ // Called when the BroadcastReceiver gets an Intent it's registered to receive
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ ArrayList listChosenDevices = ServiceManager.getChosenDevices();
+ final String status = intent.getAction();
+
+ if (Constants.REQUEST_PERMISSION_STATUS.equals(status)) {
+ requestBroadcastAction = intent.getStringExtra(Constants.REQUEST_PERMISSION_NAME);
+ requestBroadcastCallback = intent.getStringExtra(Constants.REQUEST_PERMISSION_CALLBACK);
+ ActivityCompat.requestPermissions(MainActivity.this, new String[]{requestBroadcastAction}, REQUEST_PERMISSION_SERVICE);
+ }
+ else if (Constants.MISSING_KEY.equals(status)){
+ Log.d(TAG, "Received MISSING_KEY");
+ ShowMissingPopup();
+ } else if (Constants.WRONG_KEY.equals(status)){
+ Log.d(TAG, "Received WRONG_KEY");
+ ShowWrongKeyPopup();
+ }
+ //--- This status will start activity for result.
+ else if (Constants.START_ACTIVITY_STATUS.equals(status)) {
+ requestBroadcastAction = intent.getStringExtra(Constants.START_ACTIVITY_INTENT);
+ requestBroadcastCallback = intent.getStringExtra(Constants.START_ACTIVITY_CALLBACK);
+
+ Intent actionIntent = new Intent(requestBroadcastAction);
+ startActivityForResult(actionIntent, REQUEST_ACTIVITY_BROADCAST);
+ ServiceManager.askingPermission = false;
+
+ } else if (Constants.BROADCAST_STATUS.equals(status)) {
+ final String statusType = intent.getStringExtra(Constants.EXTENDED_DATA_STATUS);
+ if (ServiceManager.STATUS_SCANNED.equals(statusType)) {
+ Log.d(TAG, "Received STATUS_SCANNED");
+ // If the device has been scanned, connect to it
+ for (Device device : listChosenDevices) {
+ device.setScanned(false);
+ for (Device scannedDevice : ServiceManager.getScannedDevices()) {
+// String deviceName = device.getName();
+// String scanDeviceName = scannedDevice.getName();
+ if (device.getName().contains(scannedDevice.getName()) || scannedDevice.getName().contains(device.getName())) {
+ device.setScanned(true);
+ device.setId(scannedDevice.getId());
+ if(!device.isConnected()) {
+ ServiceManager.startActionConnect(MainActivity.this, scannedDevice.getId(), scannedDevice.getManagerName());
+ Log.d(TAG, "Asked for the connection to " + scannedDevice.getName() +" since I have received a STATUS_SCANNED and the device was not connected yet");
+ }
+ }
+ }
+ }
+ if (deviceAdapter != null) {
+ deviceAdapter.notifyDataSetChanged();
+ }
+ }
+ if (ServiceManager.STATUS_CONNECTED.equals(statusType)) {
+ Log.d(TAG, "Received STATUS_CONNECTED");
+ // If a device has been connected, notify the user with a toast
+ for (Device device : listChosenDevices) {
+ for (Device scannedDevice : ServiceManager.getConnectedDevices()) {
+ if (device.getId().equals(scannedDevice.getId())) {
+ if (!device.isConnected()) {
+ device.setConnected(true);
+ Toast.makeText(context, getString(R.string.connected) + " to: " + device.getName(), Toast.LENGTH_SHORT).show();
+ if (isInForeground) {
+ ServiceManager.startActionReadBatteryLevel(MainActivity.this, device.getId(), device.getManagerName());
+ Log.d(TAG, "Asked for the battery since I have received STATUS_CONNECTED and we are in foreground.");
+ }
+ break;
+ }
+ }
+ }
+ }
+ if (!isInForeground && ServiceManager.saving()){
+ /* In case we have some connected devices that are not among those currently saved, we start to save. */
+ for (Device cd : ServiceManager.getConnectedDevices()) {
+ boolean isThere = false;
+ for (Session session : ServiceManager.sessionsList) {
+ if (session.getID().equals(cd.getId())) {
+ if (session.isActive())
+ isThere = true;
+ }
+ }
+ if (!isThere) {
+ Log.d(TAG, "We are not in foreground, we are saving but "+ cd.getName() + " is connected but not saved.");
+ startSaving(cd.getId());
+ break;
+ }
+ }
+ }
+ if (deviceAdapter != null) {
+ deviceAdapter.notifyDataSetChanged();
+ }
+ }
+ if (ServiceManager.STATUS_ERROR_CONNECTION.equals(statusType)) {
+ Log.d(TAG, "Received STATUS_ERROR_CONNECTION");
+ CharSequence error = intent.getStringExtra(Constants.EXTENDED_DATA_INFOS);
+
+ Snackbar.make(MainActivity.this.getWindow().getDecorView().findViewById(R.id.list_view), error, Snackbar.LENGTH_LONG).show();
+ }
+ if (ServiceManager.STATUS_DISCONNECTED.equals(statusType)) {
+ Log.d(TAG, "Received STATUS_DISCONNECTED");
+ //--- Update listChosenDevices
+ for (Device device : listChosenDevices) {
+ device.setScanned(false);
+ device.setConnected(false);
+ for (Device cd : ServiceManager.getConnectedDevices()){
+ if(device.getId().equals(cd.getId())){
+ device.setConnected(true);
+ device.setScanned(true);
+ }
+ }
+ for (Device scannedDevice : ServiceManager.getScannedDevices()) {
+ if (device.getId().equals(scannedDevice.getId())) {
+ device.setScanned(true);
+ }
+ }
+ }
+ for (Device device : listChosenDevices) {
+ if (!device.isConnected() && !device.isScanned()){
+ device.setId("N.D.");
+ }
+ }
+ if (deviceAdapter != null) {
+ deviceAdapter.notifyDataSetChanged();
+ }
+ }
+ if (ServiceManager.SATUS_BATTERY_READ.equals(statusType)){
+ Log.d(TAG, "Received SATUS_BATTERY_READ");
+ for (Device device : listChosenDevices) {
+ for (Device cd : ServiceManager.getConnectedDevices()) {
+ if (device.getId().equals(cd.getId())) {
+ int batteryLevel = cd.getBatteryLevel();
+ if (batteryLevel > 0)
+ device.setBatteryLevel(batteryLevel);
+ }
+ }
+ }
+ if (deviceAdapter != null) {
+ deviceAdapter.notifyDataSetChanged();
+ }
+ }
+
+ if (ServiceManager.STATUS_SAVE.equals(statusType)){
+ stopSaving();
+ Log.d(TAG, "Saving stopped since received STATUS SAVE");
+ startSaving();
+ Log.d(TAG, "Saving started since received STATUS SAVE");
+ if (deviceAdapter != null) {
+ deviceAdapter.notifyDataSetChanged();
+ }
+ }
+
+ if (ServiceManager.STATUS_STOP_ONE_SAVE.equals(statusType)){
+ // It can be that session list now is empty
+ if (!ServiceManager.saving()) {
+ fab2.setBackgroundTintList(getResources().getColorStateList(R.color.red));
+ }
+ // Notify to the Adapter that the list has been changed
+ if (deviceAdapter != null) {
+ deviceAdapter.notifyDataSetChanged();
+ }
+ }
+ }
+ updateAndCheck();
+ }
+ }
+
+
+ class DeviceAdapter extends BaseAdapter {
+
+ @Override
+ public int getCount() {
+ return ServiceManager.getChosenDevices().size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return ServiceManager.getChosenDevices().get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return 0;
+ }
+
+ @Override
+ public View getView(final int position, View convertView, ViewGroup parent) {
+ Device device = ServiceManager.getChosenDevices().get(position);
+ if (convertView == null) {
+ LayoutInflater vi = (LayoutInflater) MainActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ convertView = vi.inflate(R.layout.list_device, parent, false);
+ }
+ TextView textView_name = convertView.findViewById(R.id.textViewName);
+ TextView textView_id = convertView.findViewById(R.id.textViewId);
+ ImageView imageView_scanned = convertView.findViewById(R.id.imageViewScanned);
+ ImageView imageViewSaving = convertView.findViewById(R.id.imageViewSaving);
+ final ImageView imageView_connected = convertView.findViewById(R.id.imageViewConnected);
+ BatteryView batteryView = convertView.findViewById(R.id.batteryViewId);
+
+ textView_name.setText(device.getName());
+ textView_id.setText(device.getId());
+ batteryView.setBatteryLevel(0);
+
+ if (device.isConnected()) {
+ int batteryLevel = device.getBatteryLevel();
+ if (batteryLevel > 0) {
+ batteryView.setBatteryLevel(batteryLevel);
+ batteryView.setColorFilter(Constants.COLOR.JADE);
+ }
+ imageView_scanned.setColorFilter(Constants.COLOR.JADE);
+ imageView_connected.setColorFilter(Constants.COLOR.JADE);
+ imageView_connected.clearAnimation();
+ } else if (device.isScanned()) {
+ imageView_scanned.setColorFilter(Constants.COLOR.JADE);
+ imageView_connected.setColorFilter(Color.DKGRAY);
+ batteryView.setColorFilter(Color.DKGRAY);
+ } else {
+ imageView_scanned.setColorFilter(Color.DKGRAY);
+ imageView_connected.setColorFilter(Color.DKGRAY);
+ batteryView.setColorFilter(Color.DKGRAY);
+ textView_id.setText("N.D.");
+ }
+ imageViewSaving.setColorFilter(Color.DKGRAY);
+ for (Session session : ServiceManager.sessionsList){
+ if(session.getID().equals(device.getId())){
+ if (session.isActive()){
+ imageViewSaving.setColorFilter(Constants.COLOR.JADE);
+ break;
+
+ }else{
+ imageViewSaving.setColorFilter(Color.DKGRAY);
+ break;
+
+ }
+ }
+ }
+ convertView.setOnLongClickListener(v -> {
+ ServiceManager.startActionStartDisconnect(v.getContext(), device.getId(), device.getManagerName());
+ Log.d(TAG, "Asked for the disconnection of: " + device.getName() + " since the user long pressed its view and it is a connected device");
+ Toast.makeText(v.getContext(), getString(R.string.disconnection_called), Toast.LENGTH_SHORT).show();
+ return true;
+
+ });
+
+ convertView.setOnClickListener(v -> {
+ if (device.isConnected()) {
+ ServiceManager.startActionReadBatteryLevel(MainActivity.this, device.getId(), device.getManagerName());
+ Log.d(TAG, "Asked for the battery since the user tapped its view and its is already connected.");
+ Toast.makeText(v.getContext(), device.getName() + getString(R.string.is_already) + getString(R.string.connected), Toast.LENGTH_SHORT).show();
+ }else if (device.isScanned()) {
+ Toast.makeText(v.getContext(), R.string.connecting, Toast.LENGTH_SHORT).show();
+ imageView_connected.setColorFilter(Constants.COLOR.JADE);
+ Animation myBlinkingAnimation = AnimationUtils.loadAnimation(v.getContext(), R.anim.blink);
+ imageView_connected.startAnimation(myBlinkingAnimation);
+ ServiceManager.startActionConnect(v.getContext(), device.getId(), device.getManagerName());
+ Log.d(TAG, "Asked for the connection of: " + device.getName() + " since the user tapped its view and it is a scanned device");
+ }
+ else {
+ Toast.makeText(v.getContext(), R.string.device_not_scanned, Toast.LENGTH_SHORT).show();
+ ServiceManager.startActionScan(v.getContext());
+ Log.d(TAG, "Asked for a scan from the click listener of the view of the unconnected device");
+ }
+ });
+
+ return convertView;
+ }
+ }
+
+ private void updateAndCheck(){
+ ArrayList connectedDevices = ServiceManager.getConnectedDevices();
+ ArrayList listChosenDevices = ServiceManager.getChosenDevices();
+ if (ServiceManager.saving()){
+ fab2.setBackgroundTintList(getResources().getColorStateList(R.color.green));
+ } else{
+ fab2.setBackgroundTintList(getResources().getColorStateList(R.color.red));
+ }
+ if (ServiceManager.saving() && isInForeground) {
+ /* In case we have some connected devices that are not among those currently saved, we sow a popup. */
+ for (Device cd : connectedDevices) {
+ boolean isThere = false;
+ for (Session session : ServiceManager.sessionsList) {
+ if (session.getID().equals(cd.getId())) {
+ if(session.isActive())
+ isThere = true;
+ }
+ }
+ if (!isThere) {
+ ShowPopup();
+ break;
+ }
+ }
+ }
+
+ if (ServiceManager.getAutoSaving() && !ServiceManager.saving()){
+ int numDevices = 0;
+ for (Device cd : connectedDevices) {
+ for (Device device : listChosenDevices) {
+ if (device.getId().equals(cd.getId())) {
+ numDevices += 1;
+ }
+ }
+ if (numDevices == listChosenDevices.size()){
+ Toast.makeText(MainActivity.this, getString(R.string.i_have_all), Toast.LENGTH_SHORT).show();
+ startSaving();
+ Log.d(TAG, "Started saving since I have all the devices I need.");
+ }
+ }
+ }
+
+ /* In case we have some connected devices that are not among those chosen, we disconnect the unneeded ones. */
+ for (Device cd : connectedDevices) {
+ boolean isThere = false;
+ for (Device device : listChosenDevices ) {
+ if (device.getName().contains(cd.getName()) || cd.getName().contains(device.getName())) {
+ isThere = true;
+ }
+ }
+ if (!isThere){
+ ServiceManager.startActionStartDisconnect(this, cd.getId(), cd.getManagerName());
+ Log.d(TAG, "Started action disconnect " + cd.getName() + " since this device is unneeded");
+
+ }
+ }
+
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ switch (requestCode) {
+ case REQUEST_PERMISSION_SERVICE:
+ ServiceManager.onRequestPermissionsResult(requestBroadcastCallback, permissions, grantResults);
+ break;
+ case REQUEST_PERMISSION_ACCESS_COARSE_LOCATION:
+ // If request is cancelled, the result arrays are empty.
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ // Permission was granted, yay!
+ startScanning();
+ Log.d(TAG, "Asked for a scan from onRequestPermissionsResult");
+ } else {
+ // Permission denied, boo!
+ final boolean needRationale = ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_COARSE_LOCATION);
+ new AlertDialog.Builder(this).setTitle("Permission required").setMessage("Without this permission bluetooth low energy devices cannot be found, allow it in order to connect to the device.").setPositiveButton("Retry", (dialog, which) -> {
+ // try again
+ if (needRationale) {
+ // the "never ask again" flash is not set, try again with permission request
+ initBluetoothAuthorizations();
+ } else {
+ // the "never ask again" flag is set so the permission requests is disabled, try open app settings to enable the permission
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ Uri uri = Uri.fromParts("package", getPackageName(), null);
+ intent.setData(uri);
+ startActivity(intent);
+ }
+ }).setNegativeButton("Exit application", (dialog, which) -> {
+ // without permission exit is the only way
+ finish();
+ }).show();
+ }
+ break;
+ }
+ }
+
+ //--- Request Writing permission to add sessions
+ private void initExternalWriteAuthorizations() {
+ // ASK for permissions
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, Constants.PERMISSION_WRITE_EXTERNAL_STORAGE);
+ }
+
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ isInForeground = false;
+ }
+
+ private String getDeviceClasses(String selectedDevices){
+
+ StringBuilder chosenClassNames = new StringBuilder();
+ List deviceNames = Arrays.asList(Constants.DEVICES_NAME);
+ String[] classNames = Constants.DEVICES_CLASSES;
+ int index;
+ for (String name : deviceNames){
+ if(selectedDevices.contains(name)){
+ index = deviceNames.indexOf(name);
+ chosenClassNames.append(classNames[index]);
+ }
+
+ }
+ return chosenClassNames.toString();
+
+ }
+
+ protected void setLoggingHeader() {
+ File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS + File.separator
+ +"LogFromUser");
+ boolean pathIsThere;
+ boolean newly_created = false;
+ if (!path.isDirectory()) {
+ pathIsThere = path.mkdirs();
+ } else {
+ pathIsThere = true;
+ }
+ if (pathIsThere) {
+ boolean fileExist = false;
+ String header = "Phase, start[dd-MM-YYY HH:mm:ss], end[dd-MM-YYY HH:mm:ss]" + "\n";
+ File file = new File(path, this.experimentName + ".txt");
+ if (!file.exists()) {
+ //Create file
+ try {
+ fileExist = file.createNewFile();
+ newly_created = true;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } else {
+ fileExist = true;
+ }
+ if (fileExist && newly_created) {
+ try {
+ FileOutputStream stream = new FileOutputStream(file);
+ try {
+ stream.write(header.getBytes());
+ stream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private void saveDataLogging(String data) {
+ //--- Check if writable
+ if (isExternalStorageWritable()) {
+ File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS + File.separator
+ +"LogFromUser");
+ if (path.isDirectory()) {
+ File file = new File(path, this.experimentName + ".txt");
+
+ //--- check if it exist
+ if (file.exists()) {
+ FileOutputStream outputStream;
+ StringBuilder string = new StringBuilder();
+ //--- Start to print
+ try {
+ outputStream = new FileOutputStream(file, true);
+ string.append(data);
+ String endLine = "\n";
+ string.append(endLine);
+ byte[] strToBytes = string.toString().getBytes();
+ outputStream.write(strToBytes);
+ outputStream.close();
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+
+ //--- Function to export data to CSV file
+ /* Checks if external storage is available for read and write */
+ private boolean isExternalStorageWritable() {
+ String state = Environment.getExternalStorageState();
+ return Environment.MEDIA_MOUNTED.equals(state);
+ }
+
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Monitor.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Monitor.java
new file mode 100644
index 0000000..e49003d
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Monitor.java
@@ -0,0 +1,154 @@
+package ch.epfl.esl.elevatedmonitor;
+
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.graphics.Color;
+import android.os.Handler;
+import android.os.IBinder;
+
+import com.jjoe64.graphview.series.DataPoint;
+import com.jjoe64.graphview.series.LineGraphSeries;
+
+import java.util.ArrayList;
+
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Data;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Device;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+
+public class Monitor {
+ private final int WINDOW_TIME = 10;
+ private Runnable mTimer;
+ private Double time = 0.0;
+ private long delay = 0;
+ private int maxDataPoints = 0;
+ private final Handler mHandler = new Handler();
+ private Device monitoredDevice = null;
+ private SensorDevice monitoredSensor = null;
+ private Context mContext;
+ private Boolean bound;
+ private ArrayList> series = new ArrayList<>();
+ Service sensorService = null;
+
+ public Monitor(Device monitoredDevice, SensorDevice monitoredSensor, Context mContext) {
+ setContext(mContext);
+ setMonitoredDevice(monitoredDevice);
+ setMonitoredSensor(monitoredSensor);
+
+ setDelay((long) 100.0);
+ setMaxDataPoints();
+ setSeries();
+ //--- Set timer
+ time = 0.0;
+ mTimer = new Runnable() {
+ @Override
+ public void run() {
+ updateSeries();
+ time += ((double) delay / 1000.0);
+ mHandler.postDelayed(this, delay);
+ }
+ };
+ mHandler.postDelayed(mTimer, delay);
+ }
+
+ private void setMaxDataPoints() {
+ this.maxDataPoints = (int) ((WINDOW_TIME * 1000) / delay);
+ }
+
+ private void setDelay(long delay) {
+ this.delay = delay;
+ }
+
+ private void setSeries() {
+ int position = 0;
+ series.clear();
+ for (Data data : monitoredSensor.getAllData()) {
+ series.add(new LineGraphSeries<>());
+ series.get(position).setColor(getColor(position));
+ position++;
+ }
+ updateSeries();
+ }
+
+ private int getColor(int position) {
+ int color;
+ position = position % 7;
+ switch (position) {
+ case 0:
+ color = Color.BLUE;
+ break;
+ case 1:
+ color = Color.RED;
+ break;
+ case 2:
+ color = Color.GREEN;
+ break;
+ case 3:
+ color = Color.YELLOW;
+ break;
+ case 4:
+ color = Color.CYAN;
+ break;
+ case 5:
+ color = Color.MAGENTA;
+ break;
+ case 6:
+ color = Color.GRAY;
+ break;
+ default:
+ color = Color.BLUE;
+ }
+ return color;
+ }
+
+ private void updateSeries() {
+ int position = 0;
+ for (Data data : monitoredSensor.getAllData()) {
+ LineGraphSeries serie = series.get(position);
+
+ serie.appendData(new DataPoint(this.time, data.alwaysGet()), true, maxDataPoints);
+
+ position++;
+ }
+ }
+
+ private void setContext(Context context) {
+ this.mContext = context;
+ }
+
+ private void setMonitoredSensor(SensorDevice monitoredSensor) {
+ this.monitoredSensor = monitoredSensor;
+ }
+
+ private void setMonitoredDevice(Device monitoredDevice) {
+ this.monitoredDevice = monitoredDevice;
+ }
+
+ Device getMonitoredDevice() {
+ return this.monitoredDevice;
+ }
+
+ SensorDevice getMonitoredSensor() {
+ // mContext.bindService(intent, DeviceServiceConnection , Context.BIND_AUTO_CREATE);
+ return this.monitoredSensor;
+ }
+
+ public ArrayList> getSeries() {
+ return this.series;
+ }
+
+ private class DeviceServiceConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ // We've bound to LocalService, cast the IBinder and get LocalService instance
+ //sensorService = service.getService();
+ bound = true;
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName arg0) {
+ bound = false;
+ }
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/MonitoringActivity.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/MonitoringActivity.java
new file mode 100644
index 0000000..831b9eb
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/MonitoringActivity.java
@@ -0,0 +1,169 @@
+package ch.epfl.esl.elevatedmonitor;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.design.widget.FloatingActionButton;
+import android.support.design.widget.NavigationView;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.jjoe64.graphview.GraphView;
+import com.jjoe64.graphview.Viewport;
+import com.jjoe64.graphview.series.DataPoint;
+import com.jjoe64.graphview.series.LineGraphSeries;
+
+import java.util.ArrayList;
+
+public class MonitoringActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
+ private static ArrayList listMonitor = new ArrayList<>();
+ private static MonitorAdapter monitorAdapter;
+ private NavigationView navigationView;
+
+
+ private void showAddMonitor() {
+ // Create and show the dialog.
+ AddMonitorFragment addMonitorFragment = AddMonitorFragment.newInstance();
+ addMonitorFragment.show(getSupportFragmentManager(), "addMonitorDialog");
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_monitoring);
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ FloatingActionButton fab = findViewById(R.id.fab);
+ fab.setOnClickListener(view -> showAddMonitor());
+
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
+ drawer.addDrawerListener(toggle);
+ toggle.syncState();
+
+ navigationView = findViewById(R.id.nav_view);
+ navigationView.setNavigationItemSelectedListener(this);
+
+ //----------------------------------------------------------------------------------------//
+ //--- List view
+ //----------------------------------------------------------------------------------------//
+ monitorAdapter = new MonitorAdapter();
+
+ ListView monitorListView = findViewById(R.id.list_view_monitor);
+
+ monitorListView.setAdapter(monitorAdapter);
+ }
+
+ @Override
+ public void onBackPressed() {
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ if (drawer.isDrawerOpen(GravityCompat.START)) {
+ drawer.closeDrawer(GravityCompat.START);
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.monitoring, menu);
+ return true;
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ navigationView.setCheckedItem(R.id.nav_monitoring);
+ }
+
+ @Override
+ public boolean onNavigationItemSelected(@NonNull MenuItem item) {
+ // Handle navigation view item clicks here.
+ int id = item.getItemId();
+
+ if (id == R.id.nav_settings) {
+ Intent settingActivity = new Intent(this, SettingsActivity.class);
+ startActivity(settingActivity);
+ } else if (id == R.id.auto_connect) {
+ Intent mainActivity = new Intent(this, MainActivity.class);
+ startActivity(mainActivity);
+ }
+
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ drawer.closeDrawer(GravityCompat.START);
+ return true;
+ }
+
+ public static void notifyAdapterDataSetChanged() {
+ monitorAdapter.notifyDataSetChanged();
+ }
+
+ public static void addMonitor(Monitor monitor) {
+ listMonitor.add(monitor);
+ }
+
+ class MonitorAdapter extends BaseAdapter {
+
+ @Override
+ public int getCount() {
+ return listMonitor.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return listMonitor.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return 0;
+ }
+
+ @Override
+ public View getView(final int position, View convertView, ViewGroup parent) {
+ View view = getLayoutInflater().inflate(R.layout.list_monitor, null);
+
+ TextView textViewMonitorSensor = view.findViewById(R.id.textViewSessionName);
+ TextView textViewMonitorDevice = view.findViewById(R.id.textViewMonitorDevice);
+ GraphView graph = view.findViewById(R.id.graphMonitor);
+ Button buttonDeleteGraph = view.findViewById(R.id.buttonDeleteGraph);
+
+ //--- Viewport
+ Viewport vp = graph.getViewport();
+ vp.setXAxisBoundsManual(true);
+ vp.setMinX(0);
+ vp.setMaxX(10);
+
+
+ Monitor monitor = listMonitor.get(position);
+
+ textViewMonitorSensor.setText(monitor.getMonitoredSensor().getName());
+ textViewMonitorDevice.setText(monitor.getMonitoredDevice().getName());
+ for (LineGraphSeries serie : monitor.getSeries()) {
+ graph.addSeries(serie);
+ }
+
+ buttonDeleteGraph.setOnClickListener(v -> {
+ System.out.print("Click to delete");
+ listMonitor.remove(position);
+ monitorAdapter.notifyDataSetChanged();
+ });
+
+ return view;
+
+ }
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/PermissionActivity.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/PermissionActivity.java
new file mode 100644
index 0000000..a767fc5
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/PermissionActivity.java
@@ -0,0 +1,68 @@
+package ch.epfl.esl.elevatedmonitor;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.text.InputType;
+import android.widget.EditText;
+import android.widget.Toast;
+
+public class PermissionActivity extends AppCompatActivity {
+ String password = "ciao";
+
+
+ boolean goBack = true;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_permission);
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+ toolbar.setTitle("Permission Required");
+
+ AlertDialog alertDialog = new AlertDialog.Builder(PermissionActivity.this).create();
+ alertDialog.setTitle(getString(R.string.psw_required));
+ alertDialog.setMessage(getString(R.string.insert_psw));
+ // Set up the input
+ final EditText confirmPassword = new EditText(this);
+ // Specify the type of input expected as a password
+ confirmPassword.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ alertDialog.setView(confirmPassword);
+ alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, getString(R.string.dismiss),
+ (dialog, which) -> {
+ dialog.dismiss();
+ Intent mainActivity = new Intent(this, MainActivity.class);
+ startActivity(mainActivity);
+ goBack = false;
+ });
+ alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.go_to_setting),
+ (dialog, which) -> {
+ String inserted = confirmPassword.getText().toString();
+ if(password.equals(inserted)) {
+ dialog.dismiss();
+ Intent settingActivity = new Intent(this, SettingsActivity.class);
+ startActivity(settingActivity);
+ goBack = false;
+ }
+ else{
+ Toast.makeText(this, getString(R.string.wrong_password), Toast.LENGTH_LONG).show();
+ Intent permissionActivity = new Intent(this, PermissionActivity.class);
+ startActivity(permissionActivity);
+ goBack = false;
+
+ }
+ });
+ alertDialog.show();
+
+ alertDialog.setOnDismissListener(dialog -> {
+ if (goBack) {
+ Intent mainActivity = new Intent(PermissionActivity.this, MainActivity.class);
+ startActivity(mainActivity);
+ }
+ });
+ }
+
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/RecyclerViewAdapter.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/RecyclerViewAdapter.java
new file mode 100644
index 0000000..8d3ab9a
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/RecyclerViewAdapter.java
@@ -0,0 +1,100 @@
+package ch.epfl.esl.elevatedmonitor;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.constraint.ConstraintLayout;
+import android.support.design.widget.Snackbar;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.TextView;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+
+public class RecyclerViewAdapter extends RecyclerView.Adapter {
+
+ private final Context context;
+ private final JSONArray mData;
+
+ RecyclerViewAdapter(Context context, JSONArray choices) {
+ this.context = context;
+ mData = choices;
+ }
+
+ @NonNull
+ @Override
+ public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View view;
+ LayoutInflater mInflater = LayoutInflater.from(context);
+ view = mInflater.inflate(R.layout.question_card, parent, false);
+ return new MyViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
+ holder.name.setText(((JSONObject) mData.get(position)).getString("choice_en"));
+
+ JSONObject help = ((JSONObject) mData.get(position)).getJSONObject("help");
+ if (help != null) {
+ holder.infoBtn.setOnClickListener(view -> {
+ Snackbar bar = Snackbar.make(holder.infoBtn, help.getString("en"), 6000);
+ ((TextView) bar.getView().findViewById(android.support.design.R.id.snackbar_text)).setMaxLines(8);
+ // ((TextView) bar.getView().findViewById(com.google.android
+ // .material.R.id
+ // .snackbar_text)).setMaxLines(8);
+ bar.setAction(R.string.close, view1 -> bar.dismiss());
+ bar.show();
+ });
+ } else {
+ holder.infoBtn.setVisibility(View.INVISIBLE);
+ }
+
+ // If the item was previously checked and then destroyed, restore its checked state
+ ConstraintLayout layout = holder.itemView.findViewById(R.id.card_layout);
+ Boolean initially_checked = ((JSONObject) mData.get(position)).getBoolean("checked");
+ if (initially_checked == null) {
+ initially_checked = false;
+ }
+ ((JSONObject) mData.get(position)).put("checked", initially_checked);
+ if (initially_checked) {
+ layout.setBackgroundColor(holder.itemView.getContext().getResources().getColor(R.color.colorPrimary));
+ }
+
+ // Make the buttons react to taps
+ int initialColor = layout.getSolidColor();
+ layout.setOnClickListener(v -> {
+ Boolean checked = ((JSONObject) mData.get(position)).getBoolean("checked");
+ checked = !checked;
+ ((JSONObject) mData.get(position)).put("checked", checked);
+ if (checked) {
+ v.setBackgroundColor(holder.itemView.getContext().getResources().getColor(R.color.colorPrimary));
+ } else {
+ v.setBackgroundColor(holder.itemView.getContext().getResources().getColor(R.color.colorPrimary));
+ v.setBackgroundColor(initialColor);
+ }
+ });
+
+ }
+
+ @Override
+ public int getItemCount() {
+ return mData.size();
+ }
+
+ static class MyViewHolder extends RecyclerView.ViewHolder {
+
+ private final TextView name;
+ private final ImageButton infoBtn;
+
+ MyViewHolder(View itemView) {
+ super(itemView);
+
+ name = itemView.findViewById(R.id.choice_name);
+ infoBtn = itemView.findViewById(R.id.infoButton);
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/SavingActivity.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/SavingActivity.java
new file mode 100644
index 0000000..0aac441
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/SavingActivity.java
@@ -0,0 +1,264 @@
+package ch.epfl.esl.elevatedmonitor;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Environment;
+import android.support.annotation.NonNull;
+import android.support.design.widget.FloatingActionButton;
+import android.support.design.widget.NavigationView;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.CheckBox;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.ArrayList;
+
+
+public class SavingActivity extends AppCompatActivity implements ActivityCompat.OnRequestPermissionsResultCallback, NavigationView.OnNavigationItemSelectedListener {
+ public static ArrayList sessionsList = new ArrayList<>();
+ public static SessionAdapter sessionAdapter;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_save);
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ FloatingActionButton fab = findViewById(R.id.add_session);
+ fab.setOnClickListener(view -> {
+ // Add a new session
+ if (ServiceManager.getConnectedDevices().size() > 0) {
+ initExternalWriteAuthorizations();
+ } else {
+ Snackbar.make(view, R.string.connect_device_before, Snackbar.LENGTH_SHORT).show();
+ }
+ });
+
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
+ drawer.addDrawerListener(toggle);
+ toggle.syncState();
+
+ NavigationView navigationView = findViewById(R.id.nav_view);
+ navigationView.setNavigationItemSelectedListener(this);
+
+ //--- init list of sessions
+ initExternalReadAuthorizations();
+
+ sessionAdapter = new SessionAdapter();
+ ListView sessionListView = findViewById(R.id.list_view_session);
+ sessionListView.setAdapter(sessionAdapter);
+
+ }
+
+ @Override
+ public void onBackPressed() {
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ if (drawer.isDrawerOpen(GravityCompat.START)) {
+ drawer.closeDrawer(GravityCompat.START);
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.save, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onNavigationItemSelected(@NonNull MenuItem item) {
+ // Handle navigation view item clicks here.
+ int id = item.getItemId();
+
+ if (id == R.id.nav_settings) {
+ Intent settingActivity = new Intent(this, SettingsActivity.class);
+ startActivity(settingActivity);
+ } else if (id == R.id.auto_connect) {
+ Intent mainActivity = new Intent(this, MainActivity.class);
+ startActivity(mainActivity);
+ } else if (id == R.id.nav_monitoring) {
+ Intent monitoringActivity = new Intent(this, MonitoringActivity.class);
+ startActivity(monitoringActivity);
+ }
+
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ drawer.closeDrawer(GravityCompat.START);
+ return true;
+ }
+
+
+ public void updateListSession() {
+ if (isExternalStorageReadable()) {
+ //First remove all inactive sessions
+ if (sessionsList.size() > 0) {
+ ArrayList activeSessionsList = new ArrayList<>();
+ for (Session session : sessionsList) {
+ if (session.isActive()) {
+ activeSessionsList.add(session);
+ }
+ }
+ sessionsList.clear();
+ sessionsList.addAll(activeSessionsList);
+ }
+
+ //Add inactive sessions from the file system.
+ File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
+ if (path.isDirectory()) {
+ class SessionFilter implements FilenameFilter {
+ @Override
+ public boolean accept(File dir, String name) {
+ return name.indexOf("Session") == 0;
+ }
+ }
+ File[] fileList = path.listFiles(new SessionFilter());
+ for (File file : fileList) {
+ try {
+ Session newSession = new Session(file.getName());
+ boolean flagNotContains = false;
+ for (Session session : sessionsList) {
+ if (session.getSessionName().equals(newSession.getSessionName())) {
+ flagNotContains = true;
+ break;
+ }
+ }
+ if (flagNotContains) {
+ sessionsList.add(newSession);
+ }
+ } catch (Session.SessionException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ if (sessionAdapter != null) {
+ sessionAdapter.notifyDataSetChanged();
+ }
+ }
+ }
+
+ //--- Request Read permission to updateListSession
+ private void initExternalReadAuthorizations() {
+ // ASK for permissions
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, Constants.PERMISSION_READ_EXTERNAL_STORAGE);
+ } else {
+ updateListSession();
+ }
+ }
+
+ //--- Request Writing permission to add sessions
+ private void initExternalWriteAuthorizations() {
+ // ASK for permissions
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, Constants.PERMISSION_WRITE_EXTERNAL_STORAGE);
+ } else {
+ showAddSession();
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
+ switch (requestCode) {
+ case Constants.PERMISSION_WRITE_EXTERNAL_STORAGE: {
+ // If request is cancelled, the result arrays are empty.
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ showAddSession();
+ }
+ return;
+ }
+ case Constants.PERMISSION_READ_EXTERNAL_STORAGE: {
+ // If request is cancelled, the result arrays are empty.
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ updateListSession();
+ }
+
+ }
+ }
+ }
+
+ //--- Adding session
+ private void showAddSession() {
+ // Create and show the dialog.
+ AddSessionFragment addSessionFragment = AddSessionFragment.newInstance();
+ addSessionFragment.show(getSupportFragmentManager(), "addSessionDialog");
+ }
+
+ //--- Adapter
+ public class SessionAdapter extends BaseAdapter {
+
+ @Override
+ public int getCount() {
+ return sessionsList.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return sessionsList.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return 0;
+ }
+
+ @Override
+ public View getView(final int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ LayoutInflater vi = (LayoutInflater) SavingActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ convertView = vi.inflate(R.layout.list_session, parent, false);
+ }
+
+ TextView nameText = convertView.findViewById(R.id.textViewSessionName);
+ TextView dateText = convertView.findViewById(R.id.textViewDateValue);
+ final CheckBox activity = convertView.findViewById(R.id.checkBoxActivity);
+
+ nameText.setText(sessionsList.get(position).getName());
+ dateText.setText(sessionsList.get(position).getDate());
+
+ if (sessionsList.get(position).isActive()) {
+ activity.setChecked(true);
+ } else {
+ activity.setChecked(false);
+ activity.setEnabled(false);
+ }
+ activity.setOnClickListener(v -> {
+ if (!activity.isChecked()) {
+ activity.setChecked(false);
+ activity.setEnabled(false);
+ sessionsList.get(position).stopSession();
+ }
+ });
+
+ return convertView;
+ }
+ }
+
+ /* Checks if external storage is available to at least read */
+ private boolean isExternalStorageReadable() {
+ String state = Environment.getExternalStorageState();
+ return Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state);
+ }
+
+
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/SavingFragment.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/SavingFragment.java
new file mode 100644
index 0000000..bc4584f
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/SavingFragment.java
@@ -0,0 +1,108 @@
+package ch.epfl.esl.elevatedmonitor;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.support.v4.app.DialogFragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+
+public class SavingFragment extends DialogFragment {
+ private static final String TAG = "Saving Fragment";
+
+ //--- Service Manager
+ private ServiceManagerConnection serviceManagerConnection = null;
+ private Boolean serviceManagerBound = false;
+
+ public SavingFragment() {
+ // Required empty public constructor
+ }
+
+
+ public static SavingFragment newInstance() {
+ return new SavingFragment();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ View view = inflater.inflate(R.layout.fragment_saving, container, false);
+ Button btnAddNewDevices;
+ Button btnIgnore;
+ btnIgnore = view.findViewById(R.id.btnignore);
+ btnIgnore.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ MainActivity.showingPopout = false;
+ dismiss();
+ }
+ });
+ btnAddNewDevices = view.findViewById(R.id.btnadd);
+ btnAddNewDevices.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ ServiceManager.startActionSave(v.getContext());
+ MainActivity.showingPopout = false;
+ dismiss();
+ }
+ });
+ return view;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ //--- Start a new connection
+ if (serviceManagerConnection == null) {
+ serviceManagerConnection = new ServiceManagerConnection();
+ }
+
+ //------------------------------ Bind to LocalService ------------------------------------//
+ Intent serviceManagerIntent = new Intent(this.getActivity(), ServiceManager.class);
+ if (!serviceManagerBound) {
+ this.getActivity().bindService(serviceManagerIntent, serviceManagerConnection, Context.BIND_AUTO_CREATE);
+ }
+ }
+
+ private class ServiceManagerConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ // We've bound to LocalService, cast the IBinder and get LocalService instance
+ ServiceManager.ServiceManagerBinder binder = (ServiceManager.ServiceManagerBinder) service;
+ ServiceManager.serviceManager = binder.getService();
+ serviceManagerBound = true;
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName arg0) {
+ serviceManagerBound = false;
+ }
+ }
+
+
+ @Override
+ public void onStop() {
+ if (serviceManagerBound) {
+ this.getActivity().unbindService(serviceManagerConnection);
+ }
+ super.onStop();
+ }
+
+ @Override
+ public void onSaveInstanceState( Bundle outState ) {
+
+ }
+
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/ServiceManager.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/ServiceManager.java
new file mode 100644
index 0000000..7187324
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/ServiceManager.java
@@ -0,0 +1,930 @@
+package ch.epfl.esl.elevatedmonitor;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+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.mbientlab.metawear.android.BtleService;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+
+import ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.EmpaticaManager;
+import ch.epfl.esl.elevatedmonitor.Devices.MetaMotionR.MetaWear;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Device;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Manager;
+
+//import ch.epfl.esl.elevatedmonitor.Devices.Microsoft_Band_2.BandManager;
+
+public class ServiceManager extends Service implements ServiceConnection {
+ public static ServiceManager serviceManager = null;
+ // private static BluetoothScanReceiver bluetoothScanReceiver = null;
+ private static final int REQUEST_PERMISSION_ACCESS_COARSE_LOCATION = 1;
+ private static String choosenDevString = null;
+ private BluetoothBroadcast bluetoothBroadcast = new BluetoothBroadcast();
+
+ public static boolean askingPermission = false;
+
+ public static String EMPATICA_API_KEY = null; //"7e9408f4b71e4b72b3a213ba3d098745";
+
+ //------------------------------------- CONSTANT ---------------------------------------------//
+ private static final String TAG_FOREGROUND_SERVICE = "ServiceManager SQUINCI";
+
+ //--- Notif
+ private static final int MAIN_NOTIFICATION_ID = 1;
+ private static final int PERSISTENT_NOTIFICATION_ID = 300;
+
+ //--- Channel
+ public static final String CHANNEL_NONE = "ManagerService.CHANNEL.NONE";
+ public static final String CHANNEL_DEFAULT = "ManagerService.CHANNEL.DEFAULT";
+ public static final String CHANNEL_HIGH = "ManagerService.CHANNEL.HIGH";
+
+ //--- Action that Service can perform, e.g. ACTION_FETCH_NEW_ITEMS
+ public static final String ACTION_PERMISSION_MICROSOFT = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.PERMISSION_MICROSOFT";
+ public static final String ACTION_UPDATE_PERMISSION_MICROSOFT = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.UPDATE_PERMISSION_MICROSOFT";
+ public static final String ACTION_SCAN = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.SCAN";
+ public static final String ACTION_SCANNED = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.SCANNED";
+ public static final String ACTION_STOP_SCAN = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.STOP_SCAN";
+ public static final String ACTION_CONNECT = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.ACTION_CONNECT";
+ public static final String EXTRA_ID = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.EXTRA_ID";
+ public static final String EXTRA_CLASS_NAME = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.EXTRA_CLASS_NAME";
+ public static final String ACTION_CONNECTED = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.ACTION_CONNECTED";
+ public static final String ACTION_TOGGLE_MICROSOFT = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.ACTION_TOGGLE_MICROSOFT";
+ public static final String ACTION_STOP_ONE_SAVE = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.ACTION_STOP_ONE_SAVE";
+ public static final String ACTION_START_DISCONNECT = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.ACTION_START_DISCONNECT";
+ public static final String ACTION_BATTERY = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.ACTION_BATTERY";
+ public static final String ACTION_BATTERY_READ = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.ACTION_BATTERY_READ";
+ public static final String ACTION_SAVE = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.ACTION_SAVE";
+ private static final String ACTION_DISCONNECT_EMPATICA = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.ACTION_DISCONNECT_EMPATICA";
+
+
+ public static final String ACTION_ERROR_CONNECT = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.ACTION_ERROR_CONNECT";
+ public static final String EXTRA_ERROR = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.EXTRA_ERROR";
+
+ public static final String ACTION_DISCONNECT = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.ACTION_DISCONNECT";
+ public static final String EXTRA_NAME = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.EXTRA_NAME";
+ public static final String EXTRA_UNEXPECTED = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.EXTRA_UNEXPECTED";
+ public static final String ACTION_NOTIFY = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.ACTION_NOTIFY";
+ public static final String EXTRA_TEXT = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.EXTRA_TEXT";
+ public static final String EXTRA_CHANNEL = "ch.epfl.esl.elevatedmonitor.ServiceManager.action.EXTRA_CHANNEL";
+
+ //--- Status into the Intent, e.g. STATUS_CONNECTED
+ public static final String STATUS_SCANNED = "ch.epfl.esl.elevatedmonitor.Devices.Phone.status.SCANNED";
+ public static final String STATUS_CONNECTED = "ch.epfl.esl.elevatedmonitor.Devices.Phone.status.CONNECTED";
+ public static final String STATUS_ERROR_CONNECTION = "ch.epfl.esl.elevatedmonitor.Devices.Phone.status.ERROR_CONNECTION";
+ public static final String STATUS_CONNECTING = "ch.epfl.esl.elevatedmonitor.Devices.Phone.status.CONNECTING";
+ public static final String STATUS_DISCONNECTED = "ch.epfl.esl.elevatedmonitor.Devices.Phone.status.DISCONNECTED";
+ public static final String STATUS_PERMISSION_MICROSOFT = "ch.epfl.esl.elevatedmonitor.Devices.Phone.status.PERMISSION_MICROSOFT";
+ public static final String STATUS_STOP_ONE_SAVE = "ch.epfl.esl.elevatedmonitor.Devices.Phone.status.STATUS_STOP_ONE_SAVE";
+ public static final String SATUS_BATTERY_READ = "ch.epfl.esl.elevatedmonitor.Devices.Phone.status.SATUS_BATTERY_READ";
+ public static final String STATUS_SAVE = "ch.epfl.esl.elevatedmonitor.Devices.Phone.status.STATUS_SAVE";
+
+ //--- Binder given to clients
+ private final IBinder mBinder = new ServiceManagerBinder();
+
+ private static BtleService.LocalBinder serviceBinder;
+
+ private static BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+
+ //---------------------------------------- VARIABLES -----------------------------------------//
+ private static ArrayList managerDevices = new ArrayList<>();
+ public static ArrayList sessionsList = new ArrayList<>();
+ public static ArrayList deviceList = new ArrayList<>();
+ private boolean serviceBLEBound = false;
+ public static boolean autoSaving = true;
+ public static String Usage = "";
+
+ //--------------------------------------- Constuctor -----------------------------------------//
+ public ServiceManager() {
+ super();
+
+ }
+
+ public static boolean saving(){
+ return sessionsList.size() > 0;
+ }
+
+ public static void scanBluetooth(){
+
+ if (mBluetoothAdapter != null) {
+ if (!mBluetoothAdapter.isEnabled()) {
+ if (!askingPermission) {
+ //--- Request Bluetooth right
+ askingPermission = true;
+ Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+ ServiceManager.startActivityForResult(enableBtIntent, Constants.REQUEST_ENABLE_BT);
+ }
+ } else {
+ mBluetoothAdapter.startDiscovery();
+ }
+ }
+ }
+
+ public static void stopScanBluetooth(){
+ mBluetoothAdapter.cancelDiscovery();
+ }
+
+ public static BluetoothDevice getBLEDevice(String deviceID){
+ return mBluetoothAdapter.getRemoteDevice(deviceID);
+ }
+
+/*
+ public static void handleActionPermissionMicrosoft() {
+ Intent localIntent = new Intent(Constants.BROADCAST_STATUS)
+ // Puts the status into the Intent
+ .putExtra(Constants.EXTENDED_DATA_STATUS, STATUS_PERMISSION_MICROSOFT);
+ // Broadcasts the Intent to receivers in this app.
+ LocalBroadcastManager.getInstance(serviceManager).sendBroadcast(localIntent);
+ }
+
+ public static void handleActionToggleMicrosoft() {
+ System.out.print("Toggle");
+ for (Manager manager : managerDevices) {
+ if (manager.getClass().getName().equals(BandManager.class.getName())) {
+ ((BandManager) manager).toggle();
+ }
+ }
+ }*/
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ //-------------------------------- Microsoft Activity ----------------------------------------//
+ public static void startActionPermissionMicrosoft(Context context) {
+ Intent intent = new Intent(context, ServiceManager.class);
+ intent.setAction(ACTION_PERMISSION_MICROSOFT);
+ context.startService(intent);
+ }
+/*
+ public static void handleActionPermissionUpdateMicrosoft() {
+ for (Manager manager : managerDevices) {
+ if (manager.getClass().getName().equals(BandManager.class.getName())) {
+ ((BandManager) manager).updatePermissionMicrosoft();
+ }
+ }
+ }*/
+
+ public static void save(String id){
+ for (Session session : ServiceManager.sessionsList){
+ if (session.getID().equals(id)){
+ session.startSession();
+ break;
+ }
+ }
+ }
+
+ public static void startActionPermissionUpdateMicrosoft(Context context) {
+ Intent intent = new Intent(context, ServiceManager.class);
+ intent.setAction(ACTION_UPDATE_PERMISSION_MICROSOFT);
+ context.startService(intent);
+ }
+
+ public static void startActionToggleMicrosoft(Context context) {
+ Intent intent = new Intent(context, ServiceManager.class);
+ intent.setAction(ACTION_TOGGLE_MICROSOFT);
+ context.startService(intent);
+ }
+
+ //----------------------------------- BUTTON Part --------------------------------------------//
+ public static void startActionNotify(Context context, String text, String channel) {
+ Intent intent = new Intent(context, ServiceManager.class);
+ intent.setAction(ACTION_NOTIFY);
+ intent.putExtra(EXTRA_TEXT, text);
+ intent.putExtra(EXTRA_CHANNEL, channel);
+ context.startService(intent);
+ }
+
+ //-------------------------------- REQUEST Activity ------------------------------------------//
+ public static void requestPermissions(String permission, String callback) {
+ Intent localIntent = new Intent(Constants.REQUEST_PERMISSION_STATUS).putExtra(Constants.REQUEST_PERMISSION_NAME, permission)
+ .putExtra(Constants.REQUEST_PERMISSION_CALLBACK, callback);
+ // Broadcasts the Intent to receivers in this app.
+ LocalBroadcastManager.getInstance(serviceManager).sendBroadcast(localIntent);
+ }
+
+ //-------------------------------- REQUEST Activity ------------------------------------------//
+ public static void startActivityForResult(Intent intent, String managerName) {
+ //Send action as a string and callback context.
+ //
+ String action = intent.getAction();
+ Intent localIntent = new Intent(Constants.START_ACTIVITY_STATUS).putExtra(Constants.START_ACTIVITY_INTENT, intent
+ .getAction()).putExtra(Constants.START_ACTIVITY_CALLBACK, managerName);
+ // Broadcasts the Intent to receivers in this app.
+ LocalBroadcastManager.getInstance(serviceManager).sendBroadcast(localIntent);
+ }
+
+ public static void onRequestPermissionsResult(String callback, String[] permissions, int[] grantResults) {
+ if (callback.equals(ACTION_SCAN)) {
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ // Permission was granted, yay!
+ handleActionScan();
+ }
+ }
+ }
+
+ public static void onActivityResult(String requestCode, int resultCode, String callback) {
+ if (callback.equals(ACTION_SCAN)) {
+ handleActionScan();
+ } else {
+ for (final Manager manager : managerDevices) {
+ System.out.println(manager.getClass().getName());
+ if (manager.getClass().getName().equals(callback)) {
+ manager.onActivityResult(requestCode, resultCode);
+ break;
+ }
+ }
+ }
+ }
+
+ public static void handleActionNotify(String text, String channel) {
+ //Notify the user via notification
+
+ NotificationManager notificationManager = (NotificationManager) serviceManager.getSystemService(NOTIFICATION_SERVICE);
+ Intent notificationIntent = new Intent(serviceManager, MainActivity.class);
+ PendingIntent pendingIntent = PendingIntent.getActivity(serviceManager, 0, notificationIntent, 0);
+ Notification notification = new NotificationCompat.Builder(serviceManager, channel)
+ .setContentTitle(serviceManager
+ .getText(R.string.notification_title))
+ .setContentIntent(pendingIntent)
+ .setContentText(text)
+ .setSmallIcon(R.drawable.ic_notif_monitoring)
+ .setAutoCancel(true)
+ .setTimeoutAfter(50000) // time in ms before dismissing the notification
+ .build();
+
+ notificationManager.notify(MAIN_NOTIFICATION_ID, notification);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (intent != null) {
+ final String action = intent.getAction();
+ if (ACTION_SCAN.equals(action)) {
+ handleActionScan();
+
+ } else if (ACTION_SCANNED.equals(action)) {
+ handleActionScanned();
+
+ } else if (ACTION_STOP_SCAN.equals(action)) {
+ handleActionStopScan();
+
+ } else if (ACTION_CONNECT.equals(action)) {
+ handleActionConnect(intent.getStringExtra(EXTRA_ID), intent.getStringExtra(EXTRA_CLASS_NAME));
+
+ } else if (ACTION_CONNECTED.equals(action)) {
+ handleActionConnected();
+
+ } else if (ACTION_DISCONNECT.equals(action)) {
+ handleActionDisconnect(intent.getStringExtra(EXTRA_NAME));
+
+ } else if (ACTION_DISCONNECT_EMPATICA.equals(action)) {
+ handleActionDisconnectEmpatica(intent.getBooleanExtra(EXTRA_UNEXPECTED, true));
+
+ } else if (ACTION_NOTIFY.equals(action)) {
+ handleActionNotify(intent.getStringExtra(EXTRA_TEXT), intent.getStringExtra(EXTRA_CHANNEL));
+
+ } else if (ACTION_ERROR_CONNECT.equals(action)) {
+ handleActionErrorConnect(intent.getStringExtra(EXTRA_ERROR));
+
+ /*}*else if (ACTION_PERMISSION_MICROSOFT.equals(action)) {
+ //handleActionPermissionMicrosoft();
+
+ } else if (ACTION_UPDATE_PERMISSION_MICROSOFT.equals(action)) {
+ //handleActionPermissionUpdateMicrosoft();
+
+ } else if (ACTION_TOGGLE_MICROSOFT.equals(action)) {
+ //handleActionToggleMicrosoft();*/
+ } else if(ACTION_STOP_ONE_SAVE.equals(action)){
+ handleActionStopOneSave(intent.getStringExtra(EXTRA_ID), intent.getStringExtra(EXTRA_NAME));
+
+ } else if (ACTION_START_DISCONNECT.equals(action)){
+ handleActionStartDisconnect(intent.getStringExtra(EXTRA_ID), intent.getStringExtra(EXTRA_CLASS_NAME));
+
+ } else if (ACTION_BATTERY.equals(action)){
+ handleActionBattery(intent.getStringExtra(EXTRA_ID), intent.getStringExtra(EXTRA_CLASS_NAME));
+
+ } else if (ACTION_BATTERY_READ.equals(action)){
+ handleActionBatteryRead();
+
+ } else if (ACTION_SAVE.equals(action)){
+ handleActionSave();
+ }
+
+
+ }
+ // If we get killed, after returning from here, restart
+ return Service.START_STICKY;
+ }
+
+ private void handleActionBatteryRead() {
+ Intent localIntent = new Intent(Constants.BROADCAST_STATUS)
+ // Puts the status into the Intent
+ .putExtra(Constants.EXTENDED_DATA_STATUS, SATUS_BATTERY_READ);
+ // Broadcasts the Intent to receivers in this app.
+ LocalBroadcastManager.getInstance(serviceManager).sendBroadcast(localIntent);
+ }
+
+ private void handleActionBattery(String id, String className) {
+ // Get manager
+ for (final Manager manager : managerDevices) {
+ if (manager.getClass().getName().equals(className)) {
+ //Connect to the device with the right manager
+ //new Thread
+ Runnable readBatteryLevel = () -> manager.readBatteryLevel(id);
+ readBatteryLevel.run();
+ break;
+ }
+ }
+ }
+
+ public static void handleActionSave(){
+
+ Intent localIntent = new Intent(Constants.BROADCAST_STATUS)
+ // Puts the status into the Intent
+ .putExtra(Constants.EXTENDED_DATA_STATUS, STATUS_SAVE);
+ // Broadcasts the Intent to receivers in this app.
+ LocalBroadcastManager.getInstance(serviceManager).sendBroadcast(localIntent);
+
+ }
+
+
+ public static void handleActionStopOneSave(String id, String deviceName) {
+ Session sessionToRemove = null;
+ // Here we only stop one session, the one corresponding to the device we pass
+ for (Session session : ServiceManager.sessionsList) {
+ if (session.getID().equals(id)){
+ session.stopSession();
+ sessionToRemove = session;
+ break;
+ }
+ }
+ if (sessionToRemove != null) {
+ ServiceManager.sessionsList.remove(sessionToRemove);
+
+ Log.d(TAG_FOREGROUND_SERVICE, "The session of " + deviceName + " has been stopped");
+
+ Intent localIntent = new Intent(Constants.BROADCAST_STATUS)
+ // Puts the status into the Intent
+ .putExtra(Constants.EXTENDED_DATA_STATUS, STATUS_STOP_ONE_SAVE);
+ // Broadcasts the Intent to receivers in this app.
+ LocalBroadcastManager.getInstance(serviceManager).sendBroadcast(localIntent);
+ }
+
+
+ }
+
+ public static void setEmpaticaApiKey(Context context, String key){
+ EMPATICA_API_KEY = key;
+ saveKey(context, key);
+ }
+
+ public static String getEmpaticaApiKey(Context context){
+ String key = readKey(context);
+ EMPATICA_API_KEY = key;
+ return key;
+
+ }
+
+ protected static String readKey(Context context) {
+ //Get File
+ File path = context.getFilesDir();
+ File file = new File(path, Constants.FILE_KEY);
+ int length = (int) file.length();
+ byte[] bytes = new byte[length];
+ String key = "";
+
+ //Read file
+ if (file.exists()) {
+ try {
+ FileInputStream fis = new FileInputStream(file);
+ fis.read(bytes);
+ fis.close();
+ key = new String(bytes);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return key;
+ }
+
+
+ protected static void saveKey(Context context, String key) {
+
+ File path = context.getFilesDir();
+ File file = new File(path, Constants.FILE_KEY);
+ if (!file.exists()) {
+ //Create file
+ try {
+ file.createNewFile();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ try {
+ FileOutputStream stream = new FileOutputStream(file);
+ try {
+ stream.write(key.getBytes());
+ stream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+ private void handleActionDisconnectEmpatica(boolean unexpected) {
+
+ if (unexpected)
+ handleActionNotify(getString(R.string.unexpected_empa_disconnect), ServiceManager.CHANNEL_DEFAULT);
+ else
+ handleActionNotify("Empatica E4: " + serviceManager.getText(R.string.notification_device_disconnect), ServiceManager.CHANNEL_DEFAULT);
+
+ //--- Send broadcast
+ Intent localIntent = new Intent(Constants.BROADCAST_STATUS)
+ // Puts the status into the Intent
+ .putExtra(Constants.EXTENDED_DATA_STATUS, STATUS_DISCONNECTED);
+ // Broadcasts the Intent to receivers in this app.
+ LocalBroadcastManager.getInstance(serviceManager).sendBroadcast(localIntent);
+
+
+ }
+
+ public static void handleActionDisconnect(String name) {
+ //Notify user that device disconnect
+ /*
+ //---- Notification
+ Intent notificationIntent = new Intent(serviceManager, ServiceManager.class);
+ PendingIntent pendingIntent = PendingIntent.getActivity(serviceManager, 0, notificationIntent, 0);
+
+ //--- Make notification
+ NotificationManager notificationManager = (NotificationManager) serviceManager.getSystemService(NOTIFICATION_SERVICE);
+
+ Notification.Builder notification;
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
+ NotificationChannel serviceManagerChannel = notificationManager.getNotificationChannel(CHANNEL_HIGH);
+ notification = new Notification.Builder(serviceManager, serviceManagerChannel.getId());
+ } else {
+ notification = new Notification.Builder(serviceManager);
+ }
+ notification.setContentTitle(serviceManager.getText(R.string.notification_title))
+ .setContentText(name + ": " + serviceManager.getText(R.string.notification_device_disconnect))
+ .setSmallIcon(R.drawable.ic_notif_monitoring)
+ .setContentIntent(pendingIntent)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .setAutoCancel(true);
+
+ notificationManager.notify(MAIN_NOTIFICATION_ID, notification.build());*/
+
+ handleActionNotify(name + ": " + serviceManager.getText(R.string.notification_device_disconnect), ServiceManager.CHANNEL_DEFAULT);
+
+ //--- Send broadcast
+ Intent localIntent = new Intent(Constants.BROADCAST_STATUS)
+ // Puts the status into the Intent
+ .putExtra(Constants.EXTENDED_DATA_STATUS, STATUS_DISCONNECTED);
+ // Broadcasts the Intent to receivers in this app.
+ LocalBroadcastManager.getInstance(serviceManager).sendBroadcast(localIntent);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ this.unregisterReceiver(bluetoothBroadcast);
+ // this.unbindService(this);
+ this.unbindService(serviceManager);
+ for (Manager manager : managerDevices) {
+ if(manager.getTag().equals("Empatica Man SQUINCI")){
+ ((EmpaticaManager) manager).empaManager.cleanUp();
+ }
+ }
+ }
+
+ //--------------------------------- DISCONNECTION Part ---------------------------------------//
+ public static void startActionDisconnect(Context context, String name) {
+ Intent intent = new Intent(context, ServiceManager.class);
+ intent.setAction(ACTION_DISCONNECT);
+ intent.putExtra(EXTRA_NAME, name);
+ context.startService(intent);
+ }
+
+ public static void startActionDisconnectEmpatica(Context context, boolean unexpected) {
+ Intent intent = new Intent(context, ServiceManager.class);
+ intent.setAction(ACTION_DISCONNECT_EMPATICA);
+ intent.putExtra(EXTRA_UNEXPECTED, unexpected);
+ context.startService(intent);
+ }
+
+ public static void startActionStartDisconnect(Context context, String id, String className){
+ Intent intent = new Intent(context, ServiceManager.class);
+ intent.setAction(ACTION_START_DISCONNECT);
+ intent.putExtra(EXTRA_ID, id);
+ intent.putExtra(EXTRA_CLASS_NAME, className);
+ context.startService(intent);
+ }
+
+ public static void stopService(Context context){
+ Intent intent = new Intent(context, ServiceManager.class);
+ context.stopService(intent);
+ }
+
+ public static void handleActionStartDisconnect(String id, String className){
+ // Get manager
+ for (final Manager manager : managerDevices) {
+ if (manager.getClass().getName().equals(className)) {
+ //Connect to the device with the right manager
+ //new Thread
+ Runnable disconnecter = () -> manager.disconnect(id);
+ disconnecter.run();
+ break;
+ }
+ }
+ }
+
+ public static void handleActionErrorConnect(String error) {
+ Intent localIntent = new Intent(Constants.BROADCAST_STATUS)
+ // Puts the status into the Intent
+ .putExtra(Constants.EXTENDED_DATA_STATUS, STATUS_ERROR_CONNECTION)
+ .putExtra(Constants.EXTENDED_DATA_INFOS, error);
+ // Broadcasts the Intent to receivers in this app.
+ LocalBroadcastManager.getInstance(serviceManager).sendBroadcast(localIntent);
+ }
+
+ //------------------------------- START CONNECTION Part --------------------------------------//
+ public static void startActionErrorConnect(Context context, String error) {
+ Intent intent = new Intent(context, ServiceManager.class);
+ intent.setAction(ACTION_ERROR_CONNECT);
+ intent.putExtra(EXTRA_ERROR, error);
+ context.startService(intent);
+ }
+
+ public static void handleActionConnect(final String id, String className) {
+ // Get manager
+ for (final Manager manager : managerDevices) {
+ if (manager.getClass().getName().equals(className)) {
+ //Connect to the device with the right manager
+ //new Thread
+ Runnable connecter = () -> manager.connect(id);
+ connecter.run();
+ break;
+ }
+ }
+ }
+
+ public static void startActionConnect(Context context, String id, String className) {
+ Intent intent = new Intent(context, ServiceManager.class);
+ intent.setAction(ACTION_CONNECT);
+ intent.putExtra(EXTRA_ID, id);
+ intent.putExtra(EXTRA_CLASS_NAME, className);
+ context.startService(intent);
+ }
+
+ public static void startActionStopOneSave(Context context, String id, String deviceName) {
+ Intent intent = new Intent(context, ServiceManager.class);
+ intent.setAction(ACTION_STOP_ONE_SAVE);
+ intent.putExtra(EXTRA_ID, id);
+ intent.putExtra(EXTRA_NAME, deviceName);
+ context.startService(intent);
+ }
+
+ public static void startActionSave(Context context) {
+ Intent intent = new Intent(context, ServiceManager.class);
+ intent.setAction(ACTION_SAVE);
+ context.startService(intent);
+ }
+
+
+ public static void startActionReadBatteryLevel(Context context, String id, String className){
+ Intent intent = new Intent(context, ServiceManager.class);
+ intent.setAction(ACTION_BATTERY);
+ intent.putExtra(EXTRA_ID, id);
+ intent.putExtra(EXTRA_CLASS_NAME, className);
+ context.startService(intent);
+
+ }
+
+ public static void handleActionConnected() {
+ Intent localIntent = new Intent(Constants.BROADCAST_STATUS)
+ // Puts the status into the Intent
+ .putExtra(Constants.EXTENDED_DATA_STATUS, STATUS_CONNECTED);
+ // Broadcasts the Intent to receivers in this app.
+ LocalBroadcastManager.getInstance(serviceManager).sendBroadcast(localIntent);
+ }
+
+ public static void notifyMissingKey() {
+ Intent localIntent = new Intent(Constants.MISSING_KEY);
+ // Broadcasts the Intent to receivers in this app.
+ LocalBroadcastManager.getInstance(serviceManager).sendBroadcast(localIntent);
+ }
+
+ public static void notifyWrongKey() {
+ Intent localIntent = new Intent(Constants.WRONG_KEY);
+ // Broadcasts the Intent to receivers in this app.
+ LocalBroadcastManager.getInstance(serviceManager).sendBroadcast(localIntent);
+ }
+
+ public static void startActionBatteryRead(Context context){
+ Intent intent = new Intent(context, ServiceManager.class);
+ intent.setAction(ACTION_BATTERY_READ);
+ context.startService(intent);
+ }
+
+ public static void startActionConnected(Context context) {
+ Intent intent = new Intent(context, ServiceManager.class);
+ intent.setAction(ACTION_CONNECTED);
+ context.startService(intent);
+ }
+
+ /**
+ * Broadcast info that a device has been scanned
+ */
+ public static void handleActionScanned() {
+ Intent localIntent = new Intent(Constants.BROADCAST_STATUS)
+ // Puts the status into the Intent
+ .putExtra(Constants.EXTENDED_DATA_STATUS, STATUS_SCANNED);
+ // Broadcasts the Intent to receivers in this app.
+ LocalBroadcastManager.getInstance(serviceManager).sendBroadcast(localIntent);
+ }
+
+ public static ArrayList getConnectedDevices() {
+ ArrayList listDevice = new ArrayList<>();
+ for (Manager manager : managerDevices) {
+ listDevice.addAll(manager.getConnectedDevice());
+ }
+ return listDevice;
+ }
+
+ public static ArrayList getChosenDevices() {
+ return deviceList;
+ }
+
+ public static void setAutoSaving(boolean autosaving){
+ autoSaving = autosaving;
+ }
+
+ public static void setUsage(String usage){
+ Usage = usage;
+ }
+
+ public static boolean getAutoSaving(){
+ return autoSaving;
+ }
+
+ public static String getUsage(){
+ return Usage;
+ }
+
+
+ public static void updateChosenDevices(Context context, String selectedDevices){
+ deviceList.clear();
+ MetaWear.i = 0;
+ String[] allDevices = Constants.DEVICES_CLASSES;
+ choosenDevString = selectedDevices;
+ String localCopy_selectedDevices = selectedDevices;
+ for(String deviceName : allDevices){
+ if (localCopy_selectedDevices!= null && localCopy_selectedDevices.contains(deviceName)){
+ localCopy_selectedDevices = localCopy_selectedDevices.replaceFirst(deviceName, "");
+ try{
+ Class> classDevice = Class.forName(deviceName);
+ Device device = (Device) classDevice.getDeclaredConstructor(Context.class)
+ .newInstance(context);
+
+ deviceList.add(device);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.e("error", e.getCause().getMessage());
+ }
+
+ }
+
+ }
+ //--- Will get info from the broadcaster
+ int batteryLevel;
+ for (Device d : deviceList) {
+ d.setScanned(false);
+ d.setConnected(false);
+ for (Device cd : ServiceManager.getConnectedDevices()) {
+
+ if (cd.getName().contains(d.getName())) {
+ d.setScanned(true);
+ d.setConnected(true);
+ batteryLevel = cd.getBatteryLevel();
+ if (batteryLevel > 0)
+ d.setBatteryLevel(batteryLevel);
+ d.setId(cd.getId());
+ }
+ }
+ for (Device sd : ServiceManager.getScannedDevices()) {
+ String deviceName = d.getName();
+ String scanDeviceName = sd.getName();
+ if (sd.getName().contains(d.getName()) || d.getName().contains(sd.getName())) {
+ d.setScanned(true);
+ d.setId(sd.getId());
+ }
+ }
+ }
+
+ }
+
+ //-------------------------------- STOP SCANNING Part ----------------------------------------//
+ public static void startActionStopScan(Context context) {
+ Intent intent = new Intent(context, ServiceManager.class);
+ intent.setAction(ACTION_STOP_SCAN);
+ context.startService(intent);
+ }
+
+ public static void handleActionStopScan() {
+ for (final Manager manager : managerDevices) {
+ manager.stopScan();
+ }
+ }
+
+ //----------------------------------- SCANNING Part ------------------------------------------//
+ public static void startActionScan(Context context) {
+ Intent intent = new Intent(context, ServiceManager.class);
+ intent.setAction(ACTION_SCAN);
+ context.startService(intent);
+ }
+
+ public static void startActionScanned(Context context) {
+ Intent intent = new Intent(context, ServiceManager.class);
+ intent.setAction(ACTION_SCANNED);
+ context.startService(intent);
+ }
+
+ /**
+ * Action function
+ */
+ public static void handleActionScan() {
+ for (final Manager manager : managerDevices) {
+ String managerName = manager.getName();
+ if (choosenDevString.contains(managerName)) {
+ Runnable scan = manager::scan;
+ scan.run();
+ }
+ }
+ Log.d(TAG_FOREGROUND_SERVICE, "Scan has been launched.");
+ }
+
+ //--------------------------------- Override functions ---------------------------------------//
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.d(TAG_FOREGROUND_SERVICE, "Manager Foreground Service started.");
+
+ Intent notificationIntent = new Intent(this, MainActivity.class);
+ PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
+
+ IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
+ this.registerReceiver(bluetoothBroadcast, filter);
+
+ // bind this to BLE Service from Metawear
+ this.bindService(new Intent(this, BtleService.class), this, Context.BIND_AUTO_CREATE);
+
+ getEmpaticaApiKey(this);
+ //-------- Create Channel -------------//
+ NotificationChannel serviceManagerChannelHigh = null;
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
+ NotificationChannel serviceManagerChannelNone = new NotificationChannel(CHANNEL_NONE, "ServiceManager Channel NONE", NotificationManager.IMPORTANCE_NONE);
+ NotificationManager notificationManagerNone = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ notificationManagerNone.createNotificationChannel(serviceManagerChannelNone);
+
+ NotificationChannel serviceManagerChannelDefault = new NotificationChannel(CHANNEL_DEFAULT, "ServiceManager Channel NORMAL", NotificationManager.IMPORTANCE_DEFAULT);
+ NotificationManager notificationManagerDefault = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ notificationManagerDefault.createNotificationChannel(serviceManagerChannelDefault);
+
+ serviceManagerChannelHigh = new NotificationChannel(CHANNEL_HIGH, "ServiceManager Channel HIGH", NotificationManager.IMPORTANCE_HIGH);
+ serviceManagerChannelHigh.enableVibration(true);
+ serviceManagerChannelHigh.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
+
+ serviceManagerChannelHigh.setShowBadge(true);
+ NotificationManager notificationManagerHigh = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ notificationManagerHigh.createNotificationChannel(serviceManagerChannelHigh);
+ }
+
+ Notification.Builder notification;
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
+ notification = new Notification.Builder(getApplicationContext(), serviceManagerChannelHigh
+ .getId());
+ } else {
+ notification = new Notification.Builder(getApplicationContext());
+ }
+
+ notification.setContentTitle(getText(R.string.notification_title))
+ .setContentText(getText(R.string.notification_message_started))
+ .setSmallIcon(R.drawable.ic_notif_monitoring)
+ .setContentIntent(pendingIntent)
+ .setOngoing(true);
+
+ startForeground(PERSISTENT_NOTIFICATION_ID, notification.build());
+ serviceManager = this;
+
+ //--- Instantiate all managers
+ managerDevices.clear();
+
+ //---- Scanning
+ for (String className : Constants.MANAGER_CLASSES) {
+ try {
+ Class> classManager = Class.forName(className);
+
+ Manager manager = (Manager) classManager.getDeclaredConstructor(Context.class)
+ .newInstance(this);
+ managerDevices.add(manager);
+
+ } catch (InstantiationException | ClassNotFoundException | IllegalAccessException | NoSuchMethodException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ Log.e(TAG_FOREGROUND_SERVICE, e.getCause().getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ }
+
+ public static ArrayList getScannedDevices() {
+ ArrayList listDevice = new ArrayList<>();
+ for (Manager manager : managerDevices) {
+ listDevice.addAll(manager.getScannedDevices());
+ }
+ return listDevice;
+ }
+
+ public static ArrayList getManager() {
+ return managerDevices;
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ // Typecast the binder to the service's LocalBinder class
+ serviceBinder = (BtleService.LocalBinder) service;
+ serviceBLEBound = true;
+ Log.d(TAG_FOREGROUND_SERVICE, "Service connected");
+
+ }
+ public static BtleService.LocalBinder getMetawearBinder(){
+ return serviceBinder;
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ for (Manager manager : managerDevices) {
+ if(manager.getTag().equals("Hexoskin Manager SQUINCI")){
+ manager.disconnect(manager.getConnectedDevice().get(0).getId());
+ }
+ } // TODO call all disconnect, at this point keeping the other makes no sense
+ serviceBLEBound = false;
+
+ }
+ //--------------------------------------------------------------------------------------------//
+
+
+ /*--------------------------------------------------------------------------------------------//
+ //--- CLASS ---//
+ //--------------------------------------------------------------------------------------------*/
+
+ public class ServiceManagerBinder extends Binder {
+ public ServiceManager getService() {
+ // Return this instance of LocalService so clients can call public methods
+ return ServiceManager.this;
+ }
+ }
+
+ // Create a BroadcastReceiver for ACTION_FOUND.
+ public class BluetoothBroadcast extends BroadcastReceiver {
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (BluetoothDevice.ACTION_FOUND.equals(action)) {
+ // Discovery has found a device. Get the BluetoothDevice
+ // object and its info from the Intent.
+ BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ String deviceName = device.getName();
+ String deviceAddress = device.getAddress();
+ for (final Manager manager : managerDevices) {
+ manager.isDevice(deviceName, deviceAddress);
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/Session.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Session.java
new file mode 100644
index 0000000..ea34ec4
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/Session.java
@@ -0,0 +1,289 @@
+package ch.epfl.esl.elevatedmonitor;
+
+import android.os.Environment;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Data;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.Device;
+import ch.epfl.esl.elevatedmonitor.SuperClasses.SensorDevice;
+
+public class Session {
+ //--- Variables
+ private String sessionName;
+ private Device sessionDevice;
+ private SensorDevice sessionSensor;
+ private boolean active;
+
+ private String ID = "";
+ private String name = "";
+
+ //--- Exception
+ class SessionException extends Exception {
+ SessionException() {
+ System.out.println("Separator in file name are not right.");
+ }
+ }
+
+ //--- Constructor
+ Session(final Device sessionDevice, final SensorDevice sessionSensor) {
+ //---Set variable we get
+ this.sessionDevice = sessionDevice;
+ this.sessionSensor = sessionSensor;
+
+ this.ID = sessionDevice.getId();
+
+ this.name = sessionDevice.getName();
+
+ //---Set the name of the session
+ setSessionName();
+
+ //---Print the first line
+ printSessionHeader();
+
+ //--- Set active flag
+ this.active = true;
+ }
+
+ Session(String fileName) throws SessionException {
+ this.active = false;
+ if (fileName.length() < 5) {
+ throw new SessionException();
+ } else {
+ int separatorEnd = fileName.indexOf(".", fileName.length() - 5);
+ fileName = fileName.substring(0, separatorEnd);
+ this.sessionName = fileName;
+ }
+ }
+
+ private void setSessionName() {
+ if (sessionSensor != null) {
+ //Create the session name.
+ this.sessionName = String.format("Session-%s-%s-%s", sessionDevice.getName()
+ .trim()
+ .replace(" ", "_")
+ .replace("-", "_"), sessionSensor
+ .getName()
+ .trim()
+ .replace(" ", "_")
+ .replace("-", "_"), new SimpleDateFormat("dd_MM_yy_HH_mm_ss", Locale.FRENCH).format(new Date()));
+ } else {
+ //Create the session name.
+ this.sessionName = String.format("Session-%s-all_sensors-%s-%s", sessionDevice.getName()
+ .trim()
+ .replace(" ", "_")
+ .replace("-", "_"), new SimpleDateFormat("dd_MM_yy_HH_mm_ss", Locale.FRENCH)
+ .format(new Date()), sessionDevice.getId().replace(":", "-"));
+ }
+
+
+ //Check if it already exist or not.
+ if (isExternalStorageReadable()) {
+ File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
+ if (path.isDirectory()) {
+ File file = new File(path, this.sessionName + ".csv");
+ int n = 1;
+ while (file.exists()) {
+ String fileNameSave = this.sessionName + "-" + n;
+ file = new File(path, fileNameSave + ".csv");
+ n++;
+ }
+ try { //Create File
+ file.createNewFile();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ //--- Export Data of the sensors to the CSV file
+ private void printSessionHeader() {
+ //--- Check if writable
+ if (isExternalStorageWritable()) {
+ File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
+ boolean pathIsThere;
+ if (!path.isDirectory()) {
+ pathIsThere = path.mkdirs();
+ } else {
+ pathIsThere = true;
+ }
+ if (pathIsThere) {
+ File file = new File(path, this.sessionName + ".csv");
+
+ //--- check if it exist
+ if (file.exists()) {
+ FileOutputStream outputStream;
+
+ //--- Start to print
+ try {
+ StringBuilder string;
+ int numDataMax = 0;
+ boolean timeStampIsThere = false;
+ for (SensorDevice sensor : this.sessionDevice.getSensors()) {
+ int numData;
+ numData = sensor.getAllData().size();
+ if (numData >= numDataMax)
+ numDataMax = numData;
+ // Add Time stamp if there is
+ if (sensor.getTimeStamp() != null){
+ timeStampIsThere = true;
+ }
+
+ }
+ string = new StringBuilder("Sensor Name");
+ for (int i=0; i selectedDevices = new ArrayList<>();
+ private Switch autoSaveSwitch;
+ private SharedPreferences sharedPref;
+ private SharedPreferences.Editor editor;
+ private NavigationView navigationView;
+ private boolean apiSet;
+ private List initItemList;
+ private ListViewItemCheckboxBaseAdapter listViewDataAdapter;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_settings);
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
+ drawer.addDrawerListener(toggle);
+ toggle.syncState();
+
+ sharedPref = this.getSharedPreferences(
+ getString(R.string.preference_file_key), Context.MODE_PRIVATE);
+ editor = sharedPref.edit();
+
+ Spinner dropdown = findViewById(R.id.spinner);
+ String[] items = new String[]{"One", "Two"};
+ ArrayAdapter adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_dropdown_item, items);
+ dropdown.setAdapter(adapter);
+ String companyName = sharedPref.getString(getString(R.string.selected_profile), items[0]);
+ int spinnerPosition = adapter.getPosition(companyName);
+ //set the default according to value
+ dropdown.setSelection(spinnerPosition);
+ dropdown.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+
+ @Override
+ public void onItemSelected(AdapterView> adapterView, View view,
+ int position, long id) {
+ String item = (String) adapterView.getItemAtPosition(position);
+ editor.putString(getString(R.string.selected_profile), item);
+ editor.apply();
+ ReadQuestions myTask = new ReadQuestions(SettingsActivity.this, item);
+ myTask.execute();
+ if (item.equals("One")){
+ int size = initItemList.size();
+ for (int i = 0; i < size; i++) {
+ ListViewItemDTO dto = initItemList.get(i);
+ selectedDevices.clear();
+ dto.setChecked(false);
+ if (dto.getItemText().equals("Empatica")) {
+ dto.setChecked(true);
+ selectedDevices.add(dto.getItemText());
+ }
+ }
+ listViewDataAdapter.notifyDataSetChanged();
+ editor.putString(getString(R.string.selected_devices), getString(R.string.empatica));
+ editor.apply();
+ ServiceManager.updateChosenDevices(getApplicationContext(), getString(R.string.empatica_class_name));
+ } else {
+ int size = initItemList.size();
+ for (int i = 0; i < size; i++) {
+ ListViewItemDTO dto = initItemList.get(i);
+ dto.setChecked(true);
+ }
+
+ listViewDataAdapter.notifyDataSetChanged();
+ selectedDevices.clear();
+ selectedDevices.addAll(Arrays.asList(Constants.DEVICES_NAME));
+ editor.putString(getString(R.string.selected_devices), (selectedDevices.toString()));
+ editor.apply();
+ ServiceManager.updateChosenDevices(getApplicationContext(), getDeviceClasses(selectedDevices.toString()));
+ //Toast.makeText(getApplicationContext(), "Selected Items: \n" +
+ // selectedDevices.toString(), Toast.LENGTH_LONG).show();
+ listViewDataAdapter.notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> adapterView) {
+ // TODO Auto-generated method stub
+
+ }
+ });
+
+ // Switch to enable auto saving
+ autoSaveSwitch = findViewById(R.id.autoSave);
+
+ autoSaveSwitch.setChecked(sharedPref.getBoolean(getString(R.string.auto_saving), true));
+
+ apiSet = sharedPref.getBoolean(getString(R.string.key_setted), false);
+
+ TextInputLayout textInputLayout = findViewById(R.id.textInput);
+ EditText editText = textInputLayout.getEditText();
+ if (editText != null)
+ editText.setText(ServiceManager.getEmpaticaApiKey(this));
+ Button button = findViewById(R.id.btn_key);
+
+ if (!apiSet) {
+ button.setText(getString(R.string.login));
+ textInputLayout.setEnabled(true);
+ } else {
+ textInputLayout.setEnabled(false);
+ button.setText(getString(R.string.changing_api));
+
+ }
+ button.setOnClickListener(v -> {
+ apiSet = sharedPref.getBoolean(getString(R.string.key_setted), false);
+ if (!apiSet) {
+ hideKeyboard();
+ ServiceManager.setEmpaticaApiKey(this, textInputLayout.getEditText().getText().toString());
+ Toast.makeText(getApplicationContext(), "OK! I'm performing login.", Toast.LENGTH_SHORT).show();
+ if (!selectedDevices.contains("Empatica"))
+ selectedDevices.add("Empatica");
+ editor.putString(getString(R.string.selected_devices), (selectedDevices.toString()));
+ editor.apply();
+ ServiceManager.updateChosenDevices(getApplicationContext(), getDeviceClasses(selectedDevices.toString()));
+ editor.putBoolean(getString(R.string.key_setted), true);
+ editor.apply();
+ Intent mainActivity = new Intent(SettingsActivity.super.getApplication(), MainActivity.class);
+ startActivity(mainActivity);
+ } else {
+ button.setText(getString(R.string.login));
+ textInputLayout.setEnabled(true);
+ editor.putBoolean(getString(R.string.key_setted), false);
+ editor.apply();
+ EmpaticaManager.deAuthenticate();
+
+ }
+ });
+ navigationView = findViewById(R.id.nav_view);
+ navigationView.setNavigationItemSelectedListener(this);
+ selectedDevices = getSelectedDevices();
+ setTitle("Settings");
+ // Get listView checkbox.
+ final ListView listViewWithCheckbox = findViewById(R.id.list_view_with_checkbox);
+
+ // Initiate listView data.
+ initItemList = this.getInitViewItemDtoList();
+
+ // Create a custom list view adapter with checkbox control.
+ listViewDataAdapter = new ListViewItemCheckboxBaseAdapter(getApplicationContext(), initItemList);
+
+ listViewDataAdapter.notifyDataSetChanged();
+
+ // Set data adapter to list view.
+ listViewWithCheckbox.setAdapter(listViewDataAdapter);
+
+ // When list view item is clicked.
+ listViewWithCheckbox.setOnItemClickListener((adapterView, view, itemIndex, l) -> {
+ // Get user selected item.
+ Object itemObject = adapterView.getAdapter().getItem(itemIndex);
+
+ // Translate the selected item to DTO object.
+ ListViewItemDTO itemDto = (ListViewItemDTO) itemObject;
+
+ // Get the checkbox.
+ CheckBox itemCheckbox = view.findViewById(R.id.list_view_item_checkbox);
+
+ // Reverse the checkbox and clicked item check state.
+ if (itemDto.isChecked()) {
+ itemCheckbox.setChecked(false);
+ itemDto.setChecked(false);
+ selectedDevices.remove(itemDto.getItemText());
+ } else {
+ itemCheckbox.setChecked(true);
+ itemDto.setChecked(true);
+ selectedDevices.add(itemDto.getItemText());
+ }
+ editor.putString(getString(R.string.selected_devices), (selectedDevices.toString()));
+ editor.apply();
+ ServiceManager.updateChosenDevices(getApplicationContext(), getDeviceClasses(selectedDevices.toString()));
+ //Toast.makeText(getApplicationContext(), "Selected Items: \n " +
+ // selectedDevices.toString(), Toast.LENGTH_LONG).show();
+ });
+
+ // Click this button to select all listview items with checkbox checked.
+ Button selectAllButton = findViewById(R.id.list_select_all);
+ selectAllButton.setOnClickListener(view -> {
+ int size = initItemList.size();
+ for (int i = 0; i < size; i++) {
+ ListViewItemDTO dto = initItemList.get(i);
+ dto.setChecked(true);
+ }
+
+ listViewDataAdapter.notifyDataSetChanged();
+ selectedDevices.clear();
+ selectedDevices.addAll(Arrays.asList(Constants.DEVICES_NAME));
+ editor.putString(getString(R.string.selected_devices), (selectedDevices.toString()));
+ editor.apply();
+ ServiceManager.updateChosenDevices(getApplicationContext(), getDeviceClasses(selectedDevices.toString()));
+ //Toast.makeText(getApplicationContext(), "Selected Items: \n" +
+ // selectedDevices.toString(), Toast.LENGTH_LONG).show();
+ });
+
+ // Click this button to deselect all listViews items with checkbox unchecked.
+ Button selectNoneButton = findViewById(R.id.list_select_none);
+ selectNoneButton.setOnClickListener(view -> {
+ int size = initItemList.size();
+ for (int i = 0; i < size; i++) {
+ ListViewItemDTO dto = initItemList.get(i);
+ dto.setChecked(false);
+ }
+
+ listViewDataAdapter.notifyDataSetChanged();
+ selectedDevices.clear();
+ editor.putString(getString(R.string.selected_devices), (selectedDevices.toString()));
+ editor.apply();
+ ServiceManager.updateChosenDevices(getApplicationContext(), getDeviceClasses(selectedDevices.toString()));
+ //Toast.makeText(getApplicationContext(), "Selected Items: \n" +
+ // selectedDevices.toString(), Toast.LENGTH_LONG).show();
+ });
+
+ // Click this button to reverse select listView items.
+ Button selectReverseButton = findViewById(R.id.list_select_reverse);
+ selectReverseButton.setOnClickListener(view -> {
+ int size = initItemList.size();
+ for (int i = 0; i < size; i++) {
+ ListViewItemDTO dto = initItemList.get(i);
+
+ if (dto.isChecked()) {
+ dto.setChecked(false);
+ selectedDevices.remove(dto.getItemText());
+ } else {
+ dto.setChecked(true);
+ selectedDevices.add(dto.getItemText());
+ }
+ }
+
+ listViewDataAdapter.notifyDataSetChanged();
+ editor.putString(getString(R.string.selected_devices), (selectedDevices.toString()));
+ editor.apply();
+ ServiceManager.updateChosenDevices(getApplicationContext(), getDeviceClasses(selectedDevices.toString()));
+ //Toast.makeText(getApplicationContext(), "Selected Items: \n" +
+ // selectedDevices.toString(), Toast.LENGTH_LONG).show();
+ });
+
+ // Click this button to start connecting
+ Button startbutton = findViewById(R.id.startConnecting);
+ startbutton.setOnClickListener(view -> {
+ Intent mainActivity = new Intent(SettingsActivity.super.getApplication(), MainActivity.class);
+ startActivity(mainActivity);
+ });
+
+
+ autoSaveSwitch.setOnClickListener(view -> {
+ if(autoSaveSwitch.isChecked()){
+ ServiceManager.setAutoSaving(true);
+ editor.putBoolean(getString(R.string.auto_saving), true);
+ editor.apply();
+ Toast.makeText(getApplicationContext(), getString(R.string.auto_saving_mode_enabled), Toast.LENGTH_LONG).show();
+ }else {
+ ServiceManager.setAutoSaving(false);
+ editor.putBoolean(getString(R.string.auto_saving), false);
+ editor.apply();
+ Toast.makeText(getApplicationContext(), getString(R.string.auto_saving_mode_disabled), Toast.LENGTH_LONG).show();
+
+ }
+
+ });
+
+ }
+
+ private ArrayList getSelectedDevices() {
+ String[] allDevices = Constants.DEVICES_NAME;
+ ArrayList chosenDevices = new ArrayList<>();
+ String selectedDevices = sharedPref.getString(getString(R.string.selected_devices), TextUtils.join("", allDevices));
+ for (String device : Constants.DEVICES_NAME){
+ if (selectedDevices != null && selectedDevices.contains(device)){
+ chosenDevices.add(device);
+ }
+ }
+ return chosenDevices;
+ }
+
+ // Return an initialize list of ListViewItemDTO.
+ private List getInitViewItemDtoList()
+ {
+ String[] itemTextArr = Constants.DEVICES_NAME;
+
+ List ret = new ArrayList<>();
+
+ for (String itemText : itemTextArr) {
+ ListViewItemDTO dto = new ListViewItemDTO();
+ dto.setItemText(itemText);
+ if (selectedDevices.contains(itemText)){
+ dto.setChecked(true);
+ }
+
+ ret.add(dto);
+ }
+
+ editor.putString(getString(R.string.selected_devices), (selectedDevices.toString()));
+ editor.apply();
+
+ return ret;
+ }
+
+ private String getDeviceClasses(String selectedDevices){
+
+ StringBuilder chosenClassNames = new StringBuilder();
+ List deviceNames = Arrays.asList(Constants.DEVICES_NAME);
+ String[] classNames = Constants.DEVICES_CLASSES;
+ int index;
+ for (String name : deviceNames){
+ if(selectedDevices.contains(name)){
+ index = deviceNames.indexOf(name);
+ chosenClassNames.append(classNames[index]);
+ }
+
+ }
+ return chosenClassNames.toString();
+
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ editor.putString(getString(R.string.selected_devices), (selectedDevices.toString()));
+ if(autoSaveSwitch.isChecked()){
+ editor.putBoolean(getString(R.string.auto_saving), true);
+ ServiceManager.setAutoSaving(true);
+ }else {
+ editor.putBoolean(getString(R.string.auto_saving), false);
+ ServiceManager.setAutoSaving(false);
+ }
+ editor.commit();
+
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ navigationView.setCheckedItem(R.id.nav_settings);
+ }
+
+ @Override
+ public boolean onNavigationItemSelected(@NonNull MenuItem item) {
+ // Handle navigation view item clicks here.
+ int id = item.getItemId();
+
+ if (id == R.id.nav_settings) {
+ Intent settingActivity = new Intent(this, SettingsActivity.class);
+ startActivity(settingActivity);
+ } else if (id == R.id.auto_connect) {
+ Intent mainActivity = new Intent(this, MainActivity.class);
+ startActivity(mainActivity);
+ } else if (id == R.id.nav_monitoring) {
+ Intent monitoringActivity = new Intent(this, MonitoringActivity.class);
+ startActivity(monitoringActivity);
+ }
+
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ drawer.closeDrawer(GravityCompat.START);
+ return true;
+ }
+
+ @Override
+ public void onBackPressed() {
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ if (drawer.isDrawerOpen(GravityCompat.START)) {
+ drawer.closeDrawer(GravityCompat.START);
+ } else {
+ Intent mainActivity = new Intent(this, MainActivity.class);
+ startActivity(mainActivity);
+ super.onBackPressed();
+
+ }
+ }
+
+ private void hideKeyboard() {
+ View view = getCurrentFocus();
+ if (view != null) {
+ ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).
+ hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
+ }
+ }
+
+ private void loadQuestions(Context context, String choice) {
+ String TAG = "SQUINCI";
+ FirebaseDatabase database = FirebaseDatabase.getInstance();
+ final SharedPreferences prefs = context.getSharedPreferences(Constants.APP_SETTINGS, MODE_PRIVATE);
+ if (choice.equals("One")){
+ final DatabaseReference configurationRef = database.getReference(Constants.REMOTE_CONFIGURATION);
+
+ configurationRef.addValueEventListener(new ValueEventListener() {
+ @Override
+ public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
+ // This method is called once with the initial value and again
+ // whenever data at this location is updated.
+ String value = dataSnapshot.getValue(String.class);
+ prefs.edit().putString(Constants.REMOTE_CONFIGURATION, value).apply();
+ Log.v(TAG, value);
+ //JSONObject remoteConfig = JSON.parseObject(value);
+
+ // Set version config
+ //TextView versionInfo = findViewById(R.id.versionInfo);
+ //versionInfo.setText(remoteConfig.getString("config_version"));
+
+ // We don't want live updates
+ configurationRef.removeEventListener(this);
+
+ }
+
+ @Override
+ public void onCancelled(@NonNull DatabaseError databaseError) {
+ // Failed to read value
+ Log.w(TAG, "Failed to read value.", databaseError.toException());
+ }
+ });
+ try (InputStream is = getResources().openRawResource(R.raw.app_remote_config)) {
+ Writer writer = new StringWriter();
+ char[] buffer = new char[1024];
+ Reader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
+ int n;
+ while ((n = reader.read(buffer)) != -1) {
+ writer.write(buffer, 0, n);
+ }
+ String jsonString = writer.toString();
+ prefs.edit().putString(Constants.REMOTE_CONFIGURATION, jsonString).apply();
+
+ } catch (Exception e) {
+ Log.d(TAG, e.getMessage());
+ }
+
+ } else if (choice.equals("Two")) {
+ final DatabaseReference configurationRef = database.getReference(Constants.REMOTE_CONFIGURATION_TWO);
+
+ configurationRef.addValueEventListener(new ValueEventListener() {
+ @Override
+ public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
+ // This method is called once with the initial value and again
+ // whenever data at this location is updated.
+ String value = dataSnapshot.getValue(String.class);
+ prefs.edit().putString(Constants.REMOTE_CONFIGURATION, value).apply();
+ Log.v(TAG, value);
+ //JSONObject remoteConfig = JSON.parseObject(value);
+
+ // Set version config
+ //TextView versionInfo = findViewById(R.id.versionInfo);
+ //versionInfo.setText(remoteConfig.getString("config_version"));
+
+ // We don't want live updates
+ configurationRef.removeEventListener(this);
+
+ }
+
+ @Override
+ public void onCancelled(@NonNull DatabaseError databaseError) {
+ // Failed to read value
+ Log.w(TAG, "Failed to read value.", databaseError.toException());
+ }
+ });
+ try (InputStream is = getResources().openRawResource(R.raw.app_remote_config_two)) {
+ Writer writer = new StringWriter();
+ char[] buffer = new char[1024];
+ Reader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
+ int n;
+ while ((n = reader.read(buffer)) != -1) {
+ writer.write(buffer, 0, n);
+ }
+ String jsonString = writer.toString();
+ prefs.edit().putString(Constants.REMOTE_CONFIGURATION, jsonString).apply();
+
+ } catch (Exception e) {
+ Log.d(TAG, e.getMessage());
+ }
+ }
+ }
+
+ public class ReadQuestions extends AsyncTask {
+
+ private Context context;
+ private String choice;
+
+ ReadQuestions(Context context, String choice) { // can take other params if needed
+ this.context = context;
+ this.choice = choice;
+ }
+
+
+ @Override
+ protected Void doInBackground(Void... voids) {
+ loadQuestions(this.context, this.choice);
+ return null;
+ }
+
+ }
+
+
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/SuperClasses/Data.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/SuperClasses/Data.java
new file mode 100644
index 0000000..c386483
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/SuperClasses/Data.java
@@ -0,0 +1,34 @@
+package ch.epfl.esl.elevatedmonitor.SuperClasses;
+
+import java.io.Serializable;
+
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceData;
+
+public abstract class Data implements InterfaceData, Serializable {
+ protected double data = Double.NaN;
+ private boolean canBeRead = false;
+
+ @Override
+ public double get() {
+ if (canBeRead) {
+ canBeRead = false;
+ //Log.d("BESTIA", " you CAN read" );
+ return data;
+ }
+ else {
+ //Log.d("BESTIA", " you cannot read" );
+ return Double.NaN;
+ }
+ }
+
+ public double alwaysGet(){
+ return data;
+ }
+
+ public void set(double data) {
+ this.canBeRead = true;
+ //Log.d("BESTIA", " DATA available " );
+ this.data = data;
+ }
+
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/SuperClasses/Device.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/SuperClasses/Device.java
new file mode 100644
index 0000000..6a6fc07
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/SuperClasses/Device.java
@@ -0,0 +1,98 @@
+package ch.epfl.esl.elevatedmonitor.SuperClasses;
+
+import android.content.Context;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceDevice;
+
+
+public abstract class Device implements InterfaceDevice, Serializable {
+ //--- Variables declaration
+ private boolean connected = false;
+ private boolean scanned = false;
+ private boolean available = false;
+ private int batteryLevel = -1;
+
+ //------ protected
+ protected transient Context deviceContext;
+ protected ArrayList sensorArrayList = new ArrayList();
+ protected String managerName = "";
+ protected String name = "";
+ protected String id = "";
+
+ //--- Constructor
+ protected Device(Context context) {
+ deviceContext = context;
+ setManagerName();
+ }
+
+ protected Device(Context context, Device device) {
+ deviceContext = context;
+ name = device.getName();
+ id = device.getId();
+ managerName = device.getManagerName();
+ connected = device.isConnected();
+ scanned = device.isScanned();
+ }
+
+ @Override
+ public String getName() {
+ return this.name;
+ }
+
+ @Override
+ public String getId() {
+ return this.id;
+ }
+
+ @Override
+ public ArrayList getSensors() {
+ return sensorArrayList;
+ }
+
+ @Override
+ public SensorDevice getSensor(int position) {
+ return sensorArrayList.get(position);
+ }
+
+ @Override
+ public String getManagerName() {
+ return managerName;
+ }
+
+ @Override
+ public boolean isConnected() {
+ return connected;
+ }
+
+ @Override
+ public void setConnected(Boolean connected) {
+ this.connected = connected;
+ }
+
+ public boolean isScanned() {
+ return scanned;
+ }
+
+ public void setScanned(boolean scanned) {
+ this.scanned = scanned;
+ }
+
+ public void setAvailability(boolean available){
+ this.available = available;
+ }
+
+ public boolean isAvailable(){return available;}
+
+ @Override
+ public void setBatteryLevel(int percentage) {
+ this.batteryLevel = percentage;
+ }
+
+ @Override
+ public int getBatteryLevel() {
+ return batteryLevel;
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/SuperClasses/Manager.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/SuperClasses/Manager.java
new file mode 100644
index 0000000..1867c12
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/SuperClasses/Manager.java
@@ -0,0 +1,135 @@
+package ch.epfl.esl.elevatedmonitor.SuperClasses;
+
+import android.content.Context;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+
+import ch.epfl.esl.elevatedmonitor.Constants;
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceManager;
+
+
+public abstract class Manager implements InterfaceManager {
+ //--- Variable declaration
+ //------ Private
+ private ArrayList scannedDevices = new ArrayList();
+ private ArrayList connectedDevices = new ArrayList();
+ private ArrayList dataLineString = new ArrayList();
+ private String TAG;
+
+ //------ Protected
+ public Context managerContext;
+
+ //--- Constructor
+ public Manager(Context context, String TAG) {
+ this.managerContext = context;
+ this.TAG = TAG;
+ }
+
+ //--- Functions with the scanned device list
+ protected boolean addScannedDevice(Device device) {
+ boolean added = true;
+ if (scannedDevices.size() != 0) {
+ for (Device d : scannedDevices) {
+ if (d.getId().equals(device.getId())) {
+ added = false;
+ break;
+ }
+ }
+ }
+ if (added) {
+ scannedDevices.add(device);
+ }
+ return added;
+ }
+
+ public String getTag(){return this.TAG;}
+
+ public String getName(){return this.TAG.replaceAll(" Man SQUINCI", "");}
+
+ public ArrayList getScannedDevices() {
+ return scannedDevices;
+ }
+
+ protected void addConnectedDevice(Device device) {
+ connectedDevices.add(device);
+ }
+
+ public ArrayList getConnectedDevice() {
+ return connectedDevices;
+ }
+
+ protected void deleteDisconnectedDevice() {
+ ArrayList iterConnectedDevices = new ArrayList<>(connectedDevices);
+ for (Device device : iterConnectedDevices) {
+ if (!device.isConnected()) {
+ connectedDevices.remove(device);
+ }
+ }
+ ArrayList iterScannedDevices = new ArrayList<>(scannedDevices);
+ for (Device device : iterScannedDevices) {
+ if (!device.isScanned()) {
+ scannedDevices.remove(device);
+ }
+ }
+ }
+
+ //--- Function to check and save list.
+ protected void saveConnectedFile() {
+ ArrayList deviceList = new ArrayList();
+ File path = managerContext.getFilesDir();
+ File file = new File(path, Constants.FILE_DEVICES);
+
+ if (file.exists()) { // Read file
+ try {
+ //---Read device list
+ FileInputStream fis = new FileInputStream(file);
+ ObjectInputStream ois = new ObjectInputStream(fis);
+ deviceList = (ArrayList) ois.readObject();
+ ois.close();
+ fis.close();
+ } catch (IOException | ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+ } else { //Create file
+ try {
+ file.createNewFile();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ //---Output
+ if (deviceList == null) {
+ deviceList = getConnectedDevice();
+ } else {
+ for (Device connectedDevice : getConnectedDevice()) {
+ boolean flagNew = true;
+ for (Device device : deviceList) {
+ if (device.getId().equals(connectedDevice.getId())) {
+ flagNew = false;
+ break;
+ }
+ }
+ if (flagNew) {
+ deviceList.add(connectedDevice);
+ }
+ }
+ }
+
+ //--- Print object
+ try {
+ FileOutputStream fos = new FileOutputStream(file);
+ ObjectOutputStream oos = new ObjectOutputStream(fos);
+ oos.writeObject(deviceList);
+ oos.close();
+ fos.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/SuperClasses/SensorDevice.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/SuperClasses/SensorDevice.java
new file mode 100644
index 0000000..37e1655
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/SuperClasses/SensorDevice.java
@@ -0,0 +1,54 @@
+package ch.epfl.esl.elevatedmonitor.SuperClasses;
+
+import android.content.Context;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+
+import ch.epfl.esl.elevatedmonitor.Interfaces.InterfaceSensor;
+
+public abstract class SensorDevice implements InterfaceSensor, Serializable {
+
+ //--- Declaration
+ protected transient Context sensorContext;
+ protected ArrayList data = new ArrayList<>();
+ private TimeStamp timeStamp = null;
+ protected int id;
+ protected String name;
+
+ //--- Constructor
+ public SensorDevice(Context context) {
+ sensorContext = context;
+ setId();
+ setName();
+ }
+
+ public Data getData(int position) {
+ return data.get(position);
+ }
+
+ public void addData(Data d) {
+ data.add(d);
+ }
+
+ public void addTime(TimeStamp timeStamp){
+ this.timeStamp = timeStamp;
+
+ }
+
+ public int getId() {
+ return this.id;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public ArrayList getAllData() {
+ return data;
+ }
+
+ public TimeStamp getTimeStamp(){
+ return timeStamp;
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/SuperClasses/TimeStamp.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/SuperClasses/TimeStamp.java
new file mode 100644
index 0000000..5082222
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/SuperClasses/TimeStamp.java
@@ -0,0 +1,30 @@
+package ch.epfl.esl.elevatedmonitor.SuperClasses;
+
+import android.util.Log;
+
+import java.io.Serializable;
+import java.util.Calendar;
+
+public abstract class TimeStamp implements Serializable {
+ private Calendar time = null;
+ private boolean canBeread = false;
+
+ public Calendar get() {
+ if (canBeread) {
+ canBeread = false;
+ //Log.d("BESTIA", Calendar.getInstance().getTime().toString() + " you CAN read" );
+
+ }
+ else {
+ time = null;
+ Log.d("BESTIA", Calendar.getInstance().getTime().toString() + " you cannot read" );
+ }
+ return time;
+ }
+
+ public void set(Calendar time) {
+ canBeread = true;
+ this.time = time;
+ }
+
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/SurveyActivity.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/SurveyActivity.java
new file mode 100644
index 0000000..47a06aa
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/SurveyActivity.java
@@ -0,0 +1,305 @@
+package ch.epfl.esl.elevatedmonitor;
+
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.res.ColorStateList;
+import android.os.Bundle;
+import android.os.Environment;
+import android.support.annotation.NonNull;
+import android.support.design.widget.FloatingActionButton;
+import android.support.design.widget.TabLayout;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.Toolbar;
+import android.text.Html;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Calendar;
+
+public class SurveyActivity extends AppCompatActivity {
+
+ // It is static and therefore accessible from static fragments, and passed by reference
+ // to the list adapters, hence it can be used to save the state of each button
+ private static JSONArray questionnaires;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_survey);
+
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+// Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
+
+ // Get the questions
+ final SharedPreferences prefs = getSharedPreferences(Constants.APP_SETTINGS, Context.MODE_PRIVATE);
+ String conf = prefs.getString(Constants.REMOTE_CONFIGURATION, null);
+ if (conf == null) {
+ Toast.makeText(this, "Remote configuration missing. Connect to internet and retry", Toast.LENGTH_LONG).show();
+ finish();
+ }
+ questionnaires = JSON.parseObject(conf).getJSONArray("questionnaires");
+
+ SectionsPagerAdapter mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
+ ViewPager mViewPager = findViewById(R.id.container);
+ mViewPager.setAdapter(mSectionsPagerAdapter);
+
+
+ TabLayout tabLayout = findViewById(R.id.tabs);
+ for (Object question : questionnaires) {
+ tabLayout.addTab(tabLayout.newTab().setText(((JSONObject) question).getString("title_en")));
+ }
+
+ mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
+ tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager));
+
+ FloatingActionButton fab = findViewById(R.id.fab);
+ fab.setOnClickListener(view -> {
+ if (mViewPager.getCurrentItem() < mSectionsPagerAdapter.getCount() - 1) {
+ mViewPager.setCurrentItem(mViewPager.getCurrentItem() + 1, true);
+ } else {
+ JSONObject answers = new JSONObject();
+
+ // Save recording reference in questionnaire
+ answers.put("recording_id", getIntent().getStringExtra("recording_id"));
+
+ // Get a clean set of answers from questionnaire
+ for (Object q : questionnaires) {
+ JSONObject question = (JSONObject) q;
+ JSONObject answer = new JSONObject();
+
+ switch (question.getString("question_type")) {
+ case "choices":
+ for (Object item : question.getJSONArray("choices")) {
+ JSONObject choice = (JSONObject) item;
+ answer.put(choice.getString("choice_code"), choice.getBoolean("checked"));
+ }
+ break;
+
+ case "slider":
+ JSONObject slider = question.getJSONObject("slider");
+ answer.put(slider.getString("slider_code"), slider.getInteger("value"));
+ break;
+
+ default:
+ }
+
+ answers.put(question.getString("title_code"), answer);
+ }
+ String serialized = answers.toJSONString();
+ saveAnswers(serialized);
+ //Snackbar.make(view, "Questionnaire saved. Thank you.", Snackbar.LENGTH_LONG).setAction("Action", null).show();
+ finish();
+
+// // Save and send the data to Firebase
+// FirebaseDatabase database = FirebaseDatabase.getInstance();
+// String identifier = getIntent().getStringExtra("identifier");
+// DatabaseReference myRef = database.getReference("questionnaires/" + identifier);
+//
+// myRef.setValue(serialized).addOnSuccessListener(aVoid -> {
+// Snackbar.make(view, "Questionnaire saved. Thank you.", Snackbar.LENGTH_LONG).setAction("Action", null).show();
+// finish();
+// });
+//
+// Snackbar.make(view, "Saving the questionnaire…", Snackbar.LENGTH_SHORT).setAction("Action", null).show();
+ }
+ });
+
+ mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ if (position == mSectionsPagerAdapter.getCount() - 1) {
+ fab.setBackgroundTintList(ColorStateList.valueOf(getResources().getColor(R.color.colorAccent)));
+ fab.setImageResource(android.R.drawable.ic_menu_send);
+ } else {
+ fab.setBackgroundTintList(ColorStateList.valueOf(getResources().getColor(R.color.colorPrimary)));
+ fab.setImageResource(android.R.drawable.ic_media_next);
+ }
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+
+ }
+ });
+ }
+
+
+ // @Override
+ // public boolean onCreateOptionsMenu(Menu menu) {
+ // // Inflate the menu; this adds items to the action bar if it is present.
+ // getMenuInflater().inflate(R.menu.menu_questionnaire, menu);
+ // return true;
+ // }
+ //
+ // @Override
+ // public boolean onOptionsItemSelected(MenuItem item) {
+ // // Handle action bar item clicks here. The action bar will
+ // // automatically handle clicks on the Home/Up button, so long
+ // // as you specify a parent activity in AndroidManifest.xml.
+ // int id = item.getItemId();
+ //
+ // //noinspection SimplifiableIfStatement
+ // if (id == R.id.action_settings) {
+ // return true;
+ // }
+ //
+ // return super.onOptionsItemSelected(item);
+ // }
+
+ protected static void saveAnswers(String answer) {
+ File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS + File.separator
+ +"Questionnaires");
+ boolean pathIsThere;
+ if (!path.isDirectory()) {
+ pathIsThere = path.mkdirs();
+ } else {
+ pathIsThere = true;
+ }
+ if (pathIsThere) {
+ boolean fileExist = false;
+ File file = new File(path, "Questionnaire_" + Calendar.getInstance().getTime().toString() + ".csv");
+ if (!file.exists()) {
+ //Create file
+ try {
+ fileExist = file.createNewFile();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } else {
+ fileExist = true;
+ }
+ if (fileExist) {
+ try {
+ FileOutputStream stream = new FileOutputStream(file);
+ try {
+ stream.write(answer.getBytes());
+ stream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+
+ public static class PlaceholderFragment extends Fragment {
+ private static final String ARG_SECTION_NUMBER = "section_number";
+
+ public PlaceholderFragment() {
+ }
+
+ static PlaceholderFragment newInstance(int sectionNumber) {
+ PlaceholderFragment fragment = new PlaceholderFragment();
+ Bundle args = new Bundle();
+ args.putInt(ARG_SECTION_NUMBER, sectionNumber);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ assert getArguments() != null;
+ int question_number = getArguments().getInt(ARG_SECTION_NUMBER);
+ JSONObject question = questionnaires.getJSONObject(question_number);
+
+ View rootView;
+ TextView textView;
+
+ switch (question.getString("question_type")) {
+ case "choices":
+ rootView = inflater.inflate(R.layout.fragment_survey_choices, container, false);
+
+ textView = rootView.findViewById(R.id.section_label);
+ textView.setText(Html.fromHtml(question.getString("description_en")));
+
+ RecyclerView rv = rootView.findViewById(R.id.choices_recycler_view);
+ RecyclerViewAdapter adapter = new RecyclerViewAdapter(getContext(), question.getJSONArray("choices"));
+ rv.setLayoutManager(new GridLayoutManager(getContext(), 1));
+ rv.setAdapter(adapter);
+ break;
+
+ case "slider":
+ rootView = inflater.inflate(R.layout.fragment_survey_slider, container, false);
+ JSONObject slider = question.getJSONObject("slider");
+
+ textView = rootView.findViewById(R.id.section_label);
+ textView.setText(Html.fromHtml(question.getString("description_en")));
+
+ SeekBar seekBar = rootView.findViewById(R.id.seekBar);
+ Integer initialValue = slider.getInteger("value");
+ if (initialValue == null) {
+ initialValue = 5;
+ }
+ seekBar.setProgress(initialValue);
+ seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (fromUser) {
+ slider.put("value", progress);
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ // Empty
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ // Empty
+ }
+ });
+ break;
+
+ default:
+ rootView = inflater.inflate(R.layout.fragment_survey_unknown, container, false);
+ }
+
+ return rootView;
+ }
+ }
+
+ class SectionsPagerAdapter extends FragmentPagerAdapter {
+
+ SectionsPagerAdapter(FragmentManager fm) {
+ super(fm);
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ return PlaceholderFragment.newInstance(position);
+ }
+
+ @Override
+ public int getCount() {
+ return questionnaires.size();
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/data/LoginDataSource.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/data/LoginDataSource.java
new file mode 100644
index 0000000..2f993b6
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/data/LoginDataSource.java
@@ -0,0 +1,29 @@
+package ch.epfl.esl.elevatedmonitor.data;
+
+import java.io.IOException;
+
+import ch.epfl.esl.elevatedmonitor.data.model.LoggedInUser;
+
+/**
+ * Class that handles authentication w/ login credentials and retrieves user information.
+ */
+public class LoginDataSource {
+
+ public Result login(String username, String password) {
+
+ try {
+ // TODO: handle loggedInUser authentication
+ LoggedInUser fakeUser =
+ new LoggedInUser(
+ java.util.UUID.randomUUID().toString(),
+ "Jane Doe");
+ return new Result.Success<>(fakeUser);
+ } catch (Exception e) {
+ return new Result.Error(new IOException("Error logging in", e));
+ }
+ }
+
+ public void logout() {
+ // TODO: revoke authentication
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/data/LoginRepository.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/data/LoginRepository.java
new file mode 100644
index 0000000..ac22359
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/data/LoginRepository.java
@@ -0,0 +1,54 @@
+package ch.epfl.esl.elevatedmonitor.data;
+
+import ch.epfl.esl.elevatedmonitor.data.model.LoggedInUser;
+
+/**
+ * Class that requests authentication and user information from the remote data source and
+ * maintains an in-memory cache of login status and user credentials information.
+ */
+public class LoginRepository {
+
+ private static volatile LoginRepository instance;
+
+ private LoginDataSource dataSource;
+
+ // If user credentials will be cached in local storage, it is recommended it be encrypted
+ // @see https://developer.android.com/training/articles/keystore
+ private LoggedInUser user = null;
+
+ // private constructor : singleton access
+ private LoginRepository(LoginDataSource dataSource) {
+ this.dataSource = dataSource;
+ }
+
+ public static LoginRepository getInstance(LoginDataSource dataSource) {
+ if (instance == null) {
+ instance = new LoginRepository(dataSource);
+ }
+ return instance;
+ }
+
+ public boolean isLoggedIn() {
+ return user != null;
+ }
+
+ public void logout() {
+ user = null;
+ dataSource.logout();
+ }
+
+ private void setLoggedInUser(LoggedInUser user) {
+ this.user = user;
+ // If user credentials will be cached in local storage, it is recommended it be encrypted
+ // @see https://developer.android.com/training/articles/keystore
+ }
+
+ public Result login(String username, String password) {
+ // handle login
+ Result result = dataSource.login(username, password);
+ if (result instanceof Result.Success) {
+ setLoggedInUser(((Result.Success) result).getData());
+ }
+ return result;
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/data/Result.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/data/Result.java
new file mode 100644
index 0000000..d83d1b1
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/data/Result.java
@@ -0,0 +1,48 @@
+package ch.epfl.esl.elevatedmonitor.data;
+
+/**
+ * A generic class that holds a result success w/ data or an error exception.
+ */
+public class Result {
+ // hide the private constructor to limit subclass types (Success, Error)
+ private Result() {
+ }
+
+ @Override
+ public String toString() {
+ if (this instanceof Result.Success) {
+ Result.Success success = (Result.Success) this;
+ return "Success[data=" + success.getData().toString() + "]";
+ } else if (this instanceof Result.Error) {
+ Result.Error error = (Result.Error) this;
+ return "Error[exception=" + error.getError().toString() + "]";
+ }
+ return "";
+ }
+
+ // Success sub-class
+ public final static class Success extends Result {
+ private T data;
+
+ public Success(T data) {
+ this.data = data;
+ }
+
+ public T getData() {
+ return this.data;
+ }
+ }
+
+ // Error sub-class
+ public final static class Error extends Result {
+ private Exception error;
+
+ public Error(Exception error) {
+ this.error = error;
+ }
+
+ public Exception getError() {
+ return this.error;
+ }
+ }
+}
diff --git a/app/src/main/java/ch/epfl/esl/elevatedmonitor/data/model/LoggedInUser.java b/app/src/main/java/ch/epfl/esl/elevatedmonitor/data/model/LoggedInUser.java
new file mode 100644
index 0000000..78f78d8
--- /dev/null
+++ b/app/src/main/java/ch/epfl/esl/elevatedmonitor/data/model/LoggedInUser.java
@@ -0,0 +1,23 @@
+package ch.epfl.esl.elevatedmonitor.data.model;
+
+/**
+ * Data class that captures user information for logged in users retrieved from LoginRepository
+ */
+public class LoggedInUser {
+
+ private String userId;
+ private String displayName;
+
+ public LoggedInUser(String userId, String displayName) {
+ this.userId = userId;
+ this.displayName = displayName;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+}
diff --git a/app/src/main/res/anim/blink.xml b/app/src/main/res/anim/blink.xml
new file mode 100644
index 0000000..cd128ce
--- /dev/null
+++ b/app/src/main/res/anim/blink.xml
@@ -0,0 +1,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v24/ic_device_link.xml b/app/src/main/res/drawable-v24/ic_device_link.xml
new file mode 100644
index 0000000..42337c2
--- /dev/null
+++ b/app/src/main/res/drawable-v24/ic_device_link.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable-v24/ic_device_plus_thin.xml b/app/src/main/res/drawable-v24/ic_device_plus_thin.xml
new file mode 100644
index 0000000..698bb22
--- /dev/null
+++ b/app/src/main/res/drawable-v24/ic_device_plus_thin.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..1f6bb29
--- /dev/null
+++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable-v24/ic_list_connection.xml b/app/src/main/res/drawable-v24/ic_list_connection.xml
new file mode 100644
index 0000000..951e8ee
--- /dev/null
+++ b/app/src/main/res/drawable-v24/ic_list_connection.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable-v24/ic_notif_monitoring.xml b/app/src/main/res/drawable-v24/ic_notif_monitoring.xml
new file mode 100644
index 0000000..f5aabf7
--- /dev/null
+++ b/app/src/main/res/drawable-v24/ic_notif_monitoring.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable-v24/info_icon_24.png b/app/src/main/res/drawable-v24/info_icon_24.png
new file mode 100644
index 0000000..06a8af8
Binary files /dev/null and b/app/src/main/res/drawable-v24/info_icon_24.png differ
diff --git a/app/src/main/res/drawable/ic_disk.xml b/app/src/main/res/drawable/ic_disk.xml
new file mode 100644
index 0000000..a299b5d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_disk.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_diskette.xml b/app/src/main/res/drawable/ic_diskette.xml
new file mode 100644
index 0000000..108900f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_diskette.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..2408e30
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_menu_device.xml b/app/src/main/res/drawable/ic_menu_device.xml
new file mode 100644
index 0000000..2c11a02
--- /dev/null
+++ b/app/src/main/res/drawable/ic_menu_device.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_menu_download.xml b/app/src/main/res/drawable/ic_menu_download.xml
new file mode 100644
index 0000000..301b4ec
--- /dev/null
+++ b/app/src/main/res/drawable/ic_menu_download.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_menu_monitoring.xml b/app/src/main/res/drawable/ic_menu_monitoring.xml
new file mode 100644
index 0000000..f631598
--- /dev/null
+++ b/app/src/main/res/drawable/ic_menu_monitoring.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_outline_save_24px.xml b/app/src/main/res/drawable/ic_outline_save_24px.xml
new file mode 100644
index 0000000..b360afe
--- /dev/null
+++ b/app/src/main/res/drawable/ic_outline_save_24px.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_pill.xml b/app/src/main/res/drawable/ic_pill.xml
new file mode 100644
index 0000000..5ce1f37
--- /dev/null
+++ b/app/src/main/res/drawable/ic_pill.xml
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_pills.xml b/app/src/main/res/drawable/ic_pills.xml
new file mode 100644
index 0000000..ecb977f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_pills.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/side_nav_bar.xml b/app/src/main/res/drawable/side_nav_bar.xml
new file mode 100644
index 0000000..8ef428d
--- /dev/null
+++ b/app/src/main/res/drawable/side_nav_bar.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_experiment.xml b/app/src/main/res/layout/activity_experiment.xml
new file mode 100644
index 0000000..3698084
--- /dev/null
+++ b/app/src/main/res/layout/activity_experiment.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_list_view_with_checkbox.xml b/app/src/main/res/layout/activity_list_view_with_checkbox.xml
new file mode 100644
index 0000000..aaf5e5f
--- /dev/null
+++ b/app/src/main/res/layout/activity_list_view_with_checkbox.xml
@@ -0,0 +1,146 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_list_view_with_checkbox_item.xml b/app/src/main/res/layout/activity_list_view_with_checkbox_item.xml
new file mode 100644
index 0000000..fe97c95
--- /dev/null
+++ b/app/src/main/res/layout/activity_list_view_with_checkbox_item.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..153880b
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_monitoring.xml b/app/src/main/res/layout/activity_monitoring.xml
new file mode 100644
index 0000000..8da76f3
--- /dev/null
+++ b/app/src/main/res/layout/activity_monitoring.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_permission.xml b/app/src/main/res/layout/activity_permission.xml
new file mode 100644
index 0000000..91b91f0
--- /dev/null
+++ b/app/src/main/res/layout/activity_permission.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_save.xml b/app/src/main/res/layout/activity_save.xml
new file mode 100644
index 0000000..c397430
--- /dev/null
+++ b/app/src/main/res/layout/activity_save.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml
new file mode 100644
index 0000000..bf9cfbb
--- /dev/null
+++ b/app/src/main/res/layout/activity_settings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_survey.xml b/app/src/main/res/layout/activity_survey.xml
new file mode 100644
index 0000000..281582f
--- /dev/null
+++ b/app/src/main/res/layout/activity_survey.xml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/app_bar_list_devices.xml b/app/src/main/res/layout/app_bar_list_devices.xml
new file mode 100644
index 0000000..ff76951
--- /dev/null
+++ b/app/src/main/res/layout/app_bar_list_devices.xml
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/app_bar_monitoring.xml b/app/src/main/res/layout/app_bar_monitoring.xml
new file mode 100644
index 0000000..a569862
--- /dev/null
+++ b/app/src/main/res/layout/app_bar_monitoring.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/app_bar_save.xml b/app/src/main/res/layout/app_bar_save.xml
new file mode 100644
index 0000000..b52e975
--- /dev/null
+++ b/app/src/main/res/layout/app_bar_save.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/app_bar_settings.xml b/app/src/main/res/layout/app_bar_settings.xml
new file mode 100644
index 0000000..71a1a69
--- /dev/null
+++ b/app/src/main/res/layout/app_bar_settings.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/content_chuv.xml b/app/src/main/res/layout/content_chuv.xml
new file mode 100644
index 0000000..c86308f
--- /dev/null
+++ b/app/src/main/res/layout/content_chuv.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/content_experiment.xml b/app/src/main/res/layout/content_experiment.xml
new file mode 100644
index 0000000..5608e6b
--- /dev/null
+++ b/app/src/main/res/layout/content_experiment.xml
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/content_list_monitoring.xml b/app/src/main/res/layout/content_list_monitoring.xml
new file mode 100644
index 0000000..f2ddeb5
--- /dev/null
+++ b/app/src/main/res/layout/content_list_monitoring.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/content_list_save.xml b/app/src/main/res/layout/content_list_save.xml
new file mode 100644
index 0000000..65da3ab
--- /dev/null
+++ b/app/src/main/res/layout/content_list_save.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml
new file mode 100644
index 0000000..1b94a39
--- /dev/null
+++ b/app/src/main/res/layout/content_main.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/content_permission.xml b/app/src/main/res/layout/content_permission.xml
new file mode 100644
index 0000000..e8d8704
--- /dev/null
+++ b/app/src/main/res/layout/content_permission.xml
@@ -0,0 +1,11 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_add_monitor.xml b/app/src/main/res/layout/dialog_add_monitor.xml
new file mode 100644
index 0000000..b71e1ad
--- /dev/null
+++ b/app/src/main/res/layout/dialog_add_monitor.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_add_session.xml b/app/src/main/res/layout/dialog_add_session.xml
new file mode 100644
index 0000000..171cbee
--- /dev/null
+++ b/app/src/main/res/layout/dialog_add_session.xml
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_saving.xml b/app/src/main/res/layout/fragment_saving.xml
new file mode 100644
index 0000000..023c978
--- /dev/null
+++ b/app/src/main/res/layout/fragment_saving.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_survey_choices.xml b/app/src/main/res/layout/fragment_survey_choices.xml
new file mode 100644
index 0000000..4e8ebea
--- /dev/null
+++ b/app/src/main/res/layout/fragment_survey_choices.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_survey_slider.xml b/app/src/main/res/layout/fragment_survey_slider.xml
new file mode 100644
index 0000000..354aaf1
--- /dev/null
+++ b/app/src/main/res/layout/fragment_survey_slider.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_survey_unknown.xml b/app/src/main/res/layout/fragment_survey_unknown.xml
new file mode 100644
index 0000000..0e766dc
--- /dev/null
+++ b/app/src/main/res/layout/fragment_survey_unknown.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/list_device.xml b/app/src/main/res/layout/list_device.xml
new file mode 100644
index 0000000..91c3082
--- /dev/null
+++ b/app/src/main/res/layout/list_device.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/list_monitor.xml b/app/src/main/res/layout/list_monitor.xml
new file mode 100644
index 0000000..907e262
--- /dev/null
+++ b/app/src/main/res/layout/list_monitor.xml
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/list_scanned_device.xml b/app/src/main/res/layout/list_scanned_device.xml
new file mode 100644
index 0000000..cb94c51
--- /dev/null
+++ b/app/src/main/res/layout/list_scanned_device.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/list_session.xml b/app/src/main/res/layout/list_session.xml
new file mode 100644
index 0000000..1e665a5
--- /dev/null
+++ b/app/src/main/res/layout/list_session.xml
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/nav_header.xml b/app/src/main/res/layout/nav_header.xml
new file mode 100644
index 0000000..dd911ad
--- /dev/null
+++ b/app/src/main/res/layout/nav_header.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/pop_out.xml b/app/src/main/res/layout/pop_out.xml
new file mode 100644
index 0000000..023c978
--- /dev/null
+++ b/app/src/main/res/layout/pop_out.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/question_card.xml b/app/src/main/res/layout/question_card.xml
new file mode 100644
index 0000000..8a34093
--- /dev/null
+++ b/app/src/main/res/layout/question_card.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/activity_list_drawer.xml b/app/src/main/res/menu/activity_list_drawer.xml
new file mode 100644
index 0000000..e2b6f3b
--- /dev/null
+++ b/app/src/main/res/menu/activity_list_drawer.xml
@@ -0,0 +1,24 @@
+
+
diff --git a/app/src/main/res/menu/menu_advanced.xml b/app/src/main/res/menu/menu_advanced.xml
new file mode 100644
index 0000000..52592c7
--- /dev/null
+++ b/app/src/main/res/menu/menu_advanced.xml
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..060f26f
--- /dev/null
+++ b/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,17 @@
+
diff --git a/app/src/main/res/menu/monitoring.xml b/app/src/main/res/menu/monitoring.xml
new file mode 100644
index 0000000..2c5178a
--- /dev/null
+++ b/app/src/main/res/menu/monitoring.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/app/src/main/res/menu/save.xml b/app/src/main/res/menu/save.xml
new file mode 100644
index 0000000..2c5178a
--- /dev/null
+++ b/app/src/main/res/menu/save.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..4ae7d12
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..4ae7d12
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..79eaf61
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_background.png b/app/src/main/res/mipmap-hdpi/ic_launcher_background.png
new file mode 100644
index 0000000..6ebce6a
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_background.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..fa460fc
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..bf92098
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..5b01d24
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_background.png b/app/src/main/res/mipmap-mdpi/ic_launcher_background.png
new file mode 100644
index 0000000..78e3920
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_background.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..b463198
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..338ba50
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..f6064bb
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png
new file mode 100644
index 0000000..8a578a5
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..496467c
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..7de79c4
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..f7f419c
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png
new file mode 100644
index 0000000..da46328
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..b74c0d7
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..6af474f
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..efba2ca
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png
new file mode 100644
index 0000000..df3db4d
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..7ab2b5a
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..0f6b8d0
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/raw/alarm.mp3 b/app/src/main/res/raw/alarm.mp3
new file mode 100644
index 0000000..a65cd0a
Binary files /dev/null and b/app/src/main/res/raw/alarm.mp3 differ
diff --git a/app/src/main/res/raw/app_remote_config.json b/app/src/main/res/raw/app_remote_config.json
new file mode 100644
index 0000000..1ae6548
--- /dev/null
+++ b/app/src/main/res/raw/app_remote_config.json
@@ -0,0 +1,297 @@
+{
+ "NOTE": "RELEASE CONFIG",
+ "config_version": "2019-04-11 dev3",
+ "prerecording_min": 1,
+ "postrecording_min": 1,
+ "questionnaires": [
+ {
+ "title_code": "ATTACK_INTENSITY",
+ "title_en": "Int",
+ "description_en": "Intensit\u00e9 de la douleur, de nulle \u00e0 extr\u00eame.",
+ "question_type": "slider",
+ "slider": {
+ "slider_code": "PAIN_ESTIMATION"
+ }
+ },
+ {
+ "title_code": "Pain localization",
+ "title_en": "Loc",
+ "description_en": "Quelle est la partie de la t\u00eate qui fait mal ?",
+ "question_type": "choices",
+ "choices": [
+ {
+ "choice_code": "Right_ear",
+ "choice_en": "Oeil droit"
+ },
+ {
+ "choice_code": "Left_ear",
+ "choice_en": "Oeil gauche"
+ },
+ {
+ "choice_code": "Forehead_right",
+ "choice_en": "Front droit"
+ },
+ {
+ "choice_code": "Forehead_left",
+ "choice_en": "Front gauche"
+ },
+ {
+ "choice_code": "Temple right",
+ "choice_en": "Tempe droite"
+ },
+ {
+ "choice_code": "Tempple left",
+ "choice_en": "Tempe gauche"
+ },
+ {
+ "choice_code": "Upper part of the right head",
+ "choice_en": "Partie superi\u00e9ure de la t\u00eate droite"
+ },
+ {
+ "choice_code": "Upper part of the left head",
+ "choice_en": "Partie superi\u00e9ure de la t\u00eate gauche"
+ },
+ {
+ "choice_code": "Occiput right",
+ "choice_en": "Occiput droit"
+ },
+ {
+ "choice_code": "Occiput left",
+ "choice_en": "Occiput gauche"
+ },
+ {
+ "choice_code": "Neck right",
+ "choice_en": "Nuque droite"
+ },
+ {
+ "choice_code": "Neck left",
+ "choice_en": "Nuque gauche"
+ }
+ ]
+ },
+ {
+ "title_code": "SYMPTOMS during prodromal",
+ "title_en": "Sym Pro",
+ "description_en": "Quels sont les symptômes prodromiques associés (avant début de la douleur) ?",
+ "question_type": "choices",
+ "choices": [
+ {
+ "choice_code": "Nothing",
+ "choice_en": "Aucun"
+ },
+ {
+ "choice_code": "Mood",
+ "choice_en": "Changement d'humeur"
+ },
+ {
+ "choice_code": "Fatigue",
+ "choice_en": "Fatigue"
+ },
+ {
+ "choice_code": "WEAKENING",
+ "choice_en": "Affaiblissement"
+ },
+ {
+ "choice_code": "Concentration",
+ "choice_en": "Trouble de la concentration"
+ },
+ {
+ "choice_code": "Bayement",
+ "choice_en": "Bayement"
+ },
+ {
+ "choice_code": "MUSCLE_WEAKNESS",
+ "choice_en": "Faiblesse musculaire"
+ },
+ {
+ "choice_code": "SIGHT_PROBLEM",
+ "choice_en": "Probl\u00e8me de vue"
+ },
+ {
+ "choice_code": "SENSITIVITY_LIGHT",
+ "choice_en": "Sensibilit\u00e9 \u00e0 la lumi\u00e8re"
+ },
+ {
+ "choice_code": "SENSITIVITY_NOISE",
+ "choice_en": "Sensibilit\u00e9 au bruit"
+ },
+ {
+ "choice_code": "THIRST",
+ "choice_en": "Soif"
+ },
+ {
+ "choice_code": "OTHER",
+ "choice_en": "Autre"
+ }
+ ]
+ },
+ {
+ "title_code": "SYMPTOMS during pain",
+ "title_en": "Sym Douleur",
+ "description_en": "Quels sont les symptomes associ\u00e9s pendant la crise (douleur) ?",
+ "question_type": "choices",
+ "choices": [
+ {
+ "choice_code": "Nothing",
+ "choice_en": "Aucun"
+ },
+ {
+ "choice_code": "PAIN_PULSE",
+ "choice_en": "Douleurs pulsatiles"
+ },
+ {
+ "choice_code": "NAUSEA",
+ "choice_en": "Naus\u00e9es"
+ },
+ {
+ "choice_code": "VOMITTING",
+ "choice_en": "Vomissement"
+ },
+ {
+ "choice_code": "MOOD_SWING",
+ "choice_en": "Changement d\u2019humeur"
+ },
+ {
+ "choice_code": "IRRITABILITY",
+ "choice_en": "Irritabilit\u00e9"
+ },
+ {
+ "choice_code": "FATIGUE",
+ "choice_en": "Fatigue"
+ },
+ {
+ "choice_code": "WEAKENING",
+ "choice_en": "Faiblesse"
+ },
+ {
+ "choice_code": "CANNOT_FOCUS",
+ "choice_en": "Concentration"
+ },
+ {
+ "choice_code": "BAYEMENT",
+ "choice_en": "Bayement"
+ },
+ {
+ "choice_code": "MUSCLE_SENSITIVITY",
+ "choice_en": "Sensibilit\u00e9 musculaire"
+ },
+ {
+ "choice_code": "SIGHT_PROBLEM",
+ "choice_en": "Probl\u00e8me de vue"
+ },
+ {
+ "choice_code": "SENSITIVITY_LIGHT",
+ "choice_en": "Sensibilit\u00e9 \u00e0 la lumi\u00e8re"
+ },
+ {
+ "choice_code": "SENSITIVITY_NOISE",
+ "choice_en": "Sensibilit\u00e9 au bruit"
+ },
+ {
+ "choice_code": "THIRST",
+ "choice_en": "Soif"
+ },
+ {
+ "choice_code": "OTHER",
+ "choice_en": "Autre"
+ }
+ ]
+ },
+ {
+ "title_code": "HANDLING",
+ "title_en": "Gestion",
+ "description_en": "Qu\u2019avez-vous fait pour g\u00e9rer la crise migraineuse ?",
+ "question_type": "choices",
+ "choices": [
+ {
+ "choice_code": "NOTHING",
+ "choice_en": "Rien"
+ },
+ {
+ "choice_code": "DRUGS",
+ "choice_en": "Prise de m\u00e9dicaments"
+ },
+ {
+ "choice_code": "CAFFEINE",
+ "choice_en": "Caf\u00e9ine"
+ },
+ {
+ "choice_code": "COMPORTMENT",
+ "choice_en": "Technique comportementale"
+ },
+ {
+ "choice_code": "RESTING",
+ "choice_en": "Repos"
+ },
+ {
+ "choice_code": "OTHER",
+ "choice_en": "Autre"
+ }
+ ]
+ },
+ {
+ "title_code": "TRIGGERS",
+ "title_en": "D\u00e9cle",
+ "description_en": "Quels sont les d\u00e9clencheurs potentiels ?",
+ "question_type": "choices",
+ "choices": [
+ {
+ "choice_code": "Nothing",
+ "choice_en": "Aucun"
+ },
+ {
+ "choice_code": "STRESS",
+ "choice_en": "Stress"
+ },
+ {
+ "choice_code": "ANXIETY",
+ "choice_en": "Anxi\u00e9t\u00e9"
+ },
+ {
+ "choice_code": "LACK_SLEEP",
+ "choice_en": "Manque de sommeil"
+ },
+ {
+ "choice_code": "PHYSICAL_EFFORT",
+ "choice_en": "Effort physique"
+ },
+ {
+ "choice_code": "SKIPPED_LUNCH",
+ "choice_en": "Oubli d\u2019un repas"
+ },
+ {
+ "choice_code": "CAFFEINE",
+ "choice_en": "Manque de caf\u00e9ine"
+ },
+ {
+ "choice_code": "ALCOHOL",
+ "choice_en": "Alcool"
+ },
+ {
+ "choice_code": "FOOD",
+ "choice_en": "Nourriture"
+ },
+ {
+ "choice_code": "DEHYDRATION",
+ "choice_en": "Pas assez bu"
+ },
+ {
+ "choice_code": "NOISE_DISTURBANCE",
+ "choice_en": "Nuisance sonore"
+ },
+ {
+ "choice_code": "BRIGHTNESS",
+ "choice_en": "Luminosit\u00e9"
+ },
+ {
+ "choice_code": "TEMPERATURE",
+ "choice_en": "Temp\u00e9rature"
+ },
+ {
+ "choice_code": "Others",
+ "choice_en": "Autre"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/app/src/main/res/raw/app_remote_config_two.json b/app/src/main/res/raw/app_remote_config_two.json
new file mode 100644
index 0000000..370ae1f
--- /dev/null
+++ b/app/src/main/res/raw/app_remote_config_two.json
@@ -0,0 +1,173 @@
+{
+ "NOTE": "RELEASE CONFIG",
+ "config_version": "2019-04-11 dev3",
+ "prerecording_min": 1,
+ "postrecording_min": 1,
+ "questionnaires": [
+ {
+ "title_code": "Stress Level",
+ "title_en": "Stress Level",
+ "description_en": "Stress level, from relaxed to extreme.",
+ "question_type": "slider",
+ "slider": {
+ "slider_code": "PAIN_ESTIMATION"
+ }
+ },
+ {
+ "title_code": "SYMPTOMS",
+ "title_en": "Symptomes",
+ "description_en": "Quels sont les symptomes associ\u00e9s ?",
+ "question_type": "choices",
+ "choices": [
+ {
+ "choice_code": "PAIN_SHOOTING",
+ "choice_en": "Douleurs lancinantes"
+ },
+ {
+ "choice_code": "PAIN_PULSE",
+ "choice_en": "Douleurs pulsatiles"
+ },
+ {
+ "choice_code": "NAUSEA",
+ "choice_en": "Naus\u00e9es"
+ },
+ {
+ "choice_code": "VOMITTING",
+ "choice_en": "Vomissements"
+ },
+ {
+ "choice_code": "MOOD_SWING",
+ "choice_en": "Changement d\u2019humeur"
+ },
+ {
+ "choice_code": "IRRITABILITY",
+ "choice_en": "Irritabilit\u00e9"
+ },
+ {
+ "choice_code": "FATIGUE",
+ "choice_en": "Fatigue"
+ },
+ {
+ "choice_code": "WEAKENING",
+ "choice_en": "Affaiblissement"
+ },
+ {
+ "choice_code": "CANNOT_FOCUS",
+ "choice_en": "Trouble de la concentration"
+ },
+ {
+ "choice_code": "SLEEPINESS",
+ "choice_en": "Endormissement"
+ },
+ {
+ "choice_code": "MUSCLE_SENSITIVITY",
+ "choice_en": "Sensibilit\u00e9 musculaire"
+ },
+ {
+ "choice_code": "MUSCLE_WEAKNESS",
+ "choice_en": "Faiblesse musculaire"
+ },
+ {
+ "choice_code": "SIGHT_PROBLEM",
+ "choice_en": "Probl\u00e8me de vue"
+ },
+ {
+ "choice_code": "SENSITIVITY_LIGHT",
+ "choice_en": "Sensibilit\u00e9 \u00e0 la lumi\u00e8re"
+ },
+ {
+ "choice_code": "SENSITIVITY_NOISE",
+ "choice_en": "Sensibilit\u00e9 au bruit"
+ },
+ {
+ "choice_code": "THIRST",
+ "choice_en": "Soif"
+ },
+ {
+ "choice_code": "OTHER",
+ "choice_en": "Autre"
+ }
+ ]
+ },
+ {
+ "title_code": "HANDLING",
+ "title_en": "Gestion",
+ "description_en": "Qu\u2019avez-vous fait pour g\u00e9rer la crise migraineuse ?",
+ "question_type": "choices",
+ "choices": [
+ {
+ "choice_code": "DRUGS",
+ "choice_en": "Prise de m\u00e9dicaments"
+ },
+ {
+ "choice_code": "CAFFEINE",
+ "choice_en": "Caf\u00e9ine"
+ },
+ {
+ "choice_code": "COMPORTMENT",
+ "choice_en": "Technique comportementale"
+ },
+ {
+ "choice_code": "RESTING",
+ "choice_en": "Repos"
+ }
+ ]
+ },
+ {
+ "title_code": "TRIGGERS",
+ "title_en": "D\u00e9clencheurs",
+ "description_en": "Quels sont les d\u00e9clencheurs potentiels ?",
+ "question_type": "choices",
+ "choices": [
+ {
+ "choice_code": "STRESS",
+ "choice_en": "Stress"
+ },
+ {
+ "choice_code": "ANXIETY",
+ "choice_en": "Anxi\u00e9t\u00e9"
+ },
+ {
+ "choice_code": "LACK_SLEEP",
+ "choice_en": "Manque de sommeil"
+ },
+ {
+ "choice_code": "PHYSICAL_EFFORT",
+ "choice_en": "Effort physique"
+ },
+ {
+ "choice_code": "SKIPPED_LUNCH",
+ "choice_en": "Oubli d\u2019un repas"
+ },
+ {
+ "choice_code": "CAFFEINE",
+ "choice_en": "Caf\u00e9ine"
+ },
+ {
+ "choice_code": "ALCOHOL",
+ "choice_en": "Alcool"
+ },
+ {
+ "choice_code": "FOOD",
+ "choice_en": "Nourriture"
+ },
+ {
+ "choice_code": "DEHYDRATION",
+ "choice_en": "D\u00e9shydratation"
+ },
+ {
+ "choice_code": "NOISE_DISTURBANCE",
+ "choice_en": "Nuisance sonore"
+ },
+ {
+ "choice_code": "BRIGHTNESS",
+ "choice_en": "Luminosit\u00e9"
+ },
+ {
+ "choice_code": "TEMPERATURE",
+ "choice_en": "Temp\u00e9rature"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/app/src/main/res/raw/relaxing.mp3 b/app/src/main/res/raw/relaxing.mp3
new file mode 100644
index 0000000..86b0305
Binary files /dev/null and b/app/src/main/res/raw/relaxing.mp3 differ
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
new file mode 100644
index 0000000..2adbdbb
--- /dev/null
+++ b/app/src/main/res/values-fr/strings.xml
@@ -0,0 +1,83 @@
+
+
+ ElevatedMonitor
+ ajouter
+ supprimer
+ Paramètres
+ Ouvrir une session ou s\'inscrire
+ Bouton
+ Impossible de collecter un questionnaire avant de commencer à stocker les données.
+ Fermer
+ Veuillez connecter un appareil avant d\'ajouter une nouvelle session.
+ Connecté
+ Redémarrage de l\'application après le crash
+ On dirait que votre appareil n\'a pas accès à Internet, veuillez activer la connexion Internet et réintroduire Empatica parmi les appareils choisis.
+ L\'appareil n\'a pas été détecté.
+ Débrancher tous les appareils
+ Rejeter
+ Erreur lors de l\'activation du bluetooth
+ Aller à Paramètres
+ Veuillez connecter au moins un appareil avant de commencer à enregistrer les données.
+ Entrée mémorisée
+ Mot de passe requis
+ Permission Activity
+ Veuillez vérifier les réglages pour avoir inséré la bonne clé API EMPATICA E4.
+ Débrancher et arrêter
+ Le Device Manager est en cours d\'exécution.
+ Pour modifier les paramètres de l\'application, veuillez entrer le mot de passe.
+ Vous avez signalé la fin d\'une situation.
+ Vous avez signalé le début d\'une situation.
+ L\'appareil a été débranché.
+ "Niveau de stress : "
+ On dirait qu\'un ou plusieurs appareils ont été connectés après que nous ayons commencé à sauvegarder. Appuyez sur Ajouter de nouveaux périphériques pour commencer une nouvelle sauvegarde avec l\'inclusion des nouveaux périphériques. Appuyez sur Ignorer pour ignorer les nouveaux périphériques.
+ Solution insert
+ Mode d\'enregistrement automatique désactivé
+ Mode d\'enregistrement automatique activé
+ Numéro du bloc de bois :
+ Tous les capteurs
+ Ajouter de nouveaux appareils
+ Actif
+ Modifier l\'API
+ Appareil
+ Empatica : déconnexion, veuillez appuyer sur le bouton de l\'appareil.
+ Je stocke déjà les données !
+ Ignorer les nouveaux périphériques
+ Suivant
+ Aucun appareil
+ ajouter EMPATICA
+ Connectant
+ Date
+ Aucun appareil n\'a été choisi. Veuillez insérer au moins un.
+ Nom d\'utilisateur non valide
+ Ouvrir le tiroir de navigation
+ Veuillez sélectionner un appareil.
+ Veuillez sélectionner un capteur.
+ Veuillez patienter pendant que nous nous connectons à l\'appareil. Assurez-vous que l\'appareil s\'allume.
+ Vis restantes :
+ Sélection inversée
+ J\'ai commencé à stocker des données
+ Je me suis arrêté pour stocker des données
+ Scanner..
+ Sélectionner tout
+ Sélectionnez Aucun
+ Paramètres
+ Début
+ Commencez à vous connecter
+ Prodromal:
+ Aller à Paramètres
+ Douleur:
+ Clé API manquante
+ Déconnexion appelée
+ Je les ai tous !
+ " est déjà "
+ Allez dans les réglages et insérez une clé API EMPATICA E4 valide.
+ Allez dans les réglages et insérez votre clé API EMPATICA E4.
+ Clé API erronée
+ Mot de passe incorrect
+ Données rejetées
+ Choisissez le type de médicament
+ S\'il vous plaît, choisissez quelque chose
+ Effectué
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
new file mode 100644
index 0000000..eb0e07f
--- /dev/null
+++ b/app/src/main/res/values/attrs.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..c364d1f
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,22 @@
+
+
+ #008577
+ #00574B
+ #D81B60
+ #FFFFFF
+ #FFFF00
+ #FF00FF
+ #FF0000
+ #C0C0C0
+ #808080
+ #808000
+ #800080
+ #800000
+ #00FFFF
+ #00FF00
+ #008080
+ #008000
+ #0000FF
+ #000080
+ #000000
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..a693c32
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,16 @@
+
+
+ 16dp
+ 16dp
+ 8dp
+ 176dp
+ 16dp
+ 10dp
+ 70sp
+ 14dp
+ 25dp
+ 7dp
+ 3dp
+ 70sp
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..b275587
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,169 @@
+
+ ElevatedMonitor
+ Settings
+ Auto Connect
+ AutoConnect
+ ch.epfl.elevatedmonitor.PREFERENCE_FILE_KEY
+ ch.epfl.elevatedmonitor.SELECTED_DEVICES_KEY
+ ch.epfl.elevatedmonitor.AUTO_SAVE__KEY
+ ch.epfl.elevatedmonitor.KEY_SET
+ ch.epfl.elevatedmonitor.QUESTIONNAIRE_NEEDED_KEY
+ Unknown question type.\nPlease check the online configuration.
+ Auto Saving mode enabled
+ Auto Saving mode disabled
+ Start to Connect
+ Reverse Select
+ Select None
+ Select All
+ Device Manager
+ The device has been disconnected.
+ Device Manager is running.
+ Connecting
+ Connected
+ Device not detected.
+ Features from signal
+ My Phone
+ Navigation header
+ Monitoring app
+ EPFL - ESL
+ Devices
+ Settings
+ Monitoring
+ Open navigation drawer
+ Close navigation drawer
+ Error while enabling bluetooth
+ Please select a device.
+ Please select a sensor.
+ Please wait while we are connecting to device. Ensure that device turn on.
+ Device
+ Sensor
+ All sensors
+ Cancel
+ Add
+ Please connect a device before adding a new session.
+ Date
+ Active
+ Auto Save
+ I\'m already saving!
+ Looks like one or more devices have been connected after we have started to save. Press Add New Devices to start a new saving with the inclusion of the new devices. Press Ignore to ignore the new devices.
+ Add new devices
+ Ignore new devices
+ Saving started
+ Saving stopped.
+ Disconnect all devices
+ Scanning…
+ Please connect at least one device before starting to save.
+ Metawear status
+ Temperature
+ Close
+ Info
+ Can\'t collect a questionnaire before starting to save.
+ API Key
+ Empatica E4 key
+ Login
+ Change API
+ Dismiss
+ Go to Settings
+ Looks like your device has no access to Internet, please enable the internet connection and re-introduce Empatica among the chosen devices
+ Please check the settings to have inserted the correct EMPATICA E4 API Key
+ ch.epfl.esl.elevatedmonitor.Devices.Empatica_E4.Empatica
+ Empatica: disconnection, please press the button on the device
+ Schindler
+ CHUV
+ ch.epfl.elevatedmonitor.SELECTED_PROFILE
+ ExperimentActivity
+ Experiment
+ Next
+ Stress level:
+ Seconds remaining:
+ Phase One
+ Phase Two
+ Phase Three
+ Start
+ Remaining screws:
+ Phase four completed!
+ Phase Four
+ Phase Five
+ Seconds elapsed:
+ Phase five completed!
+ FINISH
+ Block of wood number:
+ Insert solution
+ Phase six
+ Phase six completed
+ Breathing Rate
+ Minute Ventilation
+ Raw Respiration
+ Respiration Circuit Temperature
+ Empatica
+ Sign in
+ Email
+ Password
+ Sign in or register
+ Sign in
+ "Welcome !"
+ Not a valid username
+ Password must be >5 characters
+ "Login failed"
+ "Experiment Name"
+
+
+ - Welcome to the new experiment:\nI would like to ask you to stand, arms by your side, for 1 minute.\nIn the meantime, please enjoy the music.
+ - Thank you; now please stand with your arm raised for one minute.\nDon’t forget to enjoy the music.
+ - Very good, now squat down for one minute.
+ - Please walk in place for one minute.
+ - Please sit down for a minute.
+ - Now insert your perceived stress level in the bar above.\nWhen you are ready, \npress the button NEXT to start the second phase of the experiment
+
+
+ - During the second part of the experiment, I would ask you to tight some screws in the block of wood number one. When you are ready, press the button START.
+ - Please collect the screwdriver on your right. And one screw only from your left at a time.\nTight the screw until it is flush with the wood block. \nYou have four minutes, tight as many screws as you can.
+ - Now insert your perceived stress level in the bar above.\nComplete by pressing the button NEXT.
+
+
+ - Please relax for 1.5 minute.
+ - Please insert your perceived stress level in the bar above. \nComplete by pressing the button NEXT.
+
+
+ - This time, you are asked to tight a total of 6 screwsb>. \nEvery time you pick up a new screw, refer to the number on the screen to understand which block of wood the screw has to be tightened in.\nWhen you press START a countdown will start and you should have completely tightened the screw and answered the question before it reaches zero. \nWhen you have finished tightening one screw, press the counter itself to restart it and look at the screen for the number of the block that has to be considered.
+ - Please insert your perceived stress level in the bar above. \nComplete by pressing the button FINISH.
+
+
+ - Please relax for 1.5 minute.
+ - Please insert your perceived stress level in the bar above. \nComplete by pressing the button NEXT.
+
+
+ - This time, you have to tight 6 screws as fast as possible.\n Block of wood: one.\nWhen you press START a countdown will start and you should have completely tightened the screw and answered the question before it reaches zero. \nWhen you have finished tightening one screw, press the counter itself to restart it.
+ - You have completed this part. \nPlease insert your perceived stress level in the bar above. \nComplete by pressing the button NEXT.
+
+ permissionActivity
+ ProChecked
+ PainChecked
+ First tim ever
+ Init Pro
+ init pain
+ No devices
+ No devices have been chosen. Please insert at least one.
+ add Empatica
+ App restarted after crash
+ Entry saved
+ To change the app settings, please insert the password.
+ Password Required
+ Disconnect and stop
+ Go to Settings
+ Prodromal:
+ Pain:
+ Please go to the settings and insert your EMPATICA E4 API Key
+ API Key missing
+ Wrong API Key
+ Please go to the settings and insert a valid EMPATICA E4 API Key
+ Disconnection called
+ is already
+ I\'ve got all of them!
+ Wrong Password
+ Entry Rejected
+ Choose the type of drug
+ Please, choose someting
+ Done
+
+
diff --git a/app/src/main/res/values/strings_empatica.xml b/app/src/main/res/values/strings_empatica.xml
new file mode 100644
index 0000000..c178359
--- /dev/null
+++ b/app/src/main/res/values/strings_empatica.xml
@@ -0,0 +1,10 @@
+
+ Temperature
+ Blood Volume Pulse
+ Electrodermal Activity
+ Interbeat Interval
+ Accelerometer
+ Button
+ You have reported the start of a situation.
+ You have reported the end of a situation.
+
diff --git a/app/src/main/res/values/strings_metamotion.xml b/app/src/main/res/values/strings_metamotion.xml
new file mode 100644
index 0000000..7ae5b18
--- /dev/null
+++ b/app/src/main/res/values/strings_metamotion.xml
@@ -0,0 +1,5 @@
+
+ Accelerometer
+ Sensor Fusion
+
+
diff --git a/app/src/main/res/values/strings_phone.xml b/app/src/main/res/values/strings_phone.xml
new file mode 100644
index 0000000..4a49bd1
--- /dev/null
+++ b/app/src/main/res/values/strings_phone.xml
@@ -0,0 +1,7 @@
+
+ My Phone
+ Proximity
+ Temperature
+ Accelerometer
+ Light
+
diff --git a/app/src/main/res/values/strings_shirt.xml b/app/src/main/res/values/strings_shirt.xml
new file mode 100644
index 0000000..a97eb98
--- /dev/null
+++ b/app/src/main/res/values/strings_shirt.xml
@@ -0,0 +1,8 @@
+
+ Heart Rate
+ Respiration Rate
+ Insp Exp
+ Step Count
+ Activity
+ Cadence
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..c712d3d
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/test/java/ch/epfl/esl/elevatedmonitor/ExampleUnitTest.java b/app/src/test/java/ch/epfl/esl/elevatedmonitor/ExampleUnitTest.java
new file mode 100644
index 0000000..fd44102
--- /dev/null
+++ b/app/src/test/java/ch/epfl/esl/elevatedmonitor/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package ch.epfl.esl.elevatedmonitor;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..081a3e1
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,33 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ google()
+ jcenter()
+
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.5.1'
+ classpath 'com.google.gms:google-services:4.2.0'
+
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ ivy {
+ url "https://mbientlab.com/releases/ivyrep"
+ layout "gradle"
+ }
+
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/circularprogressbar-example/.gitignore b/circularprogressbar-example/.gitignore
new file mode 100644
index 0000000..5d9a390
--- /dev/null
+++ b/circularprogressbar-example/.gitignore
@@ -0,0 +1,4 @@
+/build
+/libs
+/proguard-rules.pro
+/circularprogressbar-example.iml
diff --git a/circularprogressbar-example/build.gradle b/circularprogressbar-example/build.gradle
new file mode 100644
index 0000000..15171e7
--- /dev/null
+++ b/circularprogressbar-example/build.gradle
@@ -0,0 +1,25 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion androidCompileSdkVersion
+ defaultConfig {
+ applicationId "com.mikhaellopez.circularprogressbarsample"
+ minSdkVersion androidMinSdkVersion
+ targetSdkVersion androidTargetSdkVersion
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ //implementation project(':circularprogressbar')
+ implementation "com.mikhaellopez:circularprogressbar:$androidVersionName"
+ implementation appCompat
+ implementation lobsterPicker
+}
diff --git a/circularprogressbar-example/src/main/AndroidManifest.xml b/circularprogressbar-example/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..5004550
--- /dev/null
+++ b/circularprogressbar-example/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/circularprogressbar-example/src/main/java/com/mikhaellopez/circularprogressbarsample/MainActivity.java b/circularprogressbar-example/src/main/java/com/mikhaellopez/circularprogressbarsample/MainActivity.java
new file mode 100644
index 0000000..1f7d088
--- /dev/null
+++ b/circularprogressbar-example/src/main/java/com/mikhaellopez/circularprogressbarsample/MainActivity.java
@@ -0,0 +1,158 @@
+package com.mikhaellopez.circularprogressbarsample;
+
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.ColorInt;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.CompoundButton;
+import android.widget.SeekBar;
+import android.widget.Switch;
+
+import com.larswerkman.lobsterpicker.OnColorListener;
+import com.larswerkman.lobsterpicker.sliders.LobsterShadeSlider;
+import com.mikhaellopez.circularprogressbar.CircularProgressBar;
+
+public class MainActivity extends AppCompatActivity {
+
+ private CircularProgressBar circularProgressBar;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ circularProgressBar = findViewById(R.id.circularProgressbar);
+ circularProgressBar.setProgressWithAnimation(65);
+
+ // PROGRESS
+ SeekBar seekBarProgress = findViewById(R.id.seekBarProgress);
+ seekBarProgress.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ circularProgressBar.setProgress(progress);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+
+ // BORDER
+ ((SeekBar) findViewById(R.id.seekBarStrokeWidth)).setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ circularProgressBar.setProgressBarWidth(progress * getResources().getDisplayMetrics().density);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+
+ // BACKGROUND BORDER
+ ((SeekBar) findViewById(R.id.seekBarBackgroundStrokeWidth)).setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ circularProgressBar.setBackgroundProgressBarWidth(progress * getResources().getDisplayMetrics().density);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+
+ // COLOR
+ ((LobsterShadeSlider) findViewById(R.id.shadeslider)).addOnColorListener(new OnColorListener() {
+ @Override
+ public void onColorChanged(@ColorInt int color) {
+ circularProgressBar.setColor(color);
+ circularProgressBar.setBackgroundColor(adjustAlpha(color, 0.3f));
+ }
+
+ @Override
+ public void onColorSelected(@ColorInt int color) {
+ }
+ });
+
+ // INDETERMINATE MODE
+ final Switch switchIndeterminateMode = findViewById(R.id.switchIndeterminateMode);
+ switchIndeterminateMode.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ circularProgressBar.enableIndeterminateMode(isChecked);
+ }
+ });
+ circularProgressBar.setOnIndeterminateModeChangeListener(new CircularProgressBar.IndeterminateModeChangeListener() {
+ @Override
+ public void onModeChange(boolean isEnable) {
+ switchIndeterminateMode.setChecked(isEnable);
+ }
+ });
+ }
+
+ /**
+ * Transparent the given color by the factor
+ * The more the factor closer to zero the more the color gets transparent
+ *
+ * @param color The color to transparent
+ * @param factor 1.0f to 0.0f
+ * @return int - A transplanted color
+ */
+ private int adjustAlpha(int color, float factor) {
+ int alpha = Math.round(Color.alpha(color) * factor);
+ int red = Color.red(color);
+ int green = Color.green(color);
+ int blue = Color.blue(color);
+ return Color.argb(alpha, red, green, blue);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.github:
+ startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/lopspower/CircularProgressBar")));
+ return true;
+ case R.id.beer:
+ new AlertDialog.Builder(this)
+ .setTitle(getResources().getString(R.string.pay_me_a_beer))
+ .setMessage(getResources().getString(R.string.offer_me_a_beer))
+ .setPositiveButton(getResources().getString(android.R.string.ok).toUpperCase(), new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.paypal.me/LopezMikhael")));
+ }
+ })
+ .setNegativeButton(getResources().getString(android.R.string.cancel).toUpperCase(), null)
+ .show();
+ return true;
+ case android.R.id.home:
+ finish();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/circularprogressbar-example/src/main/res/drawable-hdpi/scrubber_progress_primary.9.png b/circularprogressbar-example/src/main/res/drawable-hdpi/scrubber_progress_primary.9.png
new file mode 100644
index 0000000..704cea5
Binary files /dev/null and b/circularprogressbar-example/src/main/res/drawable-hdpi/scrubber_progress_primary.9.png differ
diff --git a/circularprogressbar-example/src/main/res/drawable-hdpi/scrubber_progress_secondary.9.png b/circularprogressbar-example/src/main/res/drawable-hdpi/scrubber_progress_secondary.9.png
new file mode 100644
index 0000000..f043895
Binary files /dev/null and b/circularprogressbar-example/src/main/res/drawable-hdpi/scrubber_progress_secondary.9.png differ
diff --git a/circularprogressbar-example/src/main/res/drawable-hdpi/scrubber_progress_track.9.png b/circularprogressbar-example/src/main/res/drawable-hdpi/scrubber_progress_track.9.png
new file mode 100644
index 0000000..90528b1
Binary files /dev/null and b/circularprogressbar-example/src/main/res/drawable-hdpi/scrubber_progress_track.9.png differ
diff --git a/circularprogressbar-example/src/main/res/drawable-mdpi/scrubber_progress_primary.9.png b/circularprogressbar-example/src/main/res/drawable-mdpi/scrubber_progress_primary.9.png
new file mode 100644
index 0000000..825b0ce
Binary files /dev/null and b/circularprogressbar-example/src/main/res/drawable-mdpi/scrubber_progress_primary.9.png differ
diff --git a/circularprogressbar-example/src/main/res/drawable-mdpi/scrubber_progress_secondary.9.png b/circularprogressbar-example/src/main/res/drawable-mdpi/scrubber_progress_secondary.9.png
new file mode 100644
index 0000000..cf8db61
Binary files /dev/null and b/circularprogressbar-example/src/main/res/drawable-mdpi/scrubber_progress_secondary.9.png differ
diff --git a/circularprogressbar-example/src/main/res/drawable-mdpi/scrubber_progress_track.9.png b/circularprogressbar-example/src/main/res/drawable-mdpi/scrubber_progress_track.9.png
new file mode 100644
index 0000000..359ae4a
Binary files /dev/null and b/circularprogressbar-example/src/main/res/drawable-mdpi/scrubber_progress_track.9.png differ
diff --git a/circularprogressbar-example/src/main/res/drawable-nodpi/beer.png b/circularprogressbar-example/src/main/res/drawable-nodpi/beer.png
new file mode 100644
index 0000000..74a64e6
Binary files /dev/null and b/circularprogressbar-example/src/main/res/drawable-nodpi/beer.png differ
diff --git a/circularprogressbar-example/src/main/res/drawable-nodpi/github.png b/circularprogressbar-example/src/main/res/drawable-nodpi/github.png
new file mode 100644
index 0000000..3875dcc
Binary files /dev/null and b/circularprogressbar-example/src/main/res/drawable-nodpi/github.png differ
diff --git a/circularprogressbar-example/src/main/res/drawable-xhdpi/scrubber_progress_primary.9.png b/circularprogressbar-example/src/main/res/drawable-xhdpi/scrubber_progress_primary.9.png
new file mode 100644
index 0000000..24f941e
Binary files /dev/null and b/circularprogressbar-example/src/main/res/drawable-xhdpi/scrubber_progress_primary.9.png differ
diff --git a/circularprogressbar-example/src/main/res/drawable-xhdpi/scrubber_progress_secondary.9.png b/circularprogressbar-example/src/main/res/drawable-xhdpi/scrubber_progress_secondary.9.png
new file mode 100644
index 0000000..0090b6d
Binary files /dev/null and b/circularprogressbar-example/src/main/res/drawable-xhdpi/scrubber_progress_secondary.9.png differ
diff --git a/circularprogressbar-example/src/main/res/drawable-xhdpi/scrubber_progress_track.9.png b/circularprogressbar-example/src/main/res/drawable-xhdpi/scrubber_progress_track.9.png
new file mode 100644
index 0000000..a7d396d
Binary files /dev/null and b/circularprogressbar-example/src/main/res/drawable-xhdpi/scrubber_progress_track.9.png differ
diff --git a/circularprogressbar-example/src/main/res/drawable-xxhdpi/scrubber_progress_primary.9.png b/circularprogressbar-example/src/main/res/drawable-xxhdpi/scrubber_progress_primary.9.png
new file mode 100644
index 0000000..cb301a0
Binary files /dev/null and b/circularprogressbar-example/src/main/res/drawable-xxhdpi/scrubber_progress_primary.9.png differ
diff --git a/circularprogressbar-example/src/main/res/drawable-xxhdpi/scrubber_progress_secondary.9.png b/circularprogressbar-example/src/main/res/drawable-xxhdpi/scrubber_progress_secondary.9.png
new file mode 100644
index 0000000..f8166c0
Binary files /dev/null and b/circularprogressbar-example/src/main/res/drawable-xxhdpi/scrubber_progress_secondary.9.png differ
diff --git a/circularprogressbar-example/src/main/res/drawable-xxhdpi/scrubber_progress_track.9.png b/circularprogressbar-example/src/main/res/drawable-xxhdpi/scrubber_progress_track.9.png
new file mode 100644
index 0000000..1a6f577
Binary files /dev/null and b/circularprogressbar-example/src/main/res/drawable-xxhdpi/scrubber_progress_track.9.png differ
diff --git a/circularprogressbar-example/src/main/res/drawable/scrubber_progress.xml b/circularprogressbar-example/src/main/res/drawable/scrubber_progress.xml
new file mode 100644
index 0000000..1857fd7
--- /dev/null
+++ b/circularprogressbar-example/src/main/res/drawable/scrubber_progress.xml
@@ -0,0 +1,16 @@
+
+
+
+ -
+
+
+ -
+
+
+
diff --git a/circularprogressbar-example/src/main/res/layout/activity_main.xml b/circularprogressbar-example/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..69a7d89
--- /dev/null
+++ b/circularprogressbar-example/src/main/res/layout/activity_main.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/circularprogressbar-example/src/main/res/menu/menu.xml b/circularprogressbar-example/src/main/res/menu/menu.xml
new file mode 100644
index 0000000..fdb68de
--- /dev/null
+++ b/circularprogressbar-example/src/main/res/menu/menu.xml
@@ -0,0 +1,17 @@
+
+
\ No newline at end of file
diff --git a/circularprogressbar-example/src/main/res/mipmap-hdpi/ic_launcher.png b/circularprogressbar-example/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..266db9f
Binary files /dev/null and b/circularprogressbar-example/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/circularprogressbar-example/src/main/res/mipmap-mdpi/ic_launcher.png b/circularprogressbar-example/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..8354d00
Binary files /dev/null and b/circularprogressbar-example/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/circularprogressbar-example/src/main/res/mipmap-xhdpi/ic_launcher.png b/circularprogressbar-example/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..aeaba0a
Binary files /dev/null and b/circularprogressbar-example/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/circularprogressbar-example/src/main/res/mipmap-xxhdpi/ic_launcher.png b/circularprogressbar-example/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..55dfa3a
Binary files /dev/null and b/circularprogressbar-example/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/circularprogressbar-example/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/circularprogressbar-example/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..55cd17c
Binary files /dev/null and b/circularprogressbar-example/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/circularprogressbar-example/src/main/res/values-v21/themes.xml b/circularprogressbar-example/src/main/res/values-v21/themes.xml
new file mode 100644
index 0000000..084f7a4
--- /dev/null
+++ b/circularprogressbar-example/src/main/res/values-v21/themes.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/circularprogressbar-example/src/main/res/values/colors.xml b/circularprogressbar-example/src/main/res/values/colors.xml
new file mode 100644
index 0000000..698cf9f
--- /dev/null
+++ b/circularprogressbar-example/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #424242
+ #212121
+ @color/primary
+
\ No newline at end of file
diff --git a/circularprogressbar-example/src/main/res/values/strings.xml b/circularprogressbar-example/src/main/res/values/strings.xml
new file mode 100644
index 0000000..c50712c
--- /dev/null
+++ b/circularprogressbar-example/src/main/res/values/strings.xml
@@ -0,0 +1,12 @@
+
+
+
+ CircularProgressBar
+
+
+ Indeterminate mode
+ Show on GitHub
+ Pay me a beer
+ Offer me a beer if that application have helped you to reward me.
+
+
diff --git a/circularprogressbar-example/src/main/res/values/themes.xml b/circularprogressbar-example/src/main/res/values/themes.xml
new file mode 100644
index 0000000..0c3fbd9
--- /dev/null
+++ b/circularprogressbar-example/src/main/res/values/themes.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/circularprogressbar/.gitignore b/circularprogressbar/.gitignore
new file mode 100644
index 0000000..a88b5be
--- /dev/null
+++ b/circularprogressbar/.gitignore
@@ -0,0 +1,4 @@
+/build
+/libs
+/proguard-rules.pro
+/circularprogressbar.iml
diff --git a/circularprogressbar/build.gradle b/circularprogressbar/build.gradle
new file mode 100644
index 0000000..6186591
--- /dev/null
+++ b/circularprogressbar/build.gradle
@@ -0,0 +1,69 @@
+apply plugin: 'com.android.library'
+apply plugin: 'com.jfrog.bintray'
+apply plugin: 'maven-publish'
+
+android {
+ compileSdkVersion androidCompileSdkVersion
+ defaultConfig {
+ minSdkVersion androidMinSdkVersion
+ targetSdkVersion androidTargetSdkVersion
+ versionCode androidVersionCode
+ versionName androidVersionName
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+//region Publish to jCenter
+def libGroupId = 'com.mikhaellopez'
+def libArtifactId = 'circularprogressbar'
+def libVersion = androidVersionName
+def libDesc = 'This is an Android project allowing to realize a circular ProgressBar in the simplest way possible.'
+def libLicenses = ['Apache-2.0']
+def libVCSUrl = 'https://github.com/lopspower/CircularProgressBar.git'
+
+// Generate Source Jar
+task sourceJar(type: Jar) {
+ from android.sourceSets.main.java.srcDirs
+ classifier "sources"
+}
+
+// Create the publication with the pom configuration
+publishing {
+ publications {
+ MyPublication(MavenPublication) {
+ groupId libGroupId
+ artifactId libArtifactId
+ version libVersion
+ artifact(sourceJar)
+ artifact("$buildDir/outputs/aar/$libArtifactId-release.aar")
+ }
+ }
+}
+
+// Bintray config
+Properties properties = new Properties()
+properties.load(project.rootProject.file('local.properties').newDataInputStream())
+
+bintray {
+ user = properties.getProperty("bintray.user")
+ key = properties.getProperty("bintray.apikey")
+ publications = ['MyPublication']
+ pkg {
+ repo = "maven"
+ name = "$libGroupId:$libArtifactId"
+ desc = libDesc
+ licenses = libLicenses
+ vcsUrl = libVCSUrl
+ version {
+ name = libVersion
+ vcsTag = libVersion
+ released = new Date()
+ }
+ }
+}
+//endregion
diff --git a/circularprogressbar/src/main/AndroidManifest.xml b/circularprogressbar/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..0ebc9c6
--- /dev/null
+++ b/circularprogressbar/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+
diff --git a/circularprogressbar/src/main/java/com/mikhaellopez/circularprogressbar/CircularProgressBar.java b/circularprogressbar/src/main/java/com/mikhaellopez/circularprogressbar/CircularProgressBar.java
new file mode 100644
index 0000000..dd5d246
--- /dev/null
+++ b/circularprogressbar/src/main/java/com/mikhaellopez/circularprogressbar/CircularProgressBar.java
@@ -0,0 +1,285 @@
+package com.mikhaellopez.circularprogressbar;
+
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Copyright (C) 2018 Mikhael LOPEZ
+ * Licensed under the Apache License Version 2.0
+ */
+public class CircularProgressBar extends View {
+
+ private static final float DEFAULT_MAX_VALUE = 100;
+ private static final float DEFAULT_START_ANGLE = 270;
+ private static final int DEFAULT_ANIMATION_DURATION = 1500;
+
+ // Properties
+ private float progress = 0;
+ private float progressMax = DEFAULT_MAX_VALUE;
+ private float strokeWidth = getResources().getDimension(R.dimen.default_stroke_width);
+ private float backgroundStrokeWidth = getResources().getDimension(R.dimen.default_background_stroke_width);
+ private int color = Color.BLACK;
+ private int backgroundColor = Color.GRAY;
+ private boolean rightToLeft = true;
+ private boolean indeterminateMode = false;
+ private float startAngle = DEFAULT_START_ANGLE;
+ private ProgressChangeListener progressChangeListener;
+ private IndeterminateModeChangeListener indeterminateModeChangeListener;
+ private ValueAnimator progressAnimator;
+ private Handler indeterminateModeHandler;
+
+ // View
+ private RectF rectF;
+ private Paint backgroundPaint;
+ private Paint foregroundPaint;
+
+ //region Constructor & Init Method
+ public CircularProgressBar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context, attrs);
+ }
+
+ private void init(Context context, AttributeSet attrs) {
+ rectF = new RectF();
+ TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CircularProgressBar, 0, 0);
+ //Reading values from the XML layout
+ try {
+ // Value
+ progress = typedArray.getFloat(R.styleable.CircularProgressBar_cpb_progress, progress);
+ progressMax = typedArray.getFloat(R.styleable.CircularProgressBar_cpb_progress_max, progressMax);
+ // Indeterminate Mode
+ indeterminateMode = typedArray.getBoolean(R.styleable.CircularProgressBar_cpb_indeterminate_mode, indeterminateMode);
+ // StrokeWidth
+ strokeWidth = typedArray.getDimension(R.styleable.CircularProgressBar_cpb_progressbar_width, strokeWidth);
+ backgroundStrokeWidth = typedArray.getDimension(R.styleable.CircularProgressBar_cpb_background_progressbar_width, backgroundStrokeWidth);
+ // Color
+ color = typedArray.getInt(R.styleable.CircularProgressBar_cpb_progressbar_color, color);
+ backgroundColor = typedArray.getInt(R.styleable.CircularProgressBar_cpb_background_progressbar_color, backgroundColor);
+ } finally {
+ typedArray.recycle();
+ }
+
+ // Init Background
+ backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ backgroundPaint.setColor(backgroundColor);
+ backgroundPaint.setStyle(Paint.Style.STROKE);
+ backgroundPaint.setStrokeWidth(backgroundStrokeWidth);
+
+ // Init Foreground
+ foregroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ foregroundPaint.setColor(color);
+ foregroundPaint.setStyle(Paint.Style.STROKE);
+ foregroundPaint.setStrokeWidth(strokeWidth);
+ }
+ //endregion
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ if (indeterminateMode) enableIndeterminateMode(true);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (progressAnimator != null) progressAnimator.cancel();
+ if (indeterminateModeHandler != null) indeterminateModeHandler.removeCallbacks(indeterminateModeRunnable);
+ }
+
+ //region Draw Method
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.drawOval(rectF, backgroundPaint);
+ float realProgress = progress * DEFAULT_MAX_VALUE / progressMax;
+ float angle = (rightToLeft ? 360 : -360) * realProgress / 100;
+ canvas.drawArc(rectF, startAngle, angle, false, foregroundPaint);
+ }
+
+ private void reDraw() {
+ requestLayout();//Because it should recalculate its bounds
+ invalidate();
+ }
+ //endregion
+
+ //region Mesure Method
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
+ final int width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
+ final int min = Math.min(width, height);
+ setMeasuredDimension(min, min);
+ float highStroke = strokeWidth > backgroundStrokeWidth ? strokeWidth : backgroundStrokeWidth;
+ rectF.set(0 + highStroke / 2, 0 + highStroke / 2, min - highStroke / 2, min - highStroke / 2);
+ }
+ //endregion
+
+ //region Method Get/Set
+ public float getProgress() {
+ return progress;
+ }
+
+ public void setProgress(float progress) {
+ setProgress(progress, false);
+ }
+
+ private void setProgress(float progress, boolean fromAnimation) {
+ if (!fromAnimation && progressAnimator != null) {
+ progressAnimator.cancel();
+ if (indeterminateMode) enableIndeterminateMode(false);
+ }
+ this.progress = progress <= progressMax ? progress : progressMax;
+ if (progressChangeListener != null) progressChangeListener.onProgressChanged(progress);
+ invalidate();
+ }
+
+ public float getProgressMax() {
+ return progressMax;
+ }
+
+ public void setProgressMax(float progressMax) {
+ this.progressMax = progressMax >= 0 ? progressMax : DEFAULT_MAX_VALUE;
+ reDraw();
+ }
+
+ public float getProgressBarWidth() {
+ return strokeWidth;
+ }
+
+ public void setProgressBarWidth(float strokeWidth) {
+ this.strokeWidth = strokeWidth;
+ foregroundPaint.setStrokeWidth(strokeWidth);
+ reDraw();
+ }
+
+ public float getBackgroundProgressBarWidth() {
+ return backgroundStrokeWidth;
+ }
+
+ public void setBackgroundProgressBarWidth(float backgroundStrokeWidth) {
+ this.backgroundStrokeWidth = backgroundStrokeWidth;
+ backgroundPaint.setStrokeWidth(backgroundStrokeWidth);
+ reDraw();
+ }
+
+ public int getColor() {
+ return color;
+ }
+
+ public void setColor(int color) {
+ this.color = color;
+ foregroundPaint.setColor(color);
+ reDraw();
+ }
+
+ public int getBackgroundColor() {
+ return backgroundColor;
+ }
+
+ public void setBackgroundColor(int backgroundColor) {
+ this.backgroundColor = backgroundColor;
+ backgroundPaint.setColor(backgroundColor);
+ reDraw();
+ }
+ //endregion
+
+ //region Progress Animation
+
+ /**
+ * Set the progress with an animation with 1500 by default duration.
+ *
+ * @param progress The progress it should animate to it.
+ */
+ public void setProgressWithAnimation(float progress) {
+ setProgressWithAnimation(progress, DEFAULT_ANIMATION_DURATION);
+ }
+
+ /**
+ * Set the progress with an animation.
+ *
+ * @param progress The progress it should animate to it.
+ * @param duration The length of the animation, in milliseconds.
+ */
+ public void setProgressWithAnimation(float progress, int duration) {
+ if (progressAnimator != null) {
+ progressAnimator.cancel();
+ }
+ progressAnimator = ValueAnimator.ofFloat(this.progress, progress);
+ progressAnimator.setDuration(duration);
+ progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float progress = (Float) animation.getAnimatedValue();
+ setProgress(progress, true);
+ if (indeterminateMode) {
+ float updateAngle = progress * 360 / 100;
+ startAngle = DEFAULT_START_ANGLE + (rightToLeft ? updateAngle : -updateAngle);
+ }
+ }
+ });
+ progressAnimator.start();
+ }
+ //endregion
+
+ //region Indeterminate Mode
+ private Runnable indeterminateModeRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (indeterminateMode) {
+ indeterminateModeHandler.postDelayed(indeterminateModeRunnable, DEFAULT_ANIMATION_DURATION);
+ // whatever you want to do below
+ CircularProgressBar.this.rightToLeft = !CircularProgressBar.this.rightToLeft;
+ if (CircularProgressBar.this.rightToLeft) {
+ setProgressWithAnimation(0);
+ } else {
+ setProgressWithAnimation(progressMax);
+ }
+ }
+ }
+ };
+
+ public void enableIndeterminateMode(boolean enable) {
+ indeterminateMode = enable;
+ if (indeterminateModeChangeListener != null) indeterminateModeChangeListener.onModeChange(indeterminateMode);
+ rightToLeft = true;
+ startAngle = DEFAULT_START_ANGLE;
+
+ if (indeterminateModeHandler != null) indeterminateModeHandler.removeCallbacks(indeterminateModeRunnable);
+ if (progressAnimator != null) progressAnimator.cancel();
+ indeterminateModeHandler = new Handler();
+
+ if (indeterminateMode) {
+ indeterminateModeHandler.post(indeterminateModeRunnable);
+ } else {
+ setProgress(0, true);
+ }
+ }
+ //endregion
+
+ //region Listener
+ public void setOnProgressChangedListener(ProgressChangeListener listener) {
+ progressChangeListener = listener;
+ }
+
+ public void setOnIndeterminateModeChangeListener(IndeterminateModeChangeListener listener) {
+ indeterminateModeChangeListener = listener;
+ }
+
+ public interface ProgressChangeListener {
+ void onProgressChanged(float progress);
+ }
+
+ public interface IndeterminateModeChangeListener {
+ void onModeChange(boolean isEnable);
+ }
+ //endregion
+
+}
diff --git a/circularprogressbar/src/main/res/values/attrs.xml b/circularprogressbar/src/main/res/values/attrs.xml
new file mode 100644
index 0000000..eb0e07f
--- /dev/null
+++ b/circularprogressbar/src/main/res/values/attrs.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/circularprogressbar/src/main/res/values/dimens.xml b/circularprogressbar/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..f62478b
--- /dev/null
+++ b/circularprogressbar/src/main/res/values/dimens.xml
@@ -0,0 +1,7 @@
+
+
+
+ 7dp
+ 3dp
+
+
diff --git a/empalink-2.2/build.gradle b/empalink-2.2/build.gradle
new file mode 100644
index 0000000..971f415
--- /dev/null
+++ b/empalink-2.2/build.gradle
@@ -0,0 +1,2 @@
+configurations.maybeCreate("default")
+artifacts.add("default", file('empalink-2.2.aar'))
\ No newline at end of file
diff --git a/empalink-2.2/empalink-2.2.aar b/empalink-2.2/empalink-2.2.aar
new file mode 100644
index 0000000..d6e245f
Binary files /dev/null and b/empalink-2.2/empalink-2.2.aar differ
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..82618ce
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,15 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+
+
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..f6b961f
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a6e653a
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue Oct 15 10:47:00 CEST 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..cccdd3d
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..f955316
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..5031a3c
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+include ':app', ':empalink-2.2'