Repository: StephenBlackWasAlreadyTaken/xDrip Branch: master Commit: bd43dad2b9cf Files: 181 Total size: 717.9 KB Directory structure: gitextract_6jrc5dih/ ├── .gitignore ├── DexDrip.iml ├── LICENSE ├── README.md ├── app/ │ ├── .gitignore │ ├── app.iml │ ├── build.gradle │ ├── libs/ │ │ ├── ActiveAndroid.jar │ │ ├── android-support-v4.jar │ │ ├── hellocharts-library-1.1.jar │ │ └── usb-serial-for-android-v010.jar │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── eveningoutpost/ │ │ └── dexdrip/ │ │ └── ApplicationTest.java │ └── main/ │ ├── AndroidManifest.xml │ ├── assets/ │ │ └── migrations/ │ │ ├── 10.sql │ │ ├── 16.sql │ │ ├── 17.sql │ │ ├── 18.sql │ │ ├── 19.sql │ │ ├── 20.sql │ │ ├── 21.sql │ │ ├── 23.sql │ │ ├── 24.sql │ │ ├── 25.sql │ │ └── 26.sql │ ├── java/ │ │ └── com/ │ │ └── eveningoutpost/ │ │ └── dexdrip/ │ │ ├── AddCalibration.java │ │ ├── AutoStart.java │ │ ├── BluetoothScan.java │ │ ├── CalibrationCheckInActivity.java │ │ ├── CalibrationGraph.java │ │ ├── CalibrationOverride.java │ │ ├── DoubleCalibrationActivity.java │ │ ├── FakeNumbers.java │ │ ├── Home.java │ │ ├── ImportedLibraries/ │ │ │ ├── dexcom/ │ │ │ │ ├── CRC16.java │ │ │ │ ├── CRCFailRuntimeException.java │ │ │ │ ├── Constants.java │ │ │ │ ├── PacketBuilder.java │ │ │ │ ├── ReadData.java │ │ │ │ ├── ReadDataShare.java │ │ │ │ ├── ReadPacket.java │ │ │ │ ├── SyncingService.java │ │ │ │ ├── Utils.java │ │ │ │ └── records/ │ │ │ │ ├── CalRecord.java │ │ │ │ ├── CalSubrecord.java │ │ │ │ ├── EGVRecord.java │ │ │ │ ├── GenericTimestampRecord.java │ │ │ │ ├── GenericXMLRecord.java │ │ │ │ ├── GlucoseDataSet.java │ │ │ │ ├── MeterRecord.java │ │ │ │ ├── PageHeader.java │ │ │ │ └── SensorRecord.java │ │ │ └── usbserial/ │ │ │ ├── BuildInfo.java │ │ │ ├── driver/ │ │ │ │ ├── CdcAcmSerialDriver.java │ │ │ │ ├── CommonUsbSerialPort.java │ │ │ │ ├── Cp21xxSerialDriver.java │ │ │ │ ├── FtdiSerialDriver.java │ │ │ │ ├── ProbeTable.java │ │ │ │ ├── ProlificSerialDriver.java │ │ │ │ ├── UsbId.java │ │ │ │ ├── UsbSerialDriver.java │ │ │ │ ├── UsbSerialPort.java │ │ │ │ ├── UsbSerialProber.java │ │ │ │ └── UsbSerialRuntimeException.java │ │ │ └── util/ │ │ │ ├── HexDump.java │ │ │ └── SerialInputOutputManager.java │ │ ├── Interfaces/ │ │ │ ├── BgReadingInterface.java │ │ │ ├── CalibrationInterface.java │ │ │ ├── SensorInterface.java │ │ │ └── UserInterface.java │ │ ├── LicenseAgreementActivity.java │ │ ├── Models/ │ │ │ ├── ActiveBluetoothDevice.java │ │ │ ├── BgReading.java │ │ │ ├── Calibration.java │ │ │ ├── CalibrationRequest.java │ │ │ ├── TransmitterData.java │ │ │ ├── User.java │ │ │ └── UserNotification.java │ │ ├── NavDrawerBuilder.java │ │ ├── NavigationDrawerFragment.java │ │ ├── Sensor.java │ │ ├── Services/ │ │ │ ├── ComunicationHeader.java │ │ │ ├── DexCollectionService.java │ │ │ ├── DexShareCollectionService.java │ │ │ ├── MongoWrapper.java │ │ │ ├── SyncService.java │ │ │ ├── TransmitterRawData.java │ │ │ └── WixelReader.java │ │ ├── SettingsActivity.java │ │ ├── ShareModels/ │ │ │ ├── DexcomShareInterface.java │ │ │ ├── Egv.java │ │ │ ├── ShareAuthenticationBody.java │ │ │ ├── ShareGlucose.java │ │ │ ├── ShareRest.java │ │ │ └── ShareUploadPayload.java │ │ ├── ShareTest.java │ │ ├── StartNewSensor.java │ │ ├── StopSensor.java │ │ ├── SystemStatus.java │ │ ├── Tables/ │ │ │ ├── BgReadingTable.java │ │ │ ├── CalibrationDataTable.java │ │ │ └── SensorDataTable.java │ │ ├── UsbConnectedActivity.java │ │ ├── UtilityModels/ │ │ │ ├── BgGraphBuilder.java │ │ │ ├── BgSendQueue.java │ │ │ ├── CalibrationSendQueue.java │ │ │ ├── CollectionServiceStarter.java │ │ │ ├── Constants.java │ │ │ ├── DexShareAttributes.java │ │ │ ├── ForegroundServiceStarter.java │ │ │ ├── HM10Attributes.java │ │ │ ├── Intents.java │ │ │ ├── MongoSendTask.java │ │ │ ├── NightscoutUploader.java │ │ │ ├── Notifications.java │ │ │ ├── PebbleSync.java │ │ │ ├── RedBearLabAttributes.java │ │ │ ├── RestCalls.java │ │ │ └── SensorSendQueue.java │ │ ├── utils/ │ │ │ ├── DatabaseUtil.java │ │ │ ├── FileUtils.java │ │ │ ├── Preferences.java │ │ │ └── ShareNotification.java │ │ ├── widgetUpdateService.java │ │ ├── xDripWidget.java │ │ └── xdrip.java │ └── res/ │ ├── layout/ │ │ ├── activity_add_calibration.xml │ │ ├── activity_add_comparison.xml │ │ ├── activity_bluetooth_scan.xml │ │ ├── activity_calibration_check_in.xml │ │ ├── activity_calibration_graph.xml │ │ ├── activity_calibration_override.xml │ │ ├── activity_double_calibration.xml │ │ ├── activity_fake_numbers.xml │ │ ├── activity_home.xml │ │ ├── activity_license_agreement.xml │ │ ├── activity_raw_data_table.xml │ │ ├── activity_share_test.xml │ │ ├── activity_start_new_sensor.xml │ │ ├── activity_stop_sensor.xml │ │ ├── activity_system_status.xml │ │ ├── activity_usb_connected.xml │ │ ├── fragment_navigation_drawer.xml │ │ ├── listitem_device.xml │ │ ├── raw_data_list.xml │ │ ├── raw_data_list_item.xml │ │ └── x_drip_widget.xml │ ├── layout-xlarge/ │ │ └── activity_home.xml │ ├── menu/ │ │ ├── global.xml │ │ ├── menu_bluetooth_scan.xml │ │ ├── menu_home.xml │ │ ├── menu_share_test.xml │ │ └── menu_system_status.xml │ ├── values/ │ │ ├── arrays.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ ├── strings_activity_preferences.xml │ │ └── styles.xml │ ├── values-v14/ │ │ └── dimens.xml │ ├── values-v21/ │ │ └── styles.xml │ ├── values-w820dp/ │ │ └── dimens.xml │ └── xml/ │ ├── device_filter.xml │ ├── pref_advanced_settings.xml │ ├── pref_data_source.xml │ ├── pref_data_sync.xml │ ├── pref_general.xml │ ├── pref_headers.xml │ ├── pref_license.xml │ ├── pref_notifications.xml │ └── x_drip_widget_info.xml ├── battle_of_the_dexes.md ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── hardware_setup.md ├── settings.gradle ├── xDrip-Experimental.iml └── xDrip.iml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Built application files *.apk *.ap_ # Files for the Dalvik VM *.dex # Java class files *.class # Generated files bin/ gen/ # Gradle files .gradle/ build/ # Local configuration file (sdk path, etc) local.properties # Proguard folder generated by Eclipse proguard/ # Log Files *.log #local /local.properties .idea/ /.idea/* .idea/misc.xml .idea/vcs.xml *.idea* *manifest-merger-debug-report.txt *manifest-merger-release-report.txt *.ds_store *.DS_Store *.swp ================================================ FILE: DexDrip.iml ================================================ ================================================ FILE: LICENSE ================================================ 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 . ================================================ FILE: README.md ================================================ xDrip ======= ##### ** Please note this is __NOT__ a product created by or backed by Dexcom, you can check them out [here](http://dexcom.com/) ** #### Wirelessly with a Dexcom Share Receiver -- or -- Wireless xDrip bridge (No receiver!) #### Click here for the [Main Project Page](http://stephenblackwasalreadytaken.github.io/xDrip/) #### Access your Glucose levels from anywhere! xDrip is ready to upload your glucose data to any [Nightscout](http://nightscout.github.io/) server for remote monitoring on just about any internet connected device!! #### Want to learn more?? Jump on over to the [Wiki](https://github.com/StephenBlackWasAlreadyTaken/xDrip/wiki) for instructions and downloads!! #### Stay Up to Date! Follow me on twitter `@StephenIsTaken`, I will let everyone know when updates happen that you should definitely download! Updates will be rolling out fairly frequently as there is lots to do!!! #### Want to help out? AWESOME, let me know, put up some PRS, lets make it awesome! #### Awesome Contributors: (in no particular order, because they are all awesome) * [@tzachi-dar](https://github.com/tzachi-dar) * [@jstevensog](https://github.com/jstevensog) * [@bhandfast](https://github.com/bhandfast) * [@LorelaiL](https://github.com/LorelaiL) * [@syntaxerr66](https://github.com/syntaxerr66) * [@saercnap](https://github.com/saercnap) * [@ktind](https://github.com/ktind) * [Ly](http://youtu.be/YuxCUeJ9xAU) * [Kev](http://circles-of-blue.winchcombe.org/) * [NightScout](https://github.com/nightscout) * Whoever else is supporting this and helping people out!! *** *** ================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/app.iml ================================================ ================================================ FILE: app/build.gradle ================================================ buildscript { repositories { maven { url 'https://maven.fabric.io/public' } } dependencies { classpath 'io.fabric.tools:gradle:1.+' } } apply plugin: 'com.android.application' apply plugin: 'io.fabric' repositories { maven { url 'https://maven.fabric.io/public' } } android { compileSdkVersion 21 buildToolsVersion "20.0.0" defaultConfig { applicationId "com.eveningoutpost.dexdrip" minSdkVersion 19 targetSdkVersion 21 versionCode 1 versionName "1.0" } buildTypes { } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'com.squareup.okhttp:mockwebserver:2.2.0' compile 'com.squareup.okhttp:okhttp:2.2.0' compile 'com.google.code.gson:gson:2.3' compile 'org.mongodb:mongo-java-driver:2.10.1' compile 'com.squareup.retrofit:retrofit:1.9.0' compile 'com.getpebble:pebblekit:2.6.0@aar' compile 'io.reactivex:rxjava:1.0.0' compile 'ch.acra:acra:4.5.0' compile('com.crashlytics.sdk.android:crashlytics:2.2.3@aar') { transitive = true; } } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in /Users/stephenblack/adt-bundle-mac-x86_64-20140702 2/sdk/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} ================================================ FILE: app/src/androidTest/java/com/eveningoutpost/dexdrip/ApplicationTest.java ================================================ package com.eveningoutpost.dexdrip; import android.app.Application; import android.test.ApplicationTestCase; /** * Testing Fundamentals */ public class ApplicationTest extends ApplicationTestCase { public ApplicationTest() { super(Application.class); } } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/assets/migrations/10.sql ================================================ ALTER TABLE Notifications ADD COLUMN calibration_alert BOOLEAN; ALTER TABLE Notifications ADD COLUMN double_calibration_alert BOOLEAN; ALTER TABLE Notifications ADD COLUMN extra_calibration_alert BOOLEAN; ================================================ FILE: app/src/main/assets/migrations/16.sql ================================================ ALTER TABLE CalibrationSendQueue ADD COLUMN mongo_success BOOLEAN; ALTER TABLE BgSendQueue ADD COLUMN mongo_success BOOLEAN; ================================================ FILE: app/src/main/assets/migrations/17.sql ================================================ ALTER TABLE Calibration ADD COLUMN possible_bad BOOLEAN; ================================================ FILE: app/src/main/assets/migrations/18.sql ================================================ ALTER TABLE Calibration ADD COLUMN first_decay REAL; ALTER TABLE Calibration ADD COLUMN second_decay REAL; ALTER TABLE Calibration ADD COLUMN first_slope REAL; ALTER TABLE Calibration ADD COLUMN second_slope REAL; ================================================ FILE: app/src/main/assets/migrations/19.sql ================================================ ALTER TABLE Calibration ADD COLUMN first_intercept REAL; ALTER TABLE Calibration ADD COLUMN second_intercept REAL; ALTER TABLE Calibration ADD COLUMN first_scale REAL; ALTER TABLE Calibration ADD COLUMN second_scale REAL; ================================================ FILE: app/src/main/assets/migrations/20.sql ================================================ ALTER TABLE Calibration ADD COLUMN check_in BOOLEAN; ================================================ FILE: app/src/main/assets/migrations/21.sql ================================================ ALTER TABLE ActiveBluetoothDevice ADD COLUMN connected BOOLEAN; ================================================ FILE: app/src/main/assets/migrations/23.sql ================================================ ALTER TABLE BgReadings ADD COLUMN raw_calculated REAL; ================================================ FILE: app/src/main/assets/migrations/24.sql ================================================ ALTER TABLE BgReadings ADD COLUMN hide_slope BOOLEAN; ================================================ FILE: app/src/main/assets/migrations/25.sql ================================================ ALTER TABLE BgReadings ADD COLUMN noise TEXT; ================================================ FILE: app/src/main/assets/migrations/26.sql ================================================ ALTER TABLE BgReadings ADD COLUMN filtered_data REAL DEFAULT 0; ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/AddCalibration.java ================================================ package com.eveningoutpost.dexdrip; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.v4.widget.DrawerLayout; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import com.eveningoutpost.dexdrip.Models.Calibration; import com.eveningoutpost.dexdrip.UtilityModels.CollectionServiceStarter; public class AddCalibration extends Activity implements NavigationDrawerFragment.NavigationDrawerCallbacks { Button button; private String menu_name = "Add Calibration"; private NavigationDrawerFragment mNavigationDrawerFragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if(CollectionServiceStarter.isBTShare(getApplicationContext())) { Intent intent = new Intent(this, Home.class); startActivity(intent); finish(); } setContentView(R.layout.activity_add_calibration); addListenerOnButton(); } protected void onResume(){ super.onResume(); mNavigationDrawerFragment = (NavigationDrawerFragment) getFragmentManager().findFragmentById(R.id.navigation_drawer); mNavigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout), menu_name, this); } @Override public void onNavigationDrawerItemSelected(int position) { mNavigationDrawerFragment.swapContext(position); } public void addListenerOnButton() { button = (Button) findViewById(R.id.save_calibration_button); button.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { if (Sensor.isActive()) { EditText value = (EditText) findViewById(R.id.bg_value); String string_value = value.getText().toString(); if (!TextUtils.isEmpty(string_value)){ double calValue = Double.parseDouble(string_value); Calibration calibration = Calibration.create(calValue, getApplicationContext()); Intent tableIntent = new Intent(v.getContext(), Home.class); startActivity(tableIntent); finish(); } else { value.setError("Calibration Can Not be blank"); } } else { Log.w("CALERROR", "ERROR"); } } }); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/AutoStart.java ================================================ package com.eveningoutpost.dexdrip; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; import com.eveningoutpost.dexdrip.UtilityModels.CollectionServiceStarter; /** * Created by stephenblack on 11/3/14. */ public class AutoStart extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.w("DexDrip", "Service auto starter, starting!"); CollectionServiceStarter.newStart(context); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/BluetoothScan.java ================================================ package com.eveningoutpost.dexdrip; import android.annotation.TargetApi; import android.app.ListActivity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothManager; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.support.v4.widget.DrawerLayout; 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.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import com.activeandroid.query.Select; import com.eveningoutpost.dexdrip.Models.ActiveBluetoothDevice; import com.eveningoutpost.dexdrip.Services.DexCollectionService; import com.eveningoutpost.dexdrip.UtilityModels.CollectionServiceStarter; import java.util.ArrayList; @TargetApi(android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) public class BluetoothScan extends ListActivity implements NavigationDrawerFragment.NavigationDrawerCallbacks { private String menu_name = "Scan for BT"; private NavigationDrawerFragment mNavigationDrawerFragment; private final static String TAG = BluetoothScan.class.getSimpleName(); private static final long SCAN_PERIOD = 10000; private boolean is_scanning; private boolean has_bluetooth; private Handler mHandler; private LeDeviceListAdapter mLeDeviceListAdapter; private ArrayList found_devices; private BluetoothAdapter bluetooth_adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_bluetooth_scan); ListView lv = (ListView)findViewById(android.R.id.list); final BluetoothManager bluetooth_manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); bluetooth_adapter = bluetooth_manager.getAdapter(); mHandler = new Handler(); if (bluetooth_adapter == null) { Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_LONG).show(); has_bluetooth = false; finish(); return; } else { has_bluetooth = true; } if(bluetooth_manager == null) { Toast.makeText(this, "This device does not seem to support bluetooth", Toast.LENGTH_LONG).show(); } else { if(!bluetooth_manager.getAdapter().isEnabled()) { Toast.makeText(this, "Bluetooth is turned off on this device currently", Toast.LENGTH_LONG).show(); } else { if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR2){ Toast.makeText(this, "The android version of this device is not compatible with Bluetooth Low Energy", Toast.LENGTH_LONG).show(); } } } mLeDeviceListAdapter = new LeDeviceListAdapter(); setListAdapter(mLeDeviceListAdapter); } @Override protected void onPause() { super.onPause(); scanLeDevice(false); mLeDeviceListAdapter.clear(); } @Override protected void onResume(){ super.onResume(); mNavigationDrawerFragment = (NavigationDrawerFragment) getFragmentManager().findFragmentById(R.id.navigation_drawer); mNavigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout), menu_name, this); } @Override public void onNavigationDrawerItemSelected(int position) { mNavigationDrawerFragment.swapContext(position); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_bluetooth_scan, menu); if (!is_scanning) { menu.findItem(R.id.menu_stop).setVisible(false); menu.findItem(R.id.menu_scan).setVisible(true); } else { menu.findItem(R.id.menu_stop).setVisible(true); menu.findItem(R.id.menu_scan).setVisible(false); } return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_scan: scanLeDevice(true); BluetoothManager bluetooth_manager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); Toast.makeText(this, "Scanning", Toast.LENGTH_LONG).show(); if(bluetooth_manager == null) { Toast.makeText(this, "This device does not seem to support bluetooth", Toast.LENGTH_LONG).show(); } else { if(!bluetooth_manager.getAdapter().isEnabled()) { Toast.makeText(this, "Bluetooth is turned off on this device currently", Toast.LENGTH_LONG).show(); } else { if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR2){ Toast.makeText(this, "The android version of this device is not compatible with Bluetooth Low Energy", Toast.LENGTH_LONG).show(); } } } return true; // case R.id.menu_stop: // Intent tableIntent = new Intent(this, RawDataTable.class); // startActivity(tableIntent); // return true; default: return super.onOptionsItemSelected(item); } } private void scanLeDevice(final boolean enable) { if (enable) { // Stops scanning after a pre-defined scan period. mHandler.postDelayed(new Runnable() { @Override public void run() { is_scanning = false; bluetooth_adapter.stopLeScan(mLeScanCallback); invalidateOptionsMenu(); } }, SCAN_PERIOD); is_scanning = true; bluetooth_adapter.startLeScan(mLeScanCallback); } else { is_scanning = false; bluetooth_adapter.stopLeScan(mLeScanCallback); } invalidateOptionsMenu(); } @Override protected void onListItemClick(ListView l, View v, int position, long id) { Log.d(TAG, "Item Clicked"); final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position); if (device == null) return; Toast.makeText(this, R.string.connecting_to_device, Toast.LENGTH_LONG).show(); ActiveBluetoothDevice btDevice = new Select().from(ActiveBluetoothDevice.class) .orderBy("_ID desc") .executeSingle(); if (btDevice == null) { ActiveBluetoothDevice newBtDevice = new ActiveBluetoothDevice(); newBtDevice.name = device.getName(); newBtDevice.address = device.getAddress(); newBtDevice.save(); } else { btDevice.name = device.getName(); btDevice.address = device.getAddress(); btDevice.save(); } if (is_scanning) { bluetooth_adapter.stopLeScan(mLeScanCallback); is_scanning = false; } Intent intent = new Intent(this, Home.class); CollectionServiceStarter.newStart(getApplicationContext()); startActivity(intent); finish(); } private class LeDeviceListAdapter extends BaseAdapter { private ArrayList mLeDevices; private LayoutInflater mInflator; public LeDeviceListAdapter() { super(); mLeDevices = new ArrayList(); mInflator = BluetoothScan.this.getLayoutInflater(); } public void addDevice(BluetoothDevice device) { if(!mLeDevices.contains(device)) { mLeDevices.add(device); } } public BluetoothDevice getDevice(int position) { return mLeDevices.get(position); } public void clear() { mLeDevices.clear(); } @Override public int getCount() { return mLeDevices.size(); } @Override public Object getItem(int i) { return mLeDevices.get(i); } @Override public long getItemId(int i) { return i; } @Override public View getView(int i, View view, ViewGroup viewGroup) { ViewHolder viewHolder; // General ListView optimization code. if (view == null) { view = mInflator.inflate(R.layout.listitem_device, null); viewHolder = new ViewHolder(); viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address); viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name); view.setTag(viewHolder); } else { viewHolder = (ViewHolder) view.getTag(); } BluetoothDevice device = mLeDevices.get(i); final String deviceName = device.getName(); if (deviceName != null && deviceName.length() > 0) viewHolder.deviceName.setText(deviceName); else viewHolder.deviceName.setText(R.string.unknown_device); viewHolder.deviceAddress.setText(device.getAddress()); return view; } } private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { runOnUiThread(new Runnable() { @Override public void run() { mLeDeviceListAdapter.addDevice(device); mLeDeviceListAdapter.notifyDataSetChanged(); } }); } }; static class ViewHolder { TextView deviceName; TextView deviceAddress; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/CalibrationCheckInActivity.java ================================================ package com.eveningoutpost.dexdrip; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.v4.widget.DrawerLayout; import android.text.TextUtils; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import com.eveningoutpost.dexdrip.Home; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.SyncingService; import com.eveningoutpost.dexdrip.R; import com.eveningoutpost.dexdrip.Sensor; public class CalibrationCheckInActivity extends Activity implements NavigationDrawerFragment.NavigationDrawerCallbacks { private String menu_name = "Check in calibration"; private NavigationDrawerFragment mNavigationDrawerFragment; Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_calibration_check_in); addListenerOnButton(); } protected void onResume(){ super.onResume(); mNavigationDrawerFragment = (NavigationDrawerFragment) getFragmentManager().findFragmentById(R.id.navigation_drawer); mNavigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout), menu_name, this); } @Override public void onNavigationDrawerItemSelected(int position) { mNavigationDrawerFragment.swapContext(position); } public void addListenerOnButton() { button = (Button) findViewById(R.id.check_in_calibrations); button.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { if (Sensor.isActive()) { SyncingService.startActionCalibrationCheckin(getApplicationContext()); Toast.makeText(getApplicationContext(), "Checked in all calibrations", Toast.LENGTH_LONG).show(); Intent tableIntent = new Intent(v.getContext(), Home.class); startActivity(tableIntent); finish(); } else { Log.w("CANNOT CALIBRATE WITHOUT CURRENT SENSOR", "ERROR"); } } }); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/CalibrationGraph.java ================================================ package com.eveningoutpost.dexdrip; import android.app.Activity; import android.os.Bundle; import android.support.v4.widget.DrawerLayout; import android.view.Menu; import android.view.MenuItem; import com.eveningoutpost.dexdrip.Models.Calibration; import java.util.ArrayList; import java.util.List; import lecho.lib.hellocharts.model.Axis; import lecho.lib.hellocharts.model.Line; import lecho.lib.hellocharts.model.LineChartData; import lecho.lib.hellocharts.model.PointValue; import lecho.lib.hellocharts.util.Utils; import lecho.lib.hellocharts.view.LineChartView; public class CalibrationGraph extends Activity implements NavigationDrawerFragment.NavigationDrawerCallbacks { private String menu_name = "Calibration Graph"; private NavigationDrawerFragment mNavigationDrawerFragment; private LineChartView chart; private LineChartData data; public double start_x = 50; public double end_x = 300; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_calibration_graph); } @Override protected void onResume(){ super.onResume(); mNavigationDrawerFragment = (NavigationDrawerFragment) getFragmentManager().findFragmentById(R.id.navigation_drawer); mNavigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout), menu_name, this); setupCharts(); } @Override public void onNavigationDrawerItemSelected(int position) { mNavigationDrawerFragment.swapContext(position); } public void setupCharts() { chart = (LineChartView) findViewById(R.id.chart); List calibrations = Calibration.allForSensor(); List values = new ArrayList(); for (Calibration calibration : calibrations) { values.add(new PointValue((float)calibration.estimate_raw_at_time_of_calibration, (float)calibration.bg)); } Line line = new Line(values); line.setColor(Utils.COLOR_BLUE); line.setHasLines(false); line.setPointRadius(2); line.setHasPoints(true); Calibration calibration = Calibration.last(); List lineValues = new ArrayList(); if(calibration != null) { lineValues.add(new PointValue((float) start_x, (float) (start_x * calibration.slope + calibration.intercept))); lineValues.add(new PointValue((float) end_x, (float) (end_x * calibration.slope + calibration.intercept))); } Line calibrationLine = new Line(lineValues); calibrationLine.setColor(Utils.COLOR_RED); calibrationLine.setHasLines(true); calibrationLine.setHasPoints(false); Axis axisX = new Axis(); Axis axisY = new Axis().setHasLines(true); axisX.setName("Raw Value"); axisY.setName("BG"); List lines = new ArrayList(); lines.add(line); lines.add(calibrationLine); data = new LineChartData(lines); data.setAxisXBottom(axisX); data.setAxisYLeft(axisY); chart.setLineChartData(data); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/CalibrationOverride.java ================================================ package com.eveningoutpost.dexdrip; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.v4.widget.DrawerLayout; import android.text.TextUtils; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import com.eveningoutpost.dexdrip.Models.Calibration; import com.eveningoutpost.dexdrip.UtilityModels.CollectionServiceStarter; public class CalibrationOverride extends Activity implements NavigationDrawerFragment.NavigationDrawerCallbacks { Button button; private String menu_name = "Override Calibration"; private NavigationDrawerFragment mNavigationDrawerFragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if(CollectionServiceStarter.isBTShare(getApplicationContext())) { Intent intent = new Intent(this, Home.class); startActivity(intent); finish(); } setContentView(R.layout.activity_calibration_override); addListenerOnButton(); } @Override protected void onResume(){ super.onResume(); mNavigationDrawerFragment = (NavigationDrawerFragment) getFragmentManager().findFragmentById(R.id.navigation_drawer); mNavigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout), menu_name, this); } @Override public void onNavigationDrawerItemSelected(int position) { mNavigationDrawerFragment.swapContext(position); } public void addListenerOnButton() { button = (Button) findViewById(R.id.save_calibration_button); button.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { if (Sensor.isActive()) { EditText value = (EditText) findViewById(R.id.bg_value); String string_value = value.getText().toString(); if (!TextUtils.isEmpty(string_value)){ double calValue = Double.parseDouble(string_value); Calibration last_calibration = Calibration.last(); last_calibration.sensor_confidence = 0; last_calibration.slope_confidence = 0; last_calibration.save(); Calibration.create(calValue, getApplicationContext()); Intent tableIntent = new Intent(v.getContext(), Home.class); startActivity(tableIntent); finish(); } else { value.setError("Calibration Can Not be blank"); } } else { Log.w("CANNOT CALIBRATE WITHOUT CURRENT SENSOR", "ERROR"); } } }); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/DoubleCalibrationActivity.java ================================================ package com.eveningoutpost.dexdrip; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.v4.widget.DrawerLayout; import android.text.TextUtils; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import com.eveningoutpost.dexdrip.Models.Calibration; import com.eveningoutpost.dexdrip.UtilityModels.CollectionServiceStarter; public class DoubleCalibrationActivity extends Activity implements NavigationDrawerFragment.NavigationDrawerCallbacks { Button button; private String menu_name = "Add Double Calibration"; private NavigationDrawerFragment mNavigationDrawerFragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if(CollectionServiceStarter.isBTShare(getApplicationContext())) { Intent intent = new Intent(this, Home.class); startActivity(intent); finish(); } setContentView(R.layout.activity_double_calibration); addListenerOnButton(); } @Override protected void onResume(){ super.onResume(); mNavigationDrawerFragment = (NavigationDrawerFragment) getFragmentManager().findFragmentById(R.id.navigation_drawer); mNavigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout), menu_name, this); } @Override public void onNavigationDrawerItemSelected(int position) { mNavigationDrawerFragment.swapContext(position); } public void addListenerOnButton() { button = (Button) findViewById(R.id.save_calibration_button); button.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { if (Sensor.isActive()) { EditText value_1 = (EditText) findViewById(R.id.bg_value_1); EditText value_2 = (EditText) findViewById(R.id.bg_value_2); String string_value_1 = value_1.getText().toString(); String string_value_2 = value_2.getText().toString(); if (!TextUtils.isEmpty(string_value_1)){ if(!TextUtils.isEmpty(string_value_2)) { double calValue_1 = Double.parseDouble(string_value_1); double calValue_2 = Double.parseDouble(string_value_2); Calibration.initialCalibration(calValue_1, calValue_2, getApplicationContext()); Intent tableIntent = new Intent(v.getContext(), Home.class); startActivity(tableIntent); finish(); } else { value_2.setError("Calibration Can Not be blank"); } } else { value_1.setError("Calibration Can Not be blank"); } } else { Log.w("DoubleCalibration", "ERROR"); } } }); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/FakeNumbers.java ================================================ package com.eveningoutpost.dexdrip; import java.util.Date; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.DatePicker; import android.widget.EditText; import android.widget.TimePicker; import com.eveningoutpost.dexdrip.Models.BgReading; public class FakeNumbers extends Activity { public Button button; public DatePicker dp; public TimePicker tp; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_fake_numbers); button = (Button)findViewById(R.id.log); addListenerOnButton(); } public void addListenerOnButton() { button = (Button)findViewById(R.id.log); button.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { EditText value = (EditText) findViewById(R.id.bg_value); int intValue = Integer.parseInt(value.getText().toString()); BgReading bgReading = BgReading.create(intValue * 1000, getApplicationContext(), new Date().getTime()); Intent intent = new Intent(getApplicationContext(), Home.class); startActivity(intent); finish(); } }); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Home.java ================================================ package com.eveningoutpost.dexdrip; import android.app.Activity; import android.app.NotificationManager; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.graphics.Color; import android.graphics.Paint; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import android.support.v4.widget.DrawerLayout; import android.view.Menu; import android.view.MenuItem; import android.widget.TextView; import android.widget.Toast; import com.eveningoutpost.dexdrip.Models.ActiveBluetoothDevice; import com.eveningoutpost.dexdrip.Models.BgReading; import com.eveningoutpost.dexdrip.Models.Calibration; import com.eveningoutpost.dexdrip.Services.WixelReader; import com.eveningoutpost.dexdrip.UtilityModels.BgGraphBuilder; import com.eveningoutpost.dexdrip.UtilityModels.CollectionServiceStarter; import com.eveningoutpost.dexdrip.UtilityModels.Intents; import com.eveningoutpost.dexdrip.UtilityModels.Notifications; import com.eveningoutpost.dexdrip.utils.DatabaseUtil; import com.eveningoutpost.dexdrip.utils.ShareNotification; import java.io.File; import java.text.DecimalFormat; import java.util.Date; import java.util.List; import lecho.lib.hellocharts.ViewportChangeListener; import lecho.lib.hellocharts.gesture.ZoomType; import lecho.lib.hellocharts.model.Viewport; import lecho.lib.hellocharts.view.LineChartView; import lecho.lib.hellocharts.view.PreviewLineChartView; public class Home extends Activity implements NavigationDrawerFragment.NavigationDrawerCallbacks { private String menu_name = "xDrip"; private NavigationDrawerFragment mNavigationDrawerFragment; private LineChartView chart; private PreviewLineChartView previewChart; SharedPreferences prefs; Viewport tempViewport = new Viewport(); Viewport holdViewport = new Viewport(); public float left; public float right; public float top; public float bottom; public boolean updateStuff; public boolean updatingPreviewViewport = false; public boolean updatingChartViewport = false; boolean isBTWixel; boolean isBTShare; boolean isWifiWixel; public BgGraphBuilder bgGraphBuilder; BroadcastReceiver _broadcastReceiver; BroadcastReceiver newDataReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); CollectionServiceStarter collectionServiceStarter = new CollectionServiceStarter(getApplicationContext()); collectionServiceStarter.start(getApplicationContext()); PreferenceManager.setDefaultValues(this, R.xml.pref_notifications, false); PreferenceManager.setDefaultValues(this, R.xml.pref_data_source, false); prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); checkEula(); setContentView(R.layout.activity_home); } public void checkEula() { boolean IUnderstand = prefs.getBoolean("I_understand", false); if (!IUnderstand) { Intent intent = new Intent(getApplicationContext(), LicenseAgreementActivity.class); startActivity(intent); finish(); } } @Override protected void onResume(){ super.onResume(); checkEula(); _broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context ctx, Intent intent) { if (intent.getAction().compareTo(Intent.ACTION_TIME_TICK) == 0) { updateCurrentBgInfo(); } } }; newDataReceiver = new BroadcastReceiver() { @Override public void onReceive(Context ctx, Intent intent) { holdViewport.set(0, 0, 0, 0); setupCharts(); updateCurrentBgInfo(); } }; registerReceiver(_broadcastReceiver, new IntentFilter(Intent.ACTION_TIME_TICK)); registerReceiver(newDataReceiver, new IntentFilter(Intents.ACTION_NEW_BG_ESTIMATE_NO_DATA)); mNavigationDrawerFragment = (NavigationDrawerFragment) getFragmentManager().findFragmentById(R.id.navigation_drawer); mNavigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout), menu_name, this); holdViewport.set(0, 0, 0, 0); setupCharts(); updateCurrentBgInfo(); } public void setupCharts() { bgGraphBuilder = new BgGraphBuilder(this); updateStuff = false; chart = (LineChartView) findViewById(R.id.chart); chart.setZoomType(ZoomType.HORIZONTAL); previewChart = (PreviewLineChartView) findViewById(R.id.chart_preview); previewChart.setZoomType(ZoomType.HORIZONTAL); chart.setLineChartData(bgGraphBuilder.lineData()); previewChart.setLineChartData(bgGraphBuilder.previewLineData()); updateStuff = true; previewChart.setViewportCalculationEnabled(true); chart.setViewportCalculationEnabled(true); previewChart.setViewportChangeListener(new ViewportListener()); chart.setViewportChangeListener(new ChartViewPortListener()); setViewport(); } private class ChartViewPortListener implements ViewportChangeListener { @Override public void onViewportChanged(Viewport newViewport) { if (!updatingPreviewViewport) { updatingChartViewport = true; previewChart.setZoomType(ZoomType.HORIZONTAL); previewChart.setCurrentViewport(newViewport, false); updatingChartViewport = false; } } } private class ViewportListener implements ViewportChangeListener { @Override public void onViewportChanged(Viewport newViewport) { if (!updatingChartViewport) { updatingPreviewViewport = true; chart.setZoomType(ZoomType.HORIZONTAL); chart.setCurrentViewport(newViewport, false); tempViewport = newViewport; updatingPreviewViewport = false; } if (updateStuff == true) { holdViewport.set(newViewport.left, newViewport.top, newViewport.right, newViewport.bottom); } } } @Override public void onNavigationDrawerItemSelected(int position) { mNavigationDrawerFragment.swapContext(position); } public void setViewport() { if (tempViewport.left == 0.0 || holdViewport.left == 0.0 || holdViewport.right >= (new Date().getTime())) { previewChart.setCurrentViewport(bgGraphBuilder.advanceViewport(chart, previewChart), false); } else { previewChart.setCurrentViewport(holdViewport, false); } } @Override public void onPause() { super.onPause(); if (_broadcastReceiver != null) { unregisterReceiver(_broadcastReceiver); } if(newDataReceiver != null) { unregisterReceiver(newDataReceiver); } } public void updateCurrentBgInfo() { final TextView currentBgValueText = (TextView) findViewById(R.id.currentBgValueRealTime); final TextView notificationText = (TextView)findViewById(R.id.notices); notificationText.setText(""); isBTWixel = CollectionServiceStarter.isBTWixel(getApplicationContext()); isBTShare = CollectionServiceStarter.isBTShare(getApplicationContext()); isWifiWixel = CollectionServiceStarter.isWifiWixel(getApplicationContext()); if(isBTShare) { if((android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR2)) { notificationText.setText("Unfortunately your android version does not support Bluetooth Low Energy"); } else { String receiverSn = prefs.getString("share_key", "SM00000000").toUpperCase(); if (receiverSn.compareTo("SM00000000") == 0 || receiverSn.length() == 0) { notificationText.setText("Please set your Dex Receiver Serial Number in App Settings"); } else { if (receiverSn.length() < 10) { notificationText.setText("Double Check Dex Receiver Serial Number, should be 10 characters, don't forget the letters"); } else { if (ActiveBluetoothDevice.first() == null) { notificationText.setText("Now pair with your Dexcom Share"); } else { if (!Sensor.isActive()) { notificationText.setText("Now choose start your sensor in your settings"); } else { displayCurrentInfo(); } } } } } } if(isBTWixel) { if ((android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR2)) { notificationText.setText("Unfortunately your android version does not support Bluetooth Low Energy"); } else { if (ActiveBluetoothDevice.first() == null) { notificationText.setText("First pair with your BT device!"); } else { if (Sensor.isActive() && (Sensor.currentSensor().started_at + (60000 * 60 * 2)) < new Date().getTime()) { if (BgReading.latest(2).size() > 1) { List calibrations = Calibration.latest(2); if (calibrations.size() > 1) { if (calibrations.get(0).possible_bad != null && calibrations.get(0).possible_bad == true && calibrations.get(1).possible_bad != null && calibrations.get(1).possible_bad != true) { notificationText.setText("Possible bad calibration slope, please have a glass of water, wash hands, then recalibrate in a few!"); } displayCurrentInfo(); } else { notificationText.setText("Please enter two calibrations to get started!"); } } else { if(BgReading.latestUnCalculated(2).size() < 2) { notificationText.setText("Please wait, need 2 readings from transmitter first."); } else { List calibrations = Calibration.latest(2); if (calibrations.size() < 2) { notificationText.setText("Please enter two calibrations to get started!"); } } } } else if (Sensor.isActive() && ((Sensor.currentSensor().started_at + (60000 * 60 * 2))) >= new Date().getTime()) { double waitTime = ((Sensor.currentSensor().started_at + (60000 * 60 * 2)) - (new Date().getTime())) / (60000); notificationText.setText("Please wait while sensor warms up! (" + String.format("%.2f", waitTime) + " minutes)"); } else { notificationText.setText("Now start your sensor"); } } } } if(isWifiWixel) { if (!WixelReader.IsConfigured(getApplicationContext())) { notificationText.setText("First configure your wifi wixel reader ip addresses"); } else { if (Sensor.isActive() && (Sensor.currentSensor().started_at + (60000 * 60 * 2)) < new Date().getTime()) { if (BgReading.latest(2).size() > 1) { List calibrations = Calibration.latest(2); if (calibrations.size() > 1) { if (calibrations.get(0).possible_bad != null && calibrations.get(0).possible_bad == true && calibrations.get(1).possible_bad != null && calibrations.get(1).possible_bad != true) { notificationText.setText("Possible bad calibration slope, please have a glass of water, wash hands, then recalibrate in a few!"); } displayCurrentInfo(); } else { notificationText.setText("Please enter two calibrations to get started!"); } } else { notificationText.setText("Please wait, need 2 readings from transmitter first."); } } else if (Sensor.isActive() && ((Sensor.currentSensor().started_at + (60000 * 60 * 2))) >= new Date().getTime()) { double waitTime = ((Sensor.currentSensor().started_at + (60000 * 60 * 2)) - (new Date().getTime())) / (60000); notificationText.setText("Please wait while sensor warms up! (" + String.format("%.2f", waitTime) + " minutes)"); } else { notificationText.setText("Now start your sensor"); } } } mNavigationDrawerFragment = (NavigationDrawerFragment) getFragmentManager().findFragmentById(R.id.navigation_drawer); mNavigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout), menu_name, this); } public void displayCurrentInfo() { DecimalFormat df = new DecimalFormat("#"); df.setMaximumFractionDigits(0); final TextView currentBgValueText = (TextView)findViewById(R.id.currentBgValueRealTime); final TextView notificationText = (TextView)findViewById(R.id.notices); if ((currentBgValueText.getPaintFlags() & Paint.STRIKE_THRU_TEXT_FLAG) > 0) { currentBgValueText.setPaintFlags(currentBgValueText.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG)); } BgReading lastBgreading = BgReading.lastNoSenssor(); boolean predictive = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getBoolean("predictive_bg", false); if(isBTShare) { predictive = false; } if (lastBgreading != null) { double estimate = 0; if ((new Date().getTime()) - (60000 * 11) - lastBgreading.timestamp > 0) { notificationText.setText("Signal Missed"); if(!predictive){ estimate=lastBgreading.calculated_value; } else { estimate = BgReading.estimated_bg(lastBgreading.timestamp + (6000 * 7)); } currentBgValueText.setText(bgGraphBuilder.unitized_string(estimate)); currentBgValueText.setPaintFlags(currentBgValueText.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); } else { if(!predictive){ estimate=lastBgreading.calculated_value; String stringEstimate = bgGraphBuilder.unitized_string(estimate); String slope_arrow = BgReading.slopeArrow((lastBgreading.calculated_value_slope * 60000)); if(lastBgreading.hide_slope) { slope_arrow = ""; } currentBgValueText.setText( stringEstimate + " " + slope_arrow); } else { estimate = BgReading.activePrediction(); String stringEstimate = bgGraphBuilder.unitized_string(estimate); currentBgValueText.setText( stringEstimate + " " + BgReading.slopeArrow()); } } if(bgGraphBuilder.unitized(estimate) <= bgGraphBuilder.lowMark) { currentBgValueText.setTextColor(Color.parseColor("#C30909")); } else if(bgGraphBuilder.unitized(estimate) >= bgGraphBuilder.highMark) { currentBgValueText.setTextColor(Color.parseColor("#FFBB33")); } else { currentBgValueText.setTextColor(Color.WHITE); } } setupCharts(); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_home, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.action_export_database) { new AsyncTask() { @Override protected String doInBackground(Void... params) { return DatabaseUtil.saveSql(getBaseContext()); } @Override protected void onPostExecute(String filename) { super.onPostExecute(filename); final Context ctx = getApplicationContext(); Toast.makeText(ctx, "Export stored at " + filename, Toast.LENGTH_SHORT).show(); final NotificationCompat.Builder n = new NotificationCompat.Builder(ctx); n.setContentTitle("Export complete"); n.setContentText("Ready to be sent."); n.setAutoCancel(true); n.setSmallIcon(R.drawable.ic_action_communication_invert_colors_on); ShareNotification.viewOrShare("application/octet-stream", Uri.fromFile(new File(filename)), n, ctx); final NotificationManager manager = (NotificationManager) ctx.getSystemService(Service.NOTIFICATION_SERVICE); manager.notify(Notifications.exportCompleteNotificationId, n.build()); } }.execute(); return true; } return super.onOptionsItemSelected(item); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/dexcom/CRC16.java ================================================ package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom; // This code and this particular library are from the NightScout android uploader // Check them out here: https://github.com/nightscout/android-uploader // Some of this code may have been modified for use in this project public class CRC16 { public static byte[] calculate(byte[] buff, int start, int end) { int crcShort = 0; for (int i = start; i < end; i++) { crcShort = ((crcShort >>> 8) | (crcShort << 8) )& 0xffff; crcShort ^= (buff[i] & 0xff); crcShort ^= ((crcShort & 0xff) >> 4); crcShort ^= (crcShort << 12) & 0xffff; crcShort ^= ((crcShort & 0xFF) << 5) & 0xffff; } crcShort &= 0xffff; return new byte[] {(byte) (crcShort & 0xff), (byte) ((crcShort >> 8) & 0xff)}; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/dexcom/CRCFailRuntimeException.java ================================================ package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom; // This code and this particular library are from the NightScout android uploader // Check them out here: https://github.com/nightscout/android-uploader // Some of this code may have been modified for use in this project public class CRCFailRuntimeException extends RuntimeException { public CRCFailRuntimeException(String message){ super(message); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/dexcom/Constants.java ================================================ package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom; // This code and this particular library are from the NightScout android uploader // Check them out here: https://github.com/nightscout/android-uploader // Some of this code may have been modified for use in this project public class Constants { public final static int NULL = 0; public final static int ACK = 1; public final static int NAK = 2; public final static int INVALID_COMMAND = 3; public final static int INVALID_PARAM = 4; public final static int INCOMPLETE_PACKET_RECEIVED = 5; public final static int RECEIVER_ERROR = 6; public final static int INVALID_MODE = 7; public final static int PING = 10; public final static int READ_FIRMWARE_HEADER = 11; public final static int READ_DATABASE_PARTITION_INFO = 15; public final static int READ_DATABASE_PAGE_RANGE = 16; public final static int READ_DATABASE_PAGES = 17; public final static int READ_DATABASE_PAGE_HEADER = 18; public final static int READ_TRANSMITTER_ID = 25; public final static int WRITE_TRANSMITTER_ID = 26; public final static int READ_LANGUAGE = 27; public final static int WRITE_LANGUAGE = 28; public final static int READ_DISPLAY_TIME_OFFSET = 29; public final static int WRITE_DISPLAY_TIME_OFFSET = 30; public final static int READ_RTC = 31; public final static int RESET_RECEIVER = 32; public final static int READ_BATTERY_LEVEL = 33; public final static int READ_SYSTEM_TIME = 34; public final static int READ_SYSTEM_TIME_OFFSET = 35; public final static int WRITE_SYSTEM_TIME = 36; public final static int READ_GLUCOSE_UNIT = 37; public final static int WRITE_GLUCOSE_UNIT = 38; public final static int READ_BLINDED_MODE = 39; public final static int WRITE_BLINDED_MODE = 40; public final static int READ_CLOCK_MODE = 41; public final static int WRITE_CLOCK_MODE = 42; public final static int READ_DEVICE_MODE = 43; public final static int ERASE_DATABASE = 45; public final static int SHUTDOWN_RECEIVER = 46; public final static int WRITE_PC_PARAMETERS = 47; public final static int READ_BATTERY_STATE = 48; public final static int READ_HARDWARE_BOARD_ID = 49; public final static int READ_FIRMWARE_SETTINGS = 54; public final static int READ_ENABLE_SETUP_WIZARD_FLAG = 55; public final static int READ_SETUP_WIZARD_STATE = 57; public final static int MAX_COMMAND = 59; public final static int MAX_POSSIBLE_COMMAND = 255; public final static int EGV_VALUE_MASK = 1023; public final static int EGV_DISPLAY_ONLY_MASK = 32768; public final static int EGV_TREND_ARROW_MASK = 15; public final static int EGV_NOISE_MASK = 112; public final static float MG_DL_TO_MMOL_L = 0.05556f; public final static int CRC_LEN = 2; public enum BATTERY_STATES { NONE, CHARGING, NOT_CHARGING, NTC_FAULT, BAD_BATTERY } public enum RECORD_TYPES { MANUFACTURING_DATA, FIRMWARE_PARAMETER_DATA, PC_SOFTWARE_PARAMETER, SENSOR_DATA, EGV_DATA, CAL_SET, DEVIATION, INSERTION_TIME, RECEIVER_LOG_DATA, RECEIVER_ERROR_DATA, METER_DATA, USER_EVENT_DATA, USER_SETTING_DATA, MAX_VALUE } public enum TREND_ARROW_VALUES { NONE(0), DOUBLE_UP(1,"\u21C8", "DoubleUp"), SINGLE_UP(2,"\u2191", "SingleUp"), UP_45(3,"\u2197", "FortyFiveUp"), FLAT(4,"\u2192", "Flat"), DOWN_45(5,"\u2198", "FortyFiveDown"), SINGLE_DOWN(6,"\u2193", "SingleDown"), DOUBLE_DOWN(7,"\u21CA", "DoubleDown"), NOT_COMPUTABLE(8, "", "NOT_COMPUTABLE"), OUT_OF_RANGE(9, "", "OUT_OF_RANGE"); private String arrowSymbol; private String trendName; private int myID; TREND_ARROW_VALUES(int id, String a, String t) { myID=id; arrowSymbol = a; trendName = t; } TREND_ARROW_VALUES(int id) { this(id,null, null); } public String Symbol() { if (arrowSymbol == null) { return "\u2194"; } else { return arrowSymbol; } } public String friendlyTrendName() { if (trendName == null) { return this.name().replace("_", " "); } else { return this.trendName; } } public int getID(){ return myID; } } public enum SPECIALBGVALUES_MGDL { NONE("??0", 0), SENSORNOTACTIVE("?SN", 1), MINIMALLYEGVAB("??2", 2), NOANTENNA("?NA", 3), SENSOROUTOFCAL("?NC", 5), COUNTSAB("?CD", 6), ABSOLUTEAB("?AD", 9), POWERAB("???", 10), RFBADSTATUS("?RF", 12); private String name; private int val; private SPECIALBGVALUES_MGDL(String s, int i){ name=s; val=i; } public int getValue(){ return val; } public String toString(){ return name; } public static SPECIALBGVALUES_MGDL getEGVSpecialValue(int val){ for (SPECIALBGVALUES_MGDL e: values()){ if (e.getValue()==val) return e; } return null; } public static boolean isSpecialValue(int val){ for (SPECIALBGVALUES_MGDL e: values()){ if (e.getValue()==val) return true; } return false; } } public enum InsertionState { NONE, REMOVED, EXPIRED, RESIDUAL_DEVIATION, COUNTS_DEVIATION, SECOND_SESSION, OFF_TIME_LOSS, STARTED, BAD_TRANSMITTER, MANUFACTURING_MODE, MAX_VALUE } public enum NOISE { NOISE_NONE(0), CLEAN(1), LIGHT(2), MEDIUM(3), HEAVY(4), NOT_COMPUTED(5), MAX(6); private final int value; private NOISE(int value) { this.value = value; } } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/dexcom/PacketBuilder.java ================================================ package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom; import android.util.Log; import java.util.ArrayList; import java.util.List; // This code and this particular library are from the NightScout android uploader // Check them out here: https://github.com/nightscout/android-uploader // Some of this code may have been modified for use in this project public class PacketBuilder { public static final int MAX_PAYLOAD = 1584; public static final int MIN_LEN = 6; public static final int MAX_LEN = MAX_PAYLOAD + MIN_LEN; public static final byte SOF = 0x01; public static final int OFFSET_SOF = 0; public static final int OFFSET_LENGTH = 1; public static final int OFFSET_NULL = 2; public static final byte NULL = 0x00; public static final int OFFSET_CMD = 3; public static final int OFFSET_PAYLOAD = 4; public static final int CRC_LEN = 2; public static final int HEADER_LEN = 4; public ArrayList packet; public int command; public ArrayList payload; public PacketBuilder(int command) { this.command = command; } public PacketBuilder(int command, ArrayList payload) { this.command = command; this.payload = payload; } public byte[] compose() { packet = new ArrayList(); packet.add(OFFSET_SOF, SOF); packet.add(OFFSET_LENGTH, getLength()); packet.add(OFFSET_NULL, NULL); packet.add(OFFSET_CMD, (byte) command); if (this.payload != null) { this.packet.addAll(OFFSET_PAYLOAD, this.payload); } byte[] crc16 = CRC16.calculate(toBytes(), 0, this.packet.size()); this.packet.add(crc16[0]); this.packet.add(crc16[1]); Log.d("ShareTest", "About to start adding to Byte, size: " + this.packet.size()); return this.toBytes(); } public List composeList() { packet = new ArrayList(); packet.add(OFFSET_SOF, SOF); packet.add(OFFSET_LENGTH, getLength()); packet.add(OFFSET_NULL, NULL); packet.add(OFFSET_CMD, (byte) command); if (this.payload != null) { this.packet.addAll(OFFSET_PAYLOAD, this.payload); } byte[] crc16 = CRC16.calculate(toBytes(), 0, this.packet.size()); this.packet.add(crc16[0]); this.packet.add(crc16[1]); Log.d("ShareTest", "About to start adding to ByteList, size: " + this.packet.size()); return this.toBytesList(); } private byte getLength() { int packetSize = payload == null ? MIN_LEN : payload.size() + CRC_LEN + HEADER_LEN; if (packetSize > MAX_LEN) { throw new IndexOutOfBoundsException(packetSize + " bytes, but packet must between " + MIN_LEN + " and " + MAX_LEN + " bytes."); } return (byte) packetSize; } public byte[] toBytes() { byte[] b = new byte[this.packet.size()]; for (int i = 0; i < this.packet.size(); i++) { b[i] = this.packet.get(i).byteValue(); } return b; } public List toBytesList() { List byteMessages = new ArrayList(); double totalPacketSize = packet.size(); int messages =(int) Math.ceil(totalPacketSize/18); for(int m = 0; m < messages; m++) { int thisPacketSize; if (m == messages - 1) { thisPacketSize = ((this.packet.size()+2) % 18); } else { thisPacketSize = (20); } int offset = m * 18; Log.d("ShareTest", "This packet size: " + thisPacketSize); byte[] b = new byte[thisPacketSize]; b[0] = (byte) (m + 1); b[1] = (byte) (messages); for (int i = 2; i < thisPacketSize; i++) { b[i] = packet.get(offset + i - 2).byteValue(); } byteMessages.add(b); } return byteMessages; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/dexcom/ReadData.java ================================================ package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbInterface; import android.util.Log; import com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver.CdcAcmSerialDriver; import com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver.UsbSerialDriver; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.CalRecord; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.EGVRecord; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.GenericXMLRecord; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.MeterRecord; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.PageHeader; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.SensorRecord; import com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver.UsbSerialPort; import org.w3c.dom.Element; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; public class ReadData { // This code and this particular library are from the NightScout android uploader // Check them out here: https://github.com/nightscout/android-uploader // Some of this code may have been modified for use in this project private static final String TAG = ReadData.class.getSimpleName(); private static final int IO_TIMEOUT = 3000; private static final int MIN_LEN = 256; private UsbSerialDriver mSerialDevice; protected final Object mReadBufferLock = new Object(); private UsbDeviceConnection mConnection; private UsbDevice mDevice; public ReadData(){} public ReadData(UsbSerialDriver device) { mSerialDevice = device; } public ReadData(UsbSerialDriver device, UsbDeviceConnection connection, UsbDevice usbDevice) { mSerialDevice = device; mConnection = connection; mDevice = usbDevice; try { mSerialDevice.getPorts().get(0).open(connection); } catch(IOException e) { Log.w("FAILED WHILE", "trying to open"); } // } } public EGVRecord[] getRecentEGVs() { int recordType = Constants.RECORD_TYPES.EGV_DATA.ordinal(); int endPage = readDataBasePageRange(recordType); return readDataBasePage(recordType, endPage); } public EGVRecord[] getRecentEGVsPages(int numOfRecentPages) { if (numOfRecentPages < 1) { throw new IllegalArgumentException("Number of pages must be greater than 1."); } Log.d(TAG, "Reading EGV page range..."); int recordType = Constants.RECORD_TYPES.EGV_DATA.ordinal(); int endPage = readDataBasePageRange(recordType); Log.d(TAG, "Reading " + numOfRecentPages + " EGV page(s)..."); numOfRecentPages = numOfRecentPages - 1; EGVRecord[] allPages = new EGVRecord[0]; for (int i = Math.min(numOfRecentPages,endPage); i >= 0; i--) { int nextPage = endPage - i; Log.d(TAG, "Reading #" + i + " EGV pages (page number " + nextPage + ")"); EGVRecord[] ithEGVRecordPage = readDataBasePage(recordType, nextPage); EGVRecord[] result = Arrays.copyOf(allPages, allPages.length + ithEGVRecordPage.length); System.arraycopy(ithEGVRecordPage, 0, result, allPages.length, ithEGVRecordPage.length); allPages = result; } Log.d(TAG, "Read complete of EGV pages."); return allPages; } public long getTimeSinceEGVRecord(EGVRecord egvRecord) { return readSystemTime() - egvRecord.getSystemTimeSeconds(); } public MeterRecord[] getRecentMeterRecords() { Log.d(TAG, "Reading Meter page..."); int recordType = Constants.RECORD_TYPES.METER_DATA.ordinal(); int endPage = readDataBasePageRange(recordType); return readDataBasePage(recordType, endPage); } public SensorRecord[] getRecentSensorRecords(int numOfRecentPages) { if (numOfRecentPages < 1) { throw new IllegalArgumentException("Number of pages must be greater than 1."); } Log.d(TAG, "Reading Sensor page range..."); int recordType = Constants.RECORD_TYPES.SENSOR_DATA.ordinal(); int endPage = readDataBasePageRange(recordType); Log.d(TAG, "Reading " + numOfRecentPages + " Sensor page(s)..."); numOfRecentPages = numOfRecentPages - 1; SensorRecord[] allPages = new SensorRecord[0]; for (int i = Math.min(numOfRecentPages,endPage); i >= 0; i--) { int nextPage = endPage - i; Log.d(TAG, "Reading #" + i + " Sensor pages (page number " + nextPage + ")"); SensorRecord[] ithSensorRecordPage = readDataBasePage(recordType, nextPage); SensorRecord[] result = Arrays.copyOf(allPages, allPages.length + ithSensorRecordPage.length); System.arraycopy(ithSensorRecordPage, 0, result, allPages.length, ithSensorRecordPage.length); allPages = result; } Log.d(TAG, "Read complete of Sensor pages."); return allPages; } public CalRecord[] getRecentCalRecords() { Log.d(TAG, "Reading Cal Records page range..."); int recordType = Constants.RECORD_TYPES.CAL_SET.ordinal(); int endPage = readDataBasePageRange(recordType); Log.d(TAG, "Reading Cal Records page..."); return readDataBasePage(recordType, endPage); } public byte[] getRecentCalRecordsTest() { Log.d(TAG, "Reading Cal Records page range..."); int recordType = Constants.RECORD_TYPES.CAL_SET.ordinal(); int endPage = readDataBasePageRange(recordType); Log.d(TAG, "Reading Cal Records page..."); return readDataBasePageTest(recordType, endPage); } public boolean ping() { writeCommand(Constants.PING); return read(MIN_LEN).getCommand() == Constants.ACK; } public int readBatteryLevel() { Log.d(TAG, "Reading battery level..."); writeCommand(Constants.READ_BATTERY_LEVEL); byte[] readData = read(MIN_LEN).getData(); return ByteBuffer.wrap(readData).order(ByteOrder.LITTLE_ENDIAN).getInt(); } public String readSerialNumber() { int PAGE_OFFSET = 0; byte[] readData = readDataBasePage(Constants.RECORD_TYPES.MANUFACTURING_DATA.ordinal(), PAGE_OFFSET); Element md = ParsePage(readData, Constants.RECORD_TYPES.MANUFACTURING_DATA.ordinal()); return md.getAttribute("SerialNumber"); } public Date readDisplayTime() { return Utils.receiverTimeToDate(readSystemTime() + readDisplayTimeOffset()); } public long readSystemTime() { Log.d(TAG, "Reading system time..."); writeCommand(Constants.READ_SYSTEM_TIME); byte[] readData = read(MIN_LEN).getData(); return ByteBuffer.wrap(readData).order(ByteOrder.LITTLE_ENDIAN).getInt() & 0xffffffff; } public int readDisplayTimeOffset() { Log.d(TAG, "Reading display time offset..."); writeCommand(Constants.READ_DISPLAY_TIME_OFFSET); byte[] readData = read(MIN_LEN).getData(); return ByteBuffer.wrap(readData).order(ByteOrder.LITTLE_ENDIAN).getInt() & 0xffffffff; } private int readDataBasePageRange(int recordType) { ArrayList payload = new ArrayList(); Log.d(TAG, "adding Payload"); payload.add((byte) recordType); Log.d(TAG, "Sending write command"); writeCommand(Constants.READ_DATABASE_PAGE_RANGE, payload); Log.d(TAG, "About to call getdata"); byte[] readData = read(MIN_LEN).getData(); Log.d(TAG, "Going to return"); return ByteBuffer.wrap(readData).order(ByteOrder.LITTLE_ENDIAN).getInt(4); } private T readDataBasePage(int recordType, int page) { byte numOfPages = 1; if (page < 0){ throw new IllegalArgumentException("Invalid page requested:" + page); } ArrayList payload = new ArrayList(); payload.add((byte) recordType); byte[] pageInt = ByteBuffer.allocate(4).putInt(page).array(); payload.add(pageInt[3]); payload.add(pageInt[2]); payload.add(pageInt[1]); payload.add(pageInt[0]); payload.add(numOfPages); writeCommand(Constants.READ_DATABASE_PAGES, payload); byte[] readData = read(2122).getData(); return ParsePage(readData, recordType); } private byte[] readDataBasePageTest(int recordType, int page) { byte numOfPages = 1; if (page < 0){ throw new IllegalArgumentException("Invalid page requested:" + page); } ArrayList payload = new ArrayList(); payload.add((byte) recordType); byte[] pageInt = ByteBuffer.allocate(4).putInt(page).array(); payload.add(pageInt[3]); payload.add(pageInt[2]); payload.add(pageInt[1]); payload.add(pageInt[0]); payload.add(numOfPages); return writeCommandTest(Constants.READ_DATABASE_PAGES, payload); } private void writeCommand(int command, ArrayList payload) { byte[] packet = new PacketBuilder(command, payload).compose(); if (mSerialDevice != null) { try { // UsbInterface mDataInterface = mDevice.getInterface(1); // UsbEndpoint mWriteEndpoint = mDataInterface.getEndpoint(0); // mConnection.bulkTransfer(mWriteEndpoint, packet, packet.length, IO_TIMEOUT); mSerialDevice.getPorts().get(0).write(packet, IO_TIMEOUT); } catch (Exception e) { Log.e(TAG, "Unable to write to serial device.", e); } } } private byte[] writeCommandTest(int command, ArrayList payload) { byte[] packet = new PacketBuilder(command, payload).compose(); return packet; } private void writeCommand(int command) { byte[] packet = new PacketBuilder(command).compose(); if (mSerialDevice != null) { try { // UsbInterface mDataInterface = mDevice.getInterface(1); // UsbEndpoint mWriteEndpoint = mDataInterface.getEndpoint(0); // mConnection.bulkTransfer(mWriteEndpoint, packet, packet.length, IO_TIMEOUT); mSerialDevice.getPorts().get(0).write(packet, IO_TIMEOUT); } catch (Exception e) { Log.e(TAG, "Unable to write to serial device.", e); } } } private ReadPacket read(int numOfBytes) { byte[] readData = new byte[numOfBytes]; int len = 0; try { // UsbInterface mDataInterface = mDevice.getInterface(1); // UsbEndpoint mReadEndpoint = mDataInterface.getEndpoint(1); // byte[] mReadBuffer; // mReadBuffer = new byte[16 * 1024]; // // int readAmt = Math.min(readData.length, mReadBuffer.length); // synchronized (mReadBufferLock) { // // // Log.d(TAG, "Read about to call bulk transfer."); // if (len < 0) { // // This sucks: we get -1 on timeout, not 0 as preferred. // // We *should* use UsbRequest, except it has a bug/api oversight // // where there is no way to determine the number of bytes read // // in response :\ -- http://b.android.com/28023 // if (IO_TIMEOUT == Integer.MAX_VALUE) { // // Hack: Special case "~infinite timeout" as an error. // len = -1; // } // len = 0; // } // //// System.arraycopy(mReadBuffer, 0, readData, 0, readAmt); // } // len = mConnection.bulkTransfer(mReadEndpoint, readData, readAmt, IO_TIMEOUT); len = mSerialDevice.getPorts().get(0).read(readData, IO_TIMEOUT); Log.d(TAG, "Read " + len + " byte(s) complete."); // Add a 100ms delay for when multiple write/reads are occurring in series Thread.sleep(100); // TODO: this debug code to print data of the read, should be removed after // finding the source of the reading issue String bytes = ""; int readAmount = len; for (int i = 0; i < readAmount; i++) bytes += String.format("%02x", readData[i]) + " "; Log.d(TAG, "Read data: " + bytes); //////////////////////////////////////////////////////////////////////////////////////// } catch (Exception e) { Log.e(TAG, "Unable to read from serial device.", e); } byte[] data = Arrays.copyOfRange(readData, 0, len); return new ReadPacket(data); } private T ParsePage(byte[] data, int recordType) { int HEADER_LEN = 28; PageHeader pageHeader=new PageHeader(data); int NUM_REC_OFFSET = 4; int numRec = data[NUM_REC_OFFSET]; int rec_len; switch (Constants.RECORD_TYPES.values()[recordType]) { case MANUFACTURING_DATA: GenericXMLRecord xmlRecord = new GenericXMLRecord(Arrays.copyOfRange(data, HEADER_LEN, data.length - 1)); return (T) xmlRecord; case SENSOR_DATA: rec_len = 20; SensorRecord[] sensorRecords = new SensorRecord[numRec]; for (int i = 0; i < numRec; i++) { int startIdx = HEADER_LEN + rec_len * i; sensorRecords[i] = new SensorRecord(Arrays.copyOfRange(data, startIdx, startIdx + rec_len - 1)); } return (T) sensorRecords; case EGV_DATA: rec_len = 13; EGVRecord[] egvRecords = new EGVRecord[numRec]; for (int i = 0; i < numRec; i++) { int startIdx = HEADER_LEN + rec_len * i; egvRecords[i] = new EGVRecord(Arrays.copyOfRange(data, startIdx, startIdx + rec_len - 1)); } return (T) egvRecords; case METER_DATA: rec_len = 16; MeterRecord[] meterRecords = new MeterRecord[numRec]; for (int i = 0; i < numRec; i++) { int startIdx = HEADER_LEN + rec_len * i; meterRecords[i] = new MeterRecord(Arrays.copyOfRange(data, startIdx, startIdx + rec_len - 1)); } return (T) meterRecords; case CAL_SET: rec_len = 249; if (pageHeader.getRevision()<=2) { rec_len = 148; } CalRecord[] calRecords = new CalRecord[numRec]; for (int i = 0; i < numRec; i++) { int startIdx = HEADER_LEN + rec_len * i; calRecords[i] = new CalRecord(Arrays.copyOfRange(data, startIdx, startIdx + rec_len - 1)); } return (T) calRecords; default: // Throw error "Database record not supported" break; } return (T) null; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/dexcom/ReadDataShare.java ================================================ package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattService; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.util.Log; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.CalRecord; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.EGVRecord; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.GenericXMLRecord; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.MeterRecord; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.PageHeader; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.SensorRecord; import com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver.UsbSerialDriver; import com.eveningoutpost.dexdrip.Services.DexCollectionService; import com.eveningoutpost.dexdrip.Services.DexShareCollectionService; import com.eveningoutpost.dexdrip.ShareTest; import org.w3c.dom.Element; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigDecimal; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import rx.Observable; import rx.functions.Action1; // This code and this particular library are from the NightScout android uploader // Check them out here: https://github.com/nightscout/android-uploader // Some of this code may have been modified for use in this project public class ReadDataShare { byte[] accumulatedResponse; private ShareTest mShareTest; private DexShareCollectionService mCollectionService; public ReadDataShare(ShareTest aShareTest){ mShareTest = aShareTest; } public ReadDataShare(DexShareCollectionService collectionService){ mCollectionService = collectionService; } public void getRecentEGVs(final Action1 recordListener) { final int recordType = Constants.RECORD_TYPES.EGV_DATA.ordinal(); final Action1 fullPageListener = new Action1() { @Override public void call(byte[] s) { ParsePage(read(0,s).getData(), recordType, recordListener); } }; Action1 databasePageRangeCaller = new Action1() { @Override public void call(Integer s) { readDataBasePage(recordType, s, fullPageListener); } }; readDataBasePageRange(recordType, databasePageRangeCaller); } public void getRecentMeterRecords(final Action1 recordListener) { final int recordType = Constants.RECORD_TYPES.METER_DATA.ordinal(); final Action1 fullPageListener = new Action1() { @Override public void call(byte[] s) { ParsePage(read(0,s).getData(), recordType, recordListener); } }; Action1 databasePageRangeCaller = new Action1() { @Override public void call(Integer s) { readDataBasePage(recordType, s, fullPageListener); } }; readDataBasePageRange(recordType, databasePageRangeCaller); } public void getRecentCalRecords(final Action1 recordListener) { final int recordType = Constants.RECORD_TYPES.CAL_SET.ordinal(); final Action1 fullPageListener = new Action1() { @Override public void call(byte[] s) { ParsePage(read(0,s).getData(), recordType, recordListener); } }; Action1 databasePageRangeCaller = new Action1() { @Override public void call(Integer s) { readDataBasePage(recordType, s, fullPageListener); } }; readDataBasePageRange(recordType, databasePageRangeCaller); } public void getRecentSensorRecords(final Action1 recordListener) { final int recordType = Constants.RECORD_TYPES.SENSOR_DATA.ordinal(); final Action1 fullPageListener = new Action1() { @Override public void call(byte[] s) { ParsePage(read(0,s).getData(), recordType, recordListener); } }; Action1 databasePageRangeCaller = new Action1() { @Override public void call(Integer s) { readDataBasePage(recordType, s, fullPageListener); } }; readDataBasePageRange(recordType, databasePageRangeCaller); } public void getTimeSinceEGVRecord(final EGVRecord egvRecord, final Action1 timeSinceEgvRecord) { Action1 tempSystemTimeListener = new Action1() { @Override public void call(Long s) { Observable.just(s - egvRecord.getSystemTimeSeconds()).subscribe(timeSinceEgvRecord); } }; readSystemTime(tempSystemTimeListener); } public void ping(final Action1 pingListener) { Action1 pingReader = new Action1() { @Override public void call(byte[] s) { Observable.just(read(0, s).getCommand() == Constants.ACK).subscribe(pingListener); } }; writeCommand(Constants.PING, pingReader); } public void readBatteryLevel(final Action1 batteryLevelListener) { Action1 batteryLevelReader = new Action1() { @Override //TODO: find out if this should be wrapped in read(s).getData(); public void call(byte[] s) { Observable.just(ByteBuffer.wrap(s).order(ByteOrder.LITTLE_ENDIAN).getInt()).subscribe(batteryLevelListener); } }; writeCommand(Constants.READ_BATTERY_LEVEL, batteryLevelReader); } public void readSerialNumber(final Action1 serialNumberListener) { final Action1 manufacturingDataListener = new Action1() { @Override public void call(byte[] s) { Element el = ParsePage(s, Constants.RECORD_TYPES.MANUFACTURING_DATA.ordinal()); Observable.just(el.getAttribute("SerialNumber")).subscribe(serialNumberListener); } }; readDataBasePage(Constants.RECORD_TYPES.MANUFACTURING_DATA.ordinal(), 0, manufacturingDataListener); } public void readDisplayTime(final Action1 displayTimeListener) { Action1 tempSystemTimeListener = new Action1() { @Override public void call(Long s) { final long systemTime = s; Action1 tempSystemTimeListener = new Action1() { @Override public void call(Long s) { Date dateDisplayTime = Utils.receiverTimeToDate(systemTime + s); Observable.just(dateDisplayTime).subscribe(displayTimeListener); } }; readDisplayTimeOffset(tempSystemTimeListener); } }; readSystemTime(tempSystemTimeListener); } public void readSystemTime(final Action1 systemTimeListener) { Action1 systemTimeReader = new Action1() { @Override public void call(byte[] s) { Observable.just(Utils.receiverTimeToDate(ByteBuffer.wrap(read(0,s).getData()).order(ByteOrder.LITTLE_ENDIAN).getInt()).getTime()).subscribe(systemTimeListener); } }; writeCommand(Constants.READ_SYSTEM_TIME, systemTimeReader); } public void readDisplayTimeOffset(final Action1 displayTimeOffsetListener) { Action1 displayTimeOffsetReader = new Action1() { @Override public void call(byte[] s) { Observable.just((long) ByteBuffer.wrap(read(0,s).getData()).order(ByteOrder.LITTLE_ENDIAN).getInt()).subscribe(displayTimeOffsetListener); } }; writeCommand(Constants.READ_DISPLAY_TIME_OFFSET, displayTimeOffsetReader); } private void readDataBasePageRange(int recordType, final Action1 databasePageRangeCaller) { ArrayList payload = new ArrayList(); payload.add((byte) recordType); final Action1 databasePageRangeListener = new Action1() { @Override public void call(byte[] s) { Observable.just(ByteBuffer.wrap(new ReadPacket(s).getData()).order(ByteOrder.LITTLE_ENDIAN).getInt(4)).subscribe(databasePageRangeCaller); } }; writeCommand(Constants.READ_DATABASE_PAGE_RANGE, payload, databasePageRangeListener); } private T readDataBasePage(final int recordType, int page, final Action1 fullPageListener) { byte numOfPages = 1; if (page < 0){ throw new IllegalArgumentException("Invalid page requested:" + page); } ArrayList payload = new ArrayList(); payload.add((byte) recordType); byte[] pageInt = ByteBuffer.allocate(4).putInt(page).array(); payload.add(pageInt[3]); payload.add(pageInt[2]); payload.add(pageInt[1]); payload.add(pageInt[0]); payload.add(numOfPages); accumulatedResponse = null; final Action1 databasePageReader = new Action1() { @Override public void call(byte[] s) { Log.d("ShareTest", "Database Page Reader received SIZE: " + s.length); byte[] temp = s; if (accumulatedResponse == null) { accumulatedResponse = s; } else { try { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); outputStream.write(accumulatedResponse); outputStream.write(temp); accumulatedResponse = outputStream.toByteArray(); Log.d("ShareTest", "Combined Response length: " + accumulatedResponse.length); } catch (Exception e) { e.printStackTrace(); } } if (temp.length < 20) { Observable.just(accumulatedResponse).subscribe(fullPageListener).unsubscribe(); } } }; writeCommand(Constants.READ_DATABASE_PAGES, payload, databasePageReader); return null; } private void writeCommand(int command, ArrayList payload, Action1 responseListener) { List packets = new PacketBuilder(command, payload).composeList(); if(mShareTest != null) { mShareTest.writeCommand(packets, 0, responseListener); } else if (mCollectionService != null) { mCollectionService.writeCommand(packets, 0, responseListener); } } private void writeCommand(int command, Action1 responseListener) { List packets = new PacketBuilder(command).composeList(); if(mShareTest != null) { mShareTest.writeCommand(packets, 0, responseListener); } else if (mCollectionService != null) { mCollectionService.writeCommand(packets, 0, responseListener); } } private ReadPacket read(int numOfBytes, byte[] readPacket) { return new ReadPacket(Arrays.copyOfRange(readPacket, 0, readPacket.length)); } private T ParsePage(byte[] data, int recordType) { return ParsePage(data, recordType, null); } private T ParsePage(byte[] data, int recordType, Action1 parsedPageReceiver) { int HEADER_LEN = 28; PageHeader pageHeader=new PageHeader(data); int NUM_REC_OFFSET = 4; int numRec = data[NUM_REC_OFFSET]; int rec_len; switch (Constants.RECORD_TYPES.values()[recordType]) { case MANUFACTURING_DATA: GenericXMLRecord xmlRecord = new GenericXMLRecord(Arrays.copyOfRange(data, HEADER_LEN, data.length - 1)); if(parsedPageReceiver != null) { Observable.just((T) xmlRecord).subscribe(parsedPageReceiver); } else { return (T) xmlRecord; } break; case SENSOR_DATA: rec_len = 20; SensorRecord[] sensorRecords = new SensorRecord[numRec]; for (int i = 0; i < numRec; i++) { int startIdx = HEADER_LEN + rec_len * i; sensorRecords[i] = new SensorRecord(Arrays.copyOfRange(data, startIdx, startIdx + rec_len - 1)); } if(parsedPageReceiver != null) { Observable.just((T) sensorRecords).subscribe(parsedPageReceiver); } else { return (T) sensorRecords; } break; case EGV_DATA: rec_len = 13; EGVRecord[] egvRecords = new EGVRecord[numRec]; for (int i = 0; i < numRec; i++) { int startIdx = HEADER_LEN + rec_len * i; egvRecords[i] = new EGVRecord(Arrays.copyOfRange(data, startIdx, startIdx + rec_len - 1)); } if(parsedPageReceiver != null) { Observable.just((T) egvRecords).subscribe(parsedPageReceiver); } else { return (T) egvRecords; } break; case METER_DATA: rec_len = 16; MeterRecord[] meterRecords = new MeterRecord[numRec]; for (int i = 0; i < numRec; i++) { int startIdx = HEADER_LEN + rec_len * i; meterRecords[i] = new MeterRecord(Arrays.copyOfRange(data, startIdx, startIdx + rec_len - 1)); } if(parsedPageReceiver != null) { Observable.just((T) meterRecords).subscribe(parsedPageReceiver); } else { return (T) meterRecords; } break; case CAL_SET: rec_len = 249; if (pageHeader.getRevision()<=2) { rec_len = 148; } CalRecord[] calRecords = new CalRecord[numRec]; for (int i = 0; i < numRec; i++) { int startIdx = HEADER_LEN + rec_len * i; calRecords[i] = new CalRecord(Arrays.copyOfRange(data, startIdx, startIdx + rec_len - 1)); } if(parsedPageReceiver != null) { Observable.just((T) calRecords).subscribe(parsedPageReceiver); } else { return (T) calRecords; } break; default: break; } Observable.just((T) null).subscribe(parsedPageReceiver); return (T) null; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/dexcom/ReadPacket.java ================================================ package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom; import java.util.Arrays; // This code and this particular library are from the NightScout android uploader // Check them out here: https://github.com/nightscout/android-uploader // Some of this code may have been modified for use in this project public class ReadPacket { private int command; private byte[] data; private byte[] crc_calc; private byte[] crc; private int OFFSET_CMD = 3; private int OFFSET_DATA = 4; private int CRC_LEN = 2; public ReadPacket(byte[] readPacket) { this.command = readPacket[OFFSET_CMD]; this.data = Arrays.copyOfRange(readPacket, OFFSET_DATA, readPacket.length - CRC_LEN); this.crc = Arrays.copyOfRange(readPacket, readPacket.length - CRC_LEN, readPacket.length); this.crc_calc=CRC16.calculate(readPacket, 0, readPacket.length - 2); if (!Arrays.equals(this.crc, this.crc_calc)) { throw new CRCFailRuntimeException("CRC check failed: " + Utils.bytesToHex(this.crc) + " vs " + Utils.bytesToHex(this.crc_calc)); } } public int getCommand() { return command; } public byte[] getData() { return data; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/dexcom/SyncingService.java ================================================ package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom; import android.app.IntentService; import android.bluetooth.BluetoothClass; import android.content.Intent; import android.content.Context; import android.content.SharedPreferences; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbManager; import android.os.PowerManager; import android.preference.PreferenceManager; import android.util.Log; import android.widget.Toast; import com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver.CdcAcmSerialDriver; import com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver.ProbeTable; import com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver.UsbSerialDriver; import com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver.UsbSerialProber; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.CalRecord; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.EGVRecord; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.GlucoseDataSet; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.MeterRecord; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.SensorRecord; import com.eveningoutpost.dexdrip.Models.Calibration; import org.json.JSONArray; import java.io.IOException; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; // This code and this particular library are from the NightScout android uploader // Check them out here: https://github.com/nightscout/android-uploader // Some of this code may have been modified for use in this project /** * An {@link IntentService} subclass for handling asynchronous CGM Receiver downloads and cloud uploads * requests in a service on a separate handler thread. */ public class SyncingService extends IntentService { // Action for intent private static final String ACTION_SYNC = "com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.action.SYNC"; private static final String ACTION_CALIBRATION_CHECKIN = "com.eveningoutpost.dexdrip.CalibrationCheckInActivity"; // Parameters for intent private static final String SYNC_PERIOD = "com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.extra.SYNC_PERIOD"; // Response to broadcast to activity public static final String RESPONSE_SGV = "mySGV"; public static final String RESPONSE_TREND = "myTrend"; public static final String RESPONSE_TIMESTAMP = "myTimestamp"; public static final String RESPONSE_NEXT_UPLOAD_TIME = "myUploadTime"; public static final String RESPONSE_UPLOAD_STATUS = "myUploadStatus"; public static final String RESPONSE_DISPLAY_TIME = "myDisplayTime"; public static final String RESPONSE_JSON = "myJSON"; public static final String RESPONSE_BAT = "myBatLvl"; private final String TAG = SyncingService.class.getSimpleName(); private Context mContext; private UsbManager mUsbManager; private UsbSerialDriver mSerialDevice; private UsbDevice dexcom; private UsbDeviceConnection mConnection; // Constants private final int TIME_SYNC_OFFSET = 10000; public static final int MIN_SYNC_PAGES = 2; public static final int GAP_SYNC_PAGES = 20; /** * Starts this service to perform action Single Sync with the given parameters. If * the service is already performing a task this action will be queued. * * @see IntentService */ public static void startActionSingleSync(Context context, int numOfPages) { Intent intent = new Intent(context, SyncingService.class); intent.setAction(ACTION_SYNC); intent.putExtra(SYNC_PERIOD, numOfPages); context.startService(intent); } public static void startActionCalibrationCheckin(Context context) { Intent intent = new Intent(context, SyncingService.class); intent.setAction(ACTION_CALIBRATION_CHECKIN); context.startService(intent); } public SyncingService() { super("SyncingService"); } @Override protected void onHandleIntent(Intent intent) { mContext = getApplicationContext(); if (intent != null) { final String action = intent.getAction(); if (ACTION_SYNC.equals(action)) { final int param1 = intent.getIntExtra(SYNC_PERIOD, 1); handleActionSync(param1); } else if (ACTION_CALIBRATION_CHECKIN.equals(action)) { Log.w("CALIBRATION-CHECK-IN: ", "Beginning check in process"); performCalibrationCheckin(); } } } /** * Handle action Sync in the provided background thread with the provided * parameters. */ private void performCalibrationCheckin(){ PowerManager pm = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE); PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "NSDownload"); wl.acquire(); Log.w("CALIBRATION-CHECK-IN: ", "Wake Lock Acquired"); if (acquireSerialDevice()) { try { ReadData readData = new ReadData(mSerialDevice, mConnection, dexcom); // ReadData readData = new ReadData(mSerialDevice); CalRecord[] calRecords = readData.getRecentCalRecords(); Log.w("CALIBRATION-CHECK-IN: ", "Found "+ calRecords.length + " Records!"); save_most_recent_cal_record(calRecords); } catch (Exception e) { Log.wtf("Unhandled exception caught", e); } finally { // Close serial try { mSerialDevice.getPorts().get(0).close(); } catch (IOException e) { Log.e(TAG, "Unable to close", e); } } } else { Log.w("CALIBRATION-CHECK-IN: ", "Failed to acquire serial device"); } } private void handleActionSync(int numOfPages) { boolean broadcastSent = false; PowerManager pm = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE); PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "NSDownload"); wl.acquire(); if (acquireSerialDevice()) { try { ReadData readData = new ReadData(mSerialDevice); // TODO: need to check if numOfPages if valid on ReadData side EGVRecord[] recentRecords = readData.getRecentEGVsPages(numOfPages); MeterRecord[] meterRecords = readData.getRecentMeterRecords(); // TODO: need to check if numOfPages if valid on ReadData side SensorRecord[] sensorRecords = readData.getRecentSensorRecords(numOfPages); GlucoseDataSet[] glucoseDataSets = Utils.mergeGlucoseDataRecords(recentRecords, sensorRecords); // FIXME: This is a workaround for the new Dexcom AP which seems to have a new format SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); CalRecord[] calRecords = new CalRecord[1]; if (prefs.getBoolean("cloud_cal_data", false)) { calRecords = readData.getRecentCalRecords(); } long timeSinceLastRecord = readData.getTimeSinceEGVRecord(recentRecords[recentRecords.length - 1]); // TODO: determine if the logic here is correct. I suspect it assumes the last record was less than 5 // minutes ago. If a reading is skipped and the device is plugged in then nextUploadTime will be // set to a negative number. This situation will eventually correct itself. long nextUploadTime = (1000 * 60 * 5) - (timeSinceLastRecord * (1000)); long displayTime = readData.readDisplayTime().getTime(); // FIXME: Device seems to flake out on battery level reads. Removing for now. // int batLevel = readData.readBatteryLevel(); int batLevel = 100; // convert into json for d3 plot JSONArray array = new JSONArray(); for (int i = 0; i < recentRecords.length; i++) array.put(recentRecords[i].toJSON()); EGVRecord recentEGV = recentRecords[recentRecords.length - 1]; // broadcastSGVToUI(recentEGV, uploadStatus, nextUploadTime + TIME_SYNC_OFFSET, // displayTime, array ,batLevel); broadcastSent=true; } catch (ArrayIndexOutOfBoundsException e) { Log.wtf("Unable to read from the dexcom, maybe it will work next time", e); } catch (NegativeArraySizeException e) { Log.wtf("Negative array exception from receiver", e); } catch (IndexOutOfBoundsException e) { Log.wtf("IndexOutOfBounds exception from receiver", e); } catch (CRCFailRuntimeException e){ // FIXME: may consider localizing this catch at a lower level (like ReadData) so that // if the CRC check fails on one type of record we can capture the values if it // doesn't fail on other types of records. This means we'd need to broadcast back // partial results to the UI. Adding it to a lower level could make the ReadData class // more difficult to maintain - needs discussion. Log.wtf("CRC failed", e); } catch (Exception e) { Log.wtf("Unhandled exception caught", e); } finally { // Close serial try { mSerialDevice.getPorts().get(0).close(); } catch (IOException e) { Log.e(TAG, "Unable to close", e); } } } // if (!broadcastSent) broadcastSGVToUI(); wl.release(); } private void save_most_recent_cal_record(CalRecord[] calRecords) { int size = calRecords.length; Calibration.create(calRecords,getApplicationContext(), false, 0); } private boolean acquireSerialDevice() { UsbDevice found_device = findDexcom(); if(mUsbManager == null) { Log.w("CALIBRATION-CHECK-IN: ", "USB manager is null"); } if( mUsbManager.hasPermission(dexcom)) { // the system is allowing us to poke around this device ProbeTable customTable = new ProbeTable(); // From the USB library... customTable.addProduct(0x22A3, 0x0047, CdcAcmSerialDriver.class); // ...Specify the Vendor ID and Product ID UsbSerialProber prober = new UsbSerialProber(customTable); // Probe the device with the custom values List drivers = prober.findAllDrivers(mUsbManager); // let's go through the list Iterator foo = drivers.iterator(); // Invalid Return code while (foo.hasNext()) { // let's loop through UsbSerialDriver driver = foo.next(); // set fooDriver to the next available driver if (driver != null) { UsbDeviceConnection connection = mUsbManager.openDevice(driver.getDevice()); if (connection != null) { mSerialDevice = driver; mConnection = connection; Log.w("CALIBRATION-CHECK-IN: ", "CONNECTEDDDD!!"); return true; } } else { Log.w("CALIBRATION-CHECK-IN: ", "Driver was no good"); } } Log.w("CALIBRATION-CHECK-IN: ", "No usable drivers found"); } else { Log.w("CALIBRATION-CHECK-IN: ", "You dont have permissions for that dexcom!!"); } return false; } static public boolean isG4Connected(Context c){ UsbManager manager = (UsbManager) c.getSystemService(Context.USB_SERVICE); HashMap deviceList = manager.getDeviceList(); Log.w("USB DEVICES = ", deviceList.toString()); Iterator deviceIterator = deviceList.values().iterator(); Log.w("USB DEVICES = ", String.valueOf(deviceList.size())); while(deviceIterator.hasNext()){ UsbDevice device = deviceIterator.next(); if (device.getVendorId() == 8867 && device.getProductId() == 71 && device.getDeviceClass() == 2 && device.getDeviceSubclass() ==0 && device.getDeviceProtocol() == 0){ Log.w("CALIBRATION-CHECK-IN: ", "Dexcom Found!"); return true; } } return false; } public UsbDevice findDexcom() { Log.w("CALIBRATION-CHECK-IN: ", "Searching for dexcom"); mUsbManager = (UsbManager) getApplicationContext().getSystemService(Context.USB_SERVICE); Log.w("USB MANAGER = ", mUsbManager.toString()); HashMap deviceList = mUsbManager.getDeviceList(); Log.w("USB DEVICES = ", deviceList.toString()); Iterator deviceIterator = deviceList.values().iterator(); Log.w("USB DEVICES = ", String.valueOf(deviceList.size())); while(deviceIterator.hasNext()){ UsbDevice device = deviceIterator.next(); if (device.getVendorId() == 8867 && device.getProductId() == 71 && device.getDeviceClass() == 2 && device.getDeviceSubclass() ==0 && device.getDeviceProtocol() == 0){ dexcom = device; Log.w("CALIBRATION-CHECK-IN: ", "Dexcom Found!"); return device; } else { Log.w("CALIBRATION-CHECK-IN: ", "that was not a dexcom (I dont think)"); } } return null; } private void broadcastSGVToUI(EGVRecord egvRecord, boolean uploadStatus, long nextUploadTime, long displayTime, JSONArray json, int batLvl) { Log.d(TAG, "Current EGV: " + egvRecord.getBGValue()); Intent broadcastIntent = new Intent(); // broadcastIntent.setAction(MainActivity.CGMStatusReceiver.PROCESS_RESPONSE); broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT); broadcastIntent.putExtra(RESPONSE_SGV, egvRecord.getBGValue()); broadcastIntent.putExtra(RESPONSE_TREND, egvRecord.getTrend().getID()); broadcastIntent.putExtra(RESPONSE_TIMESTAMP, egvRecord.getDisplayTime().getTime()); broadcastIntent.putExtra(RESPONSE_NEXT_UPLOAD_TIME, nextUploadTime); broadcastIntent.putExtra(RESPONSE_UPLOAD_STATUS, uploadStatus); broadcastIntent.putExtra(RESPONSE_DISPLAY_TIME, displayTime); if (json!=null) broadcastIntent.putExtra(RESPONSE_JSON, json.toString()); broadcastIntent.putExtra(RESPONSE_BAT, batLvl); sendBroadcast(broadcastIntent); } private void broadcastSGVToUI() { EGVRecord record=new EGVRecord(-1, Constants.TREND_ARROW_VALUES.NONE,new Date(),new Date()); broadcastSGVToUI(record,false, (long) (1000 * 60 * 5) + TIME_SYNC_OFFSET, new Date().getTime(), null, 0); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/dexcom/Utils.java ================================================ package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.EGVRecord; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.GlucoseDataSet; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.SensorRecord; import java.util.Date; import java.util.TimeZone; // This code and this particular library are from the NightScout android uploader // Check them out here: https://github.com/nightscout/android-uploader // Some of this code may have been modified for use in this project public class Utils { public static Date receiverTimeToDate(long delta) { int currentTZOffset = TimeZone.getDefault().getRawOffset(); long epochMS = 1230768000000L; // Jan 01, 2009 00:00 in UTC long milliseconds = epochMS - currentTZOffset; long timeAdd = milliseconds + (1000L * delta); TimeZone tz = TimeZone.getDefault(); if (tz.inDaylightTime(new Date())) timeAdd = timeAdd - (1000 * 60 * 60); return new Date(timeAdd); } public static String getTimeString(long timeDeltaMS) { long minutes = (timeDeltaMS / 1000) / 60; long hours = minutes / 60; long days = hours / 24; long weeks = days / 7; minutes= minutes - hours * 60; hours = hours - days * 24; days= days - weeks * 7; String timeAgoString = ""; if (weeks > 0) { timeAgoString += weeks + " weeks "; } if (days > 0) { timeAgoString += days + " days "; } if (hours > 0) { timeAgoString += hours + " hours "; } if (minutes >= 0) { timeAgoString += minutes + " min "; } return (timeAgoString.equals("") ? "--" : timeAgoString + "ago"); } public static GlucoseDataSet[] mergeGlucoseDataRecords(EGVRecord[] egvRecords, SensorRecord[] sensorRecords) { int egvLength = egvRecords.length; int sensorLength = sensorRecords.length; int smallerLength = egvLength < sensorLength ? egvLength : sensorLength; GlucoseDataSet[] glucoseDataSets = new GlucoseDataSet[smallerLength]; for (int i = 1; i <= smallerLength; i++) { glucoseDataSets[smallerLength - i] = new GlucoseDataSet(egvRecords[egvLength - i], sensorRecords[sensorLength - i]); } return glucoseDataSets; } public static String bytesToHex(byte[] bytes) { char[] hexArray = "0123456789ABCDEF".toCharArray(); char[] hexChars = new char[bytes.length * 3]; for ( int j = 0; j < bytes.length; j++ ) { int v = bytes[j] & 0xFF; hexChars[j * 3] = hexArray[v >>> 4]; hexChars[j * 3 + 1] = hexArray[v & 0x0F]; hexChars[j * 3 + 2] = " ".toCharArray()[0]; } return new String(hexChars); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/dexcom/records/CalRecord.java ================================================ package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records; import android.util.Log; import java.nio.ByteBuffer; import java.nio.ByteOrder; // This code and this particular library are from the NightScout android uploader // Check them out here: https://github.com/nightscout/android-uploader // Some of this code may have been modified for use in this project public class CalRecord extends GenericTimestampRecord { private static final String TAG = CalRecord.class.getSimpleName(); private double slope; private double intercept; private double scale; private int[] unk = new int[3]; private double decay; private int numRecords; private CalSubrecord[] calSubrecords = new CalSubrecord[12]; private int SUB_LEN = 17; public CalRecord(byte[] packet) { super(packet); slope = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getDouble(8); intercept = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getDouble(16); scale = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getDouble(24); unk[0] = packet[32]; unk[1] = packet[33]; unk[2] = packet[34]; decay = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getDouble(35); numRecords = packet[43]; long displayTimeOffset = (getDisplayTime().getTime() - getSystemTime().getTime()) / (1000); int start = 44; for (int i = 0; i < numRecords; i++) { Log.d("CalDebug","Loop #"+i); byte[] temp = new byte[SUB_LEN]; System.arraycopy(packet, start, temp, 0, temp.length); calSubrecords[i] = new CalSubrecord(temp, displayTimeOffset); start += SUB_LEN; } Log.d("ShareTest", "slope: " + slope + " intercept: " + intercept); } public double getSlope() { return slope; } public double getIntercept() { return intercept; } public double getScale() { return scale; } public int[] getUnk() { return unk; } public double getDecay() { return decay; } public int getNumRecords() { return numRecords; } public CalSubrecord[] getCalSubrecords() { return calSubrecords; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/dexcom/records/CalSubrecord.java ================================================ package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records; import android.util.Log; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.Utils; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Date; // This code and this particular library are from the NightScout android uploader // Check them out here: https://github.com/nightscout/android-uploader // Some of this code may have been modified for use in this project public class CalSubrecord { private static final String TAG = CalSubrecord.class.getSimpleName(); private Date dateEntered; private int calBGL; private int calRaw; private Date dateApplied; private byte unk; public CalSubrecord(byte[] packet, long displayTimeOffset) { int delta = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(); dateEntered = Utils.receiverTimeToDate(delta + displayTimeOffset); calBGL = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(4); calRaw = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(8); delta = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(12); dateApplied = Utils.receiverTimeToDate(delta + displayTimeOffset); unk = packet[16]; } public Date getDateEntered() { return dateEntered; } public int getCalBGL() { return calBGL; } public int getCalRaw() { return calRaw; } public Date getDateApplied() { return dateApplied; } public byte getUnk() { return unk; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/dexcom/records/EGVRecord.java ================================================ package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records; import android.util.Log; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.Constants; import org.json.JSONException; import org.json.JSONObject; import java.io.Serializable; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Date; // This code and this particular library are from the NightScout android uploader // Check them out here: https://github.com/nightscout/android-uploader // Some of this code may have been modified for use in this project public class EGVRecord extends GenericTimestampRecord { private int bGValue; private int noise; private Constants.TREND_ARROW_VALUES trend; public EGVRecord(byte[] packet) { // system_time (UInt), display_time (UInt), glucose (UShort), trend_arrow (Byte), crc (UShort)) super(packet); bGValue = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getShort(8) & Constants.EGV_VALUE_MASK; byte trendAndNoise = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).get(10); int trendValue = trendAndNoise & Constants.EGV_TREND_ARROW_MASK; byte noiseValue = (byte) ((trendAndNoise & Constants.EGV_NOISE_MASK) >> 4); trend = Constants.TREND_ARROW_VALUES.values()[trendValue]; noise = noiseValue; } public EGVRecord(int bGValue,Constants.TREND_ARROW_VALUES trend,Date displayTime, Date systemTime){ super(displayTime, systemTime); this.bGValue=bGValue; this.trend=trend; } public String noiseValue() { return String.valueOf(noise); } public int getBGValue() { return bGValue; } public Constants.TREND_ARROW_VALUES getTrend() { return trend; } public JSONObject toJSON() { JSONObject obj = new JSONObject(); try { obj.put("sgv", getBGValue()); obj.put("date", getDisplayTimeSeconds()); } catch (JSONException e) { e.printStackTrace(); } return obj; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/dexcom/records/GenericTimestampRecord.java ================================================ package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.Utils; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Date; // This code and this particular library are from the NightScout android uploader // Check them out here: https://github.com/nightscout/android-uploader // Some of this code may have been modified for use in this project public class GenericTimestampRecord { protected final int OFFSET_SYS_TIME = 0; protected final int OFFSET_DISPLAY_TIME = 4; protected Date systemTime; protected int systemTimeSeconds; protected Date displayTime; public GenericTimestampRecord(byte[] packet) { systemTimeSeconds = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(OFFSET_SYS_TIME); systemTime = Utils.receiverTimeToDate(systemTimeSeconds); int dt = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(OFFSET_DISPLAY_TIME); displayTime = Utils.receiverTimeToDate(dt); } public GenericTimestampRecord(Date displayTime, Date systemTime){ this.displayTime=displayTime; this.systemTime=systemTime; } public Date getSystemTime() { return systemTime; } public int getSystemTimeSeconds() { return systemTimeSeconds; } public Date getDisplayTime() { return displayTime; } public long getDisplayTimeSeconds() { return displayTime.getTime(); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/dexcom/records/GenericXMLRecord.java ================================================ package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records; import android.util.Log; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.InputSource; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.Serializable; import java.io.StringReader; import java.util.Arrays; // This code and this particular library are from the NightScout android uploader // Check them out here: https://github.com/nightscout/android-uploader // Some of this code may have been modified for use in this project public class GenericXMLRecord extends GenericTimestampRecord { int XML_START = 8; int XML_END = 241; private final String TAG = GenericXMLRecord.class.getSimpleName(); private Element xmlElement; public GenericXMLRecord(byte[] packet) { super(packet); Document document; // TODO: it would be best if we could just remove /x00 characters and read till end String xml = new String(Arrays.copyOfRange(packet, XML_START, XML_END)); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder; try { builder = factory.newDocumentBuilder(); document = builder.parse(new InputSource(new StringReader(xml))); xmlElement = document.getDocumentElement(); } catch (Exception e) { Log.e(TAG, "Unable to build xml element", e); } } // example: String sn = getXmlElement().getAttribute("SerialNumber"); public Element getXmlElement() { return xmlElement; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/dexcom/records/GlucoseDataSet.java ================================================ package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.Constants; import java.util.Date; // This code and this particular library are from the NightScout android uploader // Check them out here: https://github.com/nightscout/android-uploader // Some of this code may have been modified for use in this project public class GlucoseDataSet { private Date systemTime; private Date displayTime; private int bGValue; private Constants.TREND_ARROW_VALUES trend; private long unfiltered; private long filtered; private int rssi; public GlucoseDataSet(EGVRecord egvRecord, SensorRecord sensorRecord) { // TODO check times match between record systemTime = egvRecord.getSystemTime(); displayTime = egvRecord.getDisplayTime(); bGValue = egvRecord.getBGValue(); trend = egvRecord.getTrend(); unfiltered = sensorRecord.getUnfiltered(); filtered = sensorRecord.getFiltered(); rssi = sensorRecord.getRSSI(); } public Date getSystemTime() { return systemTime; } public Date getDisplayTime() { return displayTime; } public int getBGValue() { return bGValue; } public Constants.TREND_ARROW_VALUES getTrend() { return trend; } public String getTrendSymbol() { return trend.Symbol(); } public long getUnfiltered() { return unfiltered; } public long getFiltered() { return filtered; } public int getRssi() { return rssi; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/dexcom/records/MeterRecord.java ================================================ package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records; import java.nio.ByteBuffer; import java.nio.ByteOrder; // This code and this particular library are from the NightScout android uploader // Check them out here: https://github.com/nightscout/android-uploader // Some of this code may have been modified for use in this project public class MeterRecord extends GenericTimestampRecord { private int meterBG; private int meterTime; public MeterRecord(byte[] packet) { super(packet); meterBG = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getShort(8); meterTime = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(10); } public int getMeterBG() { return meterBG; } public int getMeterTime() { return meterTime; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/dexcom/records/PageHeader.java ================================================ package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records; import android.util.Log; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.CRC16; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.CRCFailRuntimeException; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.Constants; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.Utils; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; // This code and this particular library are from the NightScout android uploader // Check them out here: https://github.com/nightscout/android-uploader // Some of this code may have been modified for use in this project public class PageHeader { protected final int HEADER_SIZE=28; protected final int FIRSTRECORDINDEX_OFFSET=0; protected final int NUMRECS_OFFSET=4; protected final int RECTYPE_OFFSET=8; protected final int REV_OFFSET=9; protected final int PAGENUMBER_OFFSET=10; protected final int RESERVED2_OFFSET=14; protected final int RESERVED3_OFFSET=18; protected final int RESERVED4_OFFSET=22; protected int firstRecordIndex; protected int numOfRecords; protected Constants.RECORD_TYPES recordType; protected byte revision; protected int pageNumber; protected int reserved2; protected int reserved3; protected int reserved4; protected byte[] crc=new byte[2]; public PageHeader(byte[] packet) { Log.d("ShareTest", "Header Packet Data Length: " + packet.length); firstRecordIndex = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(FIRSTRECORDINDEX_OFFSET); numOfRecords = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(NUMRECS_OFFSET); recordType = Constants.RECORD_TYPES.values()[packet[RECTYPE_OFFSET]]; revision = packet[REV_OFFSET]; pageNumber = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(PAGENUMBER_OFFSET); reserved2 = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(RESERVED2_OFFSET); reserved3 = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(RESERVED3_OFFSET); reserved4 = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(RESERVED4_OFFSET); System.arraycopy(packet,HEADER_SIZE-Constants.CRC_LEN,crc,0,Constants.CRC_LEN); byte[] crc_calc = CRC16.calculate(packet,0,HEADER_SIZE - Constants.CRC_LEN); if (!Arrays.equals(this.crc, crc_calc)) { throw new CRCFailRuntimeException("CRC check failed: " + Utils.bytesToHex(this.crc) + " vs " + Utils.bytesToHex(crc_calc)); } } public byte getRevision() { return revision; } public Constants.RECORD_TYPES getRecordType() { return recordType; } public int getFirstRecordIndex() { return firstRecordIndex; } public int getNumOfRecords() { return numOfRecords; } public int getPageNumber() { return pageNumber; } public int getReserved2() { return reserved2; } public int getReserved3() { return reserved3; } public int getReserved4() { return reserved4; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/dexcom/records/SensorRecord.java ================================================ package com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records; import android.util.Log; import java.nio.ByteBuffer; import java.nio.ByteOrder; // This code and this particular library are from the NightScout android uploader // Check them out here: https://github.com/nightscout/android-uploader // Some of this code may have been modified for use in this project public class SensorRecord extends GenericTimestampRecord { private int unfiltered; private int filtered; private int rssi; private int OFFSET_UNFILTERED = 8; private int OFFSET_FILTERED = 12; private int OFFSET_RSSI = 16; public SensorRecord(byte[] packet) { super(packet); unfiltered = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(OFFSET_UNFILTERED); filtered = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getInt(OFFSET_FILTERED); rssi = ByteBuffer.wrap(packet).order(ByteOrder.LITTLE_ENDIAN).getShort(OFFSET_RSSI); Log.d("ShareTest", "filtered: " + filtered + " unfiltered: " + unfiltered); } public long getUnfiltered() { return unfiltered; } public long getFiltered() { return filtered; } public int getRSSI() { return rssi; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/usbserial/BuildInfo.java ================================================ package com.eveningoutpost.dexdrip.ImportedLibraries.usbserial; /** * Static container of information about this library. */ public final class BuildInfo { /** * The current version of this library. Values are of the form * "major.minor.micro[-suffix]". A suffix of "-pre" indicates a pre-release * of the version preceeding it. */ public static final String VERSION = "0.2.0-pre"; private BuildInfo() { throw new IllegalStateException("Non-instantiable class."); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/usbserial/driver/CdcAcmSerialDriver.java ================================================ /* Copyright 2011-2013 Google Inc. * Copyright 2013 mike wakerly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Project home page: https://github.com/mik3y/usb-serial-for-android */ package com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver; import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbRequest; import android.os.Build; import android.util.Log; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * USB CDC/ACM serial driver implementation. * * @author mike wakerly (opensource@hoho.com) * @see Universal * Serial Bus Class Definitions for Communication Devices, v1.1 */ public class CdcAcmSerialDriver implements UsbSerialDriver { private final String TAG = CdcAcmSerialDriver.class.getSimpleName(); private final UsbDevice mDevice; private final UsbSerialPort mPort; public CdcAcmSerialDriver(UsbDevice device) { mDevice = device; mPort = new CdcAcmSerialPort(device, 0); } @Override public UsbDevice getDevice() { return mDevice; } @Override public List getPorts() { return Collections.singletonList(mPort); } class CdcAcmSerialPort extends CommonUsbSerialPort { private final boolean mEnableAsyncReads; private UsbInterface mControlInterface; private UsbInterface mDataInterface; private UsbEndpoint mControlEndpoint; private UsbEndpoint mReadEndpoint; private UsbEndpoint mWriteEndpoint; private boolean mRts = false; private boolean mDtr = false; private static final int USB_RECIP_INTERFACE = 0x01; private static final int USB_RT_ACM = UsbConstants.USB_TYPE_CLASS | USB_RECIP_INTERFACE; private static final int SET_LINE_CODING = 0x20; // USB CDC 1.1 section 6.2 private static final int GET_LINE_CODING = 0x21; private static final int SET_CONTROL_LINE_STATE = 0x22; private static final int SEND_BREAK = 0x23; public CdcAcmSerialPort(UsbDevice device, int portNumber) { super(device, portNumber); mEnableAsyncReads = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1); } @Override public UsbSerialDriver getDriver() { return CdcAcmSerialDriver.this; } @Override public void open(UsbDeviceConnection connection) throws IOException { if (mConnection != null && false) { throw new IOException("Already open"); } mConnection = connection; boolean opened = false; try { Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount()); mControlInterface = mDevice.getInterface(0); Log.d(TAG, "Control iface=" + mControlInterface); // class should be USB_CLASS_COMM if (!mConnection.claimInterface(mControlInterface, true)) { throw new IOException("Could not claim control interface."); } mControlEndpoint = mControlInterface.getEndpoint(0); Log.d(TAG, "Control endpoint direction: " + mControlEndpoint.getDirection()); Log.d(TAG, "Claiming data interface."); mDataInterface = mDevice.getInterface(1); Log.d(TAG, "data iface=" + mDataInterface); // class should be USB_CLASS_CDC_DATA if (!mConnection.claimInterface(mDataInterface, true)) { throw new IOException("Could not claim data interface."); } mReadEndpoint = mDataInterface.getEndpoint(1); Log.d(TAG, "Read endpoint direction: " + mReadEndpoint.getDirection()); mWriteEndpoint = mDataInterface.getEndpoint(0); Log.d(TAG, "Write endpoint direction: " + mWriteEndpoint.getDirection()); if (mEnableAsyncReads) { Log.d(TAG, "Async reads enabled"); } else { Log.d(TAG, "Async reads disabled."); } opened = true; } finally { if (!opened) { mConnection = null; } } } private int sendAcmControlMessage(int request, int value, byte[] buf) { return mConnection.controlTransfer( USB_RT_ACM, request, value, 0, buf, buf != null ? buf.length : 0, 5000); } @Override public void close() throws IOException { if (mConnection == null) { throw new IOException("Already closed"); } mConnection.close(); mConnection = null; } @Override public int read(byte[] dest, int timeoutMillis, UsbDeviceConnection connection) throws IOException {return 0;} @Override public int read(byte[] dest, int timeoutMillis) throws IOException { if (mEnableAsyncReads) { final UsbRequest request = new UsbRequest(); try { request.initialize(mConnection, mReadEndpoint); final ByteBuffer buf = ByteBuffer.wrap(dest); if (!request.queue(buf, dest.length)) { throw new IOException("Error queueing request."); } final UsbRequest response = mConnection.requestWait(); if (response == null) { throw new IOException("Null response"); } final int nread = buf.position(); if (nread > 0) { //Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length))); return nread; } else { return 0; } } finally { request.close(); } } final int numBytesRead; synchronized (mReadBufferLock) { int readAmt = Math.min(dest.length, mReadBuffer.length); numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt, timeoutMillis); if (numBytesRead < 0) { // This sucks: we get -1 on timeout, not 0 as preferred. // We *should* use UsbRequest, except it has a bug/api oversight // where there is no way to determine the number of bytes read // in response :\ -- http://b.android.com/28023 if (timeoutMillis == Integer.MAX_VALUE) { // Hack: Special case "~infinite timeout" as an error. return -1; } return 0; } System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead); } return numBytesRead; } @Override public int write(byte[] src, int timeoutMillis) throws IOException { // TODO(mikey): Nearly identical to FtdiSerial write. Refactor. int offset = 0; while (offset < src.length) { final int writeLength; final int amtWritten; synchronized (mWriteBufferLock) { final byte[] writeBuffer; writeLength = Math.min(src.length - offset, mWriteBuffer.length); if (offset == 0) { writeBuffer = src; } else { // bulkTransfer does not support offsets, make a copy. System.arraycopy(src, offset, mWriteBuffer, 0, writeLength); writeBuffer = mWriteBuffer; } amtWritten = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, writeLength, timeoutMillis); } if (amtWritten <= 0) { throw new IOException("Error writing " + writeLength + " bytes at offset " + offset + " length=" + src.length); } Log.d(TAG, "Wrote amt=" + amtWritten + " attempted=" + writeLength); offset += amtWritten; } return offset; } @Override public void setParameters(int baudRate, int dataBits, int stopBits, int parity) { byte stopBitsByte; switch (stopBits) { case STOPBITS_1: stopBitsByte = 0; break; case STOPBITS_1_5: stopBitsByte = 1; break; case STOPBITS_2: stopBitsByte = 2; break; default: throw new IllegalArgumentException("Bad value for stopBits: " + stopBits); } byte parityBitesByte; switch (parity) { case PARITY_NONE: parityBitesByte = 0; break; case PARITY_ODD: parityBitesByte = 1; break; case PARITY_EVEN: parityBitesByte = 2; break; case PARITY_MARK: parityBitesByte = 3; break; case PARITY_SPACE: parityBitesByte = 4; break; default: throw new IllegalArgumentException("Bad value for parity: " + parity); } byte[] msg = { (byte) ( baudRate & 0xff), (byte) ((baudRate >> 8 ) & 0xff), (byte) ((baudRate >> 16) & 0xff), (byte) ((baudRate >> 24) & 0xff), stopBitsByte, parityBitesByte, (byte) dataBits}; sendAcmControlMessage(SET_LINE_CODING, 0, msg); } @Override public boolean getCD() throws IOException { return false; // TODO } @Override public boolean getCTS() throws IOException { return false; // TODO } @Override public boolean getDSR() throws IOException { return false; // TODO } @Override public boolean getDTR() throws IOException { return mDtr; } @Override public void setDTR(boolean value) throws IOException { mDtr = value; setDtrRts(); } @Override public boolean getRI() throws IOException { return false; // TODO } @Override public boolean getRTS() throws IOException { return mRts; } @Override public void setRTS(boolean value) throws IOException { mRts = value; setDtrRts(); } private void setDtrRts() { int value = (mRts ? 0x2 : 0) | (mDtr ? 0x1 : 0); sendAcmControlMessage(SET_CONTROL_LINE_STATE, value, null); } } public static Map getSupportedDevices() { final Map supportedDevices = new LinkedHashMap(); supportedDevices.put(Integer.valueOf(UsbId.VENDOR_ARDUINO), new int[] { UsbId.ARDUINO_UNO, UsbId.ARDUINO_UNO_R3, UsbId.ARDUINO_MEGA_2560, UsbId.ARDUINO_MEGA_2560_R3, UsbId.ARDUINO_SERIAL_ADAPTER, UsbId.ARDUINO_SERIAL_ADAPTER_R3, UsbId.ARDUINO_MEGA_ADK, UsbId.ARDUINO_MEGA_ADK_R3, UsbId.ARDUINO_LEONARDO, }); supportedDevices.put(Integer.valueOf(UsbId.VENDOR_VAN_OOIJEN_TECH), new int[] { UsbId.VAN_OOIJEN_TECH_TEENSYDUINO_SERIAL, }); supportedDevices.put(Integer.valueOf(UsbId.VENDOR_ATMEL), new int[] { UsbId.ATMEL_LUFA_CDC_DEMO_APP, }); supportedDevices.put(Integer.valueOf(UsbId.VENDOR_LEAFLABS), new int[] { UsbId.LEAFLABS_MAPLE, }); return supportedDevices; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/usbserial/driver/CommonUsbSerialPort.java ================================================ /* Copyright 2011-2013 Google Inc. * Copyright 2013 mike wakerly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Project home page: https://github.com/mik3y/usb-serial-for-android */ package com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import java.io.IOException; /** * A base class shared by several driver implementations. * * @author mike wakerly (opensource@hoho.com) */ abstract class CommonUsbSerialPort implements UsbSerialPort { public static final int DEFAULT_READ_BUFFER_SIZE = 16 * 1024; public static final int DEFAULT_WRITE_BUFFER_SIZE = 16 * 1024; protected final UsbDevice mDevice; protected final int mPortNumber; // non-null when open() protected UsbDeviceConnection mConnection = null; protected final Object mReadBufferLock = new Object(); protected final Object mWriteBufferLock = new Object(); /** Internal read buffer. Guarded by {@link #mReadBufferLock}. */ protected byte[] mReadBuffer; /** Internal write buffer. Guarded by {@link #mWriteBufferLock}. */ protected byte[] mWriteBuffer; public CommonUsbSerialPort(UsbDevice device, int portNumber) { mDevice = device; mPortNumber = portNumber; mReadBuffer = new byte[DEFAULT_READ_BUFFER_SIZE]; mWriteBuffer = new byte[DEFAULT_WRITE_BUFFER_SIZE]; } @Override public String toString() { return String.format("<%s device_name=%s device_id=%s port_number=%s>", getClass().getSimpleName(), mDevice.getDeviceName(), mDevice.getDeviceId(), mPortNumber); } /** * Returns the currently-bound USB device. * * @return the device */ public final UsbDevice getDevice() { return mDevice; } @Override public int getPortNumber() { return mPortNumber; } /** * Returns the device serial number * @return serial number */ @Override public String getSerial() { return mConnection.getSerial(); } /** * Sets the size of the internal buffer used to exchange data with the USB * stack for read operations. Most users should not need to change this. * * @param bufferSize the size in bytes */ public final void setReadBufferSize(int bufferSize) { synchronized (mReadBufferLock) { if (bufferSize == mReadBuffer.length) { return; } mReadBuffer = new byte[bufferSize]; } } /** * Sets the size of the internal buffer used to exchange data with the USB * stack for write operations. Most users should not need to change this. * * @param bufferSize the size in bytes */ public final void setWriteBufferSize(int bufferSize) { synchronized (mWriteBufferLock) { if (bufferSize == mWriteBuffer.length) { return; } mWriteBuffer = new byte[bufferSize]; } } @Override public abstract void open(UsbDeviceConnection connection) throws IOException; @Override public abstract void close() throws IOException; @Override public abstract int read(final byte[] dest, final int timeoutMillis) throws IOException; @Override public abstract int write(final byte[] src, final int timeoutMillis) throws IOException; @Override public abstract void setParameters( int baudRate, int dataBits, int stopBits, int parity) throws IOException; @Override public abstract boolean getCD() throws IOException; @Override public abstract boolean getCTS() throws IOException; @Override public abstract boolean getDSR() throws IOException; @Override public abstract boolean getDTR() throws IOException; @Override public abstract void setDTR(boolean value) throws IOException; @Override public abstract boolean getRI() throws IOException; @Override public abstract boolean getRTS() throws IOException; @Override public abstract void setRTS(boolean value) throws IOException; @Override public boolean purgeHwBuffers(boolean flushReadBuffers, boolean flushWriteBuffers) throws IOException { return !flushReadBuffers && !flushWriteBuffers; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/usbserial/driver/Cp21xxSerialDriver.java ================================================ /* Copyright 2011-2013 Google Inc. * Copyright 2013 mike wakerly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Project home page: https://github.com/mik3y/usb-serial-for-android */ package com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver; import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbRequest; import android.util.Log; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; public class Cp21xxSerialDriver implements UsbSerialDriver { private static final String TAG = Cp21xxSerialDriver.class.getSimpleName(); private final UsbDevice mDevice; private final UsbSerialPort mPort; public Cp21xxSerialDriver(UsbDevice device) { mDevice = device; mPort = new Cp21xxSerialPort(mDevice, 0); } @Override public UsbDevice getDevice() { return mDevice; } @Override public List getPorts() { return Collections.singletonList(mPort); } public class Cp21xxSerialPort extends CommonUsbSerialPort { private static final int DEFAULT_BAUD_RATE = 9600; private static final int USB_WRITE_TIMEOUT_MILLIS = 5000; /* * Configuration Request Types */ private static final int REQTYPE_HOST_TO_DEVICE = 0x41; /* * Configuration Request Codes */ private static final int SILABSER_IFC_ENABLE_REQUEST_CODE = 0x00; private static final int SILABSER_SET_BAUDDIV_REQUEST_CODE = 0x01; private static final int SILABSER_SET_LINE_CTL_REQUEST_CODE = 0x03; private static final int SILABSER_SET_MHS_REQUEST_CODE = 0x07; private static final int SILABSER_SET_BAUDRATE = 0x1E; private static final int SILABSER_FLUSH_REQUEST_CODE = 0x12; private static final int FLUSH_READ_CODE = 0x0a; private static final int FLUSH_WRITE_CODE = 0x05; /* * SILABSER_IFC_ENABLE_REQUEST_CODE */ private static final int UART_ENABLE = 0x0001; private static final int UART_DISABLE = 0x0000; /* * SILABSER_SET_BAUDDIV_REQUEST_CODE */ private static final int BAUD_RATE_GEN_FREQ = 0x384000; /* * SILABSER_SET_MHS_REQUEST_CODE */ private static final int MCR_DTR = 0x0001; private static final int MCR_RTS = 0x0002; private static final int MCR_ALL = 0x0003; private static final int CONTROL_WRITE_DTR = 0x0100; private static final int CONTROL_WRITE_RTS = 0x0200; private UsbEndpoint mReadEndpoint; private UsbEndpoint mWriteEndpoint; public Cp21xxSerialPort(UsbDevice device, int portNumber) { super(device, portNumber); } @Override public UsbSerialDriver getDriver() { return Cp21xxSerialDriver.this; } private int setConfigSingle(int request, int value) { return mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, request, value, 0, null, 0, USB_WRITE_TIMEOUT_MILLIS); } @Override public void open(UsbDeviceConnection connection) throws IOException { if (mConnection != null) { throw new IOException("Already opened."); } mConnection = connection; boolean opened = false; try { for (int i = 0; i < mDevice.getInterfaceCount(); i++) { UsbInterface usbIface = mDevice.getInterface(i); if (mConnection.claimInterface(usbIface, true)) { Log.d(TAG, "claimInterface " + i + " SUCCESS"); } else { Log.d(TAG, "claimInterface " + i + " FAIL"); } } UsbInterface dataIface = mDevice.getInterface(mDevice.getInterfaceCount() - 1); for (int i = 0; i < dataIface.getEndpointCount(); i++) { UsbEndpoint ep = dataIface.getEndpoint(i); if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) { if (ep.getDirection() == UsbConstants.USB_DIR_IN) { mReadEndpoint = ep; } else { mWriteEndpoint = ep; } } } setConfigSingle(SILABSER_IFC_ENABLE_REQUEST_CODE, UART_ENABLE); setConfigSingle(SILABSER_SET_MHS_REQUEST_CODE, MCR_ALL | CONTROL_WRITE_DTR | CONTROL_WRITE_RTS); setConfigSingle(SILABSER_SET_BAUDDIV_REQUEST_CODE, BAUD_RATE_GEN_FREQ / DEFAULT_BAUD_RATE); // setParameters(DEFAULT_BAUD_RATE, DEFAULT_DATA_BITS, DEFAULT_STOP_BITS, DEFAULT_PARITY); opened = true; } finally { if (!opened) { try { close(); } catch (IOException e) { // Ignore IOExceptions during close() } } } } @Override public void close() throws IOException { if (mConnection == null) { throw new IOException("Already closed"); } try { setConfigSingle(SILABSER_IFC_ENABLE_REQUEST_CODE, UART_DISABLE); mConnection.close(); } finally { mConnection = null; } } @Override public int read(byte[] dest, int timeoutMillis) throws IOException { final int numBytesRead; synchronized (mReadBufferLock) { int readAmt = Math.min(dest.length, mReadBuffer.length); numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt, timeoutMillis); if (numBytesRead < 0) { // This sucks: we get -1 on timeout, not 0 as preferred. // We *should* use UsbRequest, except it has a bug/api oversight // where there is no way to determine the number of bytes read // in response :\ -- http://b.android.com/28023 return 0; } System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead); } return numBytesRead; } @Override public int write(byte[] src, int timeoutMillis) throws IOException { int offset = 0; while (offset < src.length) { final int writeLength; final int amtWritten; synchronized (mWriteBufferLock) { final byte[] writeBuffer; writeLength = Math.min(src.length - offset, mWriteBuffer.length); if (offset == 0) { writeBuffer = src; } else { // bulkTransfer does not support offsets, make a copy. System.arraycopy(src, offset, mWriteBuffer, 0, writeLength); writeBuffer = mWriteBuffer; } amtWritten = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, writeLength, timeoutMillis); } if (amtWritten <= 0) { throw new IOException("Error writing " + writeLength + " bytes at offset " + offset + " length=" + src.length); } Log.d(TAG, "Wrote amt=" + amtWritten + " attempted=" + writeLength); offset += amtWritten; } return offset; } @Override public int read(byte[] dest, int timeoutMillis, UsbDeviceConnection connection) throws IOException { if (false) { final UsbRequest request = new UsbRequest(); try { request.initialize(connection, mReadEndpoint); final ByteBuffer buf = ByteBuffer.wrap(dest); if (!request.queue(buf, dest.length)) { throw new IOException("Error queueing request."); } final UsbRequest response = connection.requestWait(); if (response == null) { throw new IOException("Null response"); } final int nread = buf.position(); if (nread > 0) { //Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length))); return nread; } else { return 0; } } finally { request.close(); } } final int numBytesRead; synchronized (mReadBufferLock) { int readAmt = Math.min(dest.length, mReadBuffer.length); numBytesRead = connection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt, timeoutMillis); if (numBytesRead < 0) { // This sucks: we get -1 on timeout, not 0 as preferred. // We *should* use UsbRequest, except it has a bug/api oversight // where there is no way to determine the number of bytes read // in response :\ -- http://b.android.com/28023 if (timeoutMillis == Integer.MAX_VALUE) { // Hack: Special case "~infinite timeout" as an error. return -1; } return 0; } System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead); } return numBytesRead; } private void setBaudRate(int baudRate) throws IOException { byte[] data = new byte[] { (byte) ( baudRate & 0xff), (byte) ((baudRate >> 8 ) & 0xff), (byte) ((baudRate >> 16) & 0xff), (byte) ((baudRate >> 24) & 0xff) }; int ret = mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, SILABSER_SET_BAUDRATE, 0, 0, data, 4, USB_WRITE_TIMEOUT_MILLIS); if (ret < 0) { throw new IOException("Error setting baud rate."); } } @Override public void setParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException { setBaudRate(baudRate); int configDataBits = 0; switch (dataBits) { case DATABITS_5: configDataBits |= 0x0500; break; case DATABITS_6: configDataBits |= 0x0600; break; case DATABITS_7: configDataBits |= 0x0700; break; case DATABITS_8: configDataBits |= 0x0800; break; default: configDataBits |= 0x0800; break; } switch (parity) { case PARITY_ODD: configDataBits |= 0x0010; break; case PARITY_EVEN: configDataBits |= 0x0020; break; } switch (stopBits) { case STOPBITS_1: configDataBits |= 0; break; case STOPBITS_2: configDataBits |= 2; break; } setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configDataBits); } @Override public boolean getCD() throws IOException { return false; } @Override public boolean getCTS() throws IOException { return false; } @Override public boolean getDSR() throws IOException { return false; } @Override public boolean getDTR() throws IOException { return true; } @Override public void setDTR(boolean value) throws IOException { } @Override public boolean getRI() throws IOException { return false; } @Override public boolean getRTS() throws IOException { return true; } @Override public void setRTS(boolean value) throws IOException { } @Override public boolean purgeHwBuffers(boolean purgeReadBuffers, boolean purgeWriteBuffers) throws IOException { int value = (purgeReadBuffers ? FLUSH_READ_CODE : 0) | (purgeWriteBuffers ? FLUSH_WRITE_CODE : 0); if (value != 0) { setConfigSingle(SILABSER_FLUSH_REQUEST_CODE, value); } return true; } } public static Map getSupportedDevices() { final Map supportedDevices = new LinkedHashMap(); supportedDevices.put(Integer.valueOf(UsbId.VENDOR_SILABS), new int[] { UsbId.SILABS_CP2102, UsbId.SILABS_CP2105, UsbId.SILABS_CP2108, UsbId.SILABS_CP2110 }); return supportedDevices; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/usbserial/driver/FtdiSerialDriver.java ================================================ /* Copyright 2011-2013 Google Inc. * Copyright 2013 mike wakerly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Project home page: https://github.com/mik3y/usb-serial-for-android */ package com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver; import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbRequest; import android.util.Log; import com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.util.HexDump; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * A {@link CommonUsbSerialPort} implementation for a variety of FTDI devices *

* This driver is based on libftdi, and is * copyright and subject to the following terms: * *

 *   Copyright (C) 2003 by Intra2net AG
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License
 *   version 2.1 as published by the Free Software Foundation;
 *
 *   opensource@intra2net.com
 *   http://www.intra2net.com/en/developer/libftdi
 * 
* *

*

* Some FTDI devices have not been tested; see later listing of supported and * unsupported devices. Devices listed as "supported" support the following * features: *

    *
  • Read and write of serial data (see * {@link CommonUsbSerialPort#read(byte[], int)} and * {@link CommonUsbSerialPort#write(byte[], int)}.
  • *
  • Setting serial line parameters (see * {@link CommonUsbSerialPort#setParameters(int, int, int, int)}.
  • *
*

*

* Supported and tested devices: *

    *
  • {@value DeviceType#TYPE_R}
  • *
*

*

* Unsupported but possibly working devices (please contact the author with * feedback or patches): *

    *
  • {@value DeviceType#TYPE_2232C}
  • *
  • {@value DeviceType#TYPE_2232H}
  • *
  • {@value DeviceType#TYPE_4232H}
  • *
  • {@value DeviceType#TYPE_AM}
  • *
  • {@value DeviceType#TYPE_BM}
  • *
*

* * @author mike wakerly (opensource@hoho.com) * @see USB Serial * for Android project page * @see FTDI Homepage * @see libftdi */ public class FtdiSerialDriver implements UsbSerialDriver { private final UsbDevice mDevice; private final UsbSerialPort mPort; /** * FTDI chip types. */ private static enum DeviceType { TYPE_BM, TYPE_AM, TYPE_2232C, TYPE_R, TYPE_2232H, TYPE_4232H; } public FtdiSerialDriver(UsbDevice device) { mDevice = device; mPort = new FtdiSerialPort(mDevice, 0); } @Override public UsbDevice getDevice() { return mDevice; } @Override public List getPorts() { return Collections.singletonList(mPort); } private class FtdiSerialPort extends CommonUsbSerialPort { public static final int USB_TYPE_STANDARD = 0x00 << 5; public static final int USB_TYPE_CLASS = 0x00 << 5; public static final int USB_TYPE_VENDOR = 0x00 << 5; public static final int USB_TYPE_RESERVED = 0x00 << 5; public static final int USB_RECIP_DEVICE = 0x00; public static final int USB_RECIP_INTERFACE = 0x01; public static final int USB_RECIP_ENDPOINT = 0x02; public static final int USB_RECIP_OTHER = 0x03; public static final int USB_ENDPOINT_IN = 0x80; public static final int USB_ENDPOINT_OUT = 0x00; public static final int USB_WRITE_TIMEOUT_MILLIS = 5000; public static final int USB_READ_TIMEOUT_MILLIS = 5000; // From ftdi.h /** * Reset the port. */ private static final int SIO_RESET_REQUEST = 0; /** * Set the modem control register. */ private static final int SIO_MODEM_CTRL_REQUEST = 1; /** * Set flow control register. */ private static final int SIO_SET_FLOW_CTRL_REQUEST = 2; /** * Set baud rate. */ private static final int SIO_SET_BAUD_RATE_REQUEST = 3; /** * Set the data characteristics of the port. */ private static final int SIO_SET_DATA_REQUEST = 4; private static final int SIO_RESET_SIO = 0; private static final int SIO_RESET_PURGE_RX = 1; private static final int SIO_RESET_PURGE_TX = 2; public static final int FTDI_DEVICE_OUT_REQTYPE = UsbConstants.USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT; public static final int FTDI_DEVICE_IN_REQTYPE = UsbConstants.USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN; /** * Length of the modem status header, transmitted with every read. */ private static final int MODEM_STATUS_HEADER_LENGTH = 2; private final String TAG = FtdiSerialDriver.class.getSimpleName(); private DeviceType mType; private int mInterface = 0; /* INTERFACE_ANY */ private int mMaxPacketSize = 64; // TODO(mikey): detect /** * Due to http://b.android.com/28023 , we cannot use UsbRequest async reads * since it gives no indication of number of bytes read. Set this to * {@code true} on platforms where it is fixed. */ private static final boolean ENABLE_ASYNC_READS = false; public FtdiSerialPort(UsbDevice device, int portNumber) { super(device, portNumber); } @Override public UsbSerialDriver getDriver() { return FtdiSerialDriver.this; } /** * Filter FTDI status bytes from buffer * @param src The source buffer (which contains status bytes) * @param dest The destination buffer to write the status bytes into (can be src) * @param totalBytesRead Number of bytes read to src * @param maxPacketSize The USB endpoint max packet size * @return The number of payload bytes */ private final int filterStatusBytes(byte[] src, byte[] dest, int totalBytesRead, int maxPacketSize) { final int packetsCount = totalBytesRead / maxPacketSize + (totalBytesRead % maxPacketSize == 0 ? 0 : 1); for (int packetIdx = 0; packetIdx < packetsCount; ++packetIdx) { final int count = (packetIdx == (packetsCount - 1)) ? (totalBytesRead % maxPacketSize) - MODEM_STATUS_HEADER_LENGTH : maxPacketSize - MODEM_STATUS_HEADER_LENGTH; if (count > 0) { System.arraycopy(src, packetIdx * maxPacketSize + MODEM_STATUS_HEADER_LENGTH, dest, packetIdx * (maxPacketSize - MODEM_STATUS_HEADER_LENGTH), count); } } return totalBytesRead - (packetsCount * 2); } public int read(byte[] dest, int timeoutMillis, UsbDeviceConnection connection) throws IOException { if (false) { final UsbRequest request = new UsbRequest(); try { request.initialize(connection, null); final ByteBuffer buf = ByteBuffer.wrap(dest); if (!request.queue(buf, dest.length)) { throw new IOException("Error queueing request."); } final UsbRequest response = connection.requestWait(); if (response == null) { throw new IOException("Null response"); } final int nread = buf.position(); if (nread > 0) { //Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length))); return nread; } else { return 0; } } finally { request.close(); } } final int numBytesRead; synchronized (mReadBufferLock) { int readAmt = Math.min(dest.length, mReadBuffer.length); numBytesRead = connection.bulkTransfer(null, mReadBuffer, readAmt, timeoutMillis); if (numBytesRead < 0) { // This sucks: we get -1 on timeout, not 0 as preferred. // We *should* use UsbRequest, except it has a bug/api oversight // where there is no way to determine the number of bytes read // in response :\ -- http://b.android.com/28023 if (timeoutMillis == Integer.MAX_VALUE) { // Hack: Special case "~infinite timeout" as an error. return -1; } return 0; } System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead); } return numBytesRead; } public void reset() throws IOException { int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST, SIO_RESET_SIO, 0 /* index */, null, 0, USB_WRITE_TIMEOUT_MILLIS); if (result != 0) { throw new IOException("Reset failed: result=" + result); } // TODO(mikey): autodetect. mType = DeviceType.TYPE_R; } @Override public void open(UsbDeviceConnection connection) throws IOException { if (mConnection != null) { throw new IOException("Already open"); } mConnection = connection; boolean opened = false; try { for (int i = 0; i < mDevice.getInterfaceCount(); i++) { if (connection.claimInterface(mDevice.getInterface(i), true)) { Log.d(TAG, "claimInterface " + i + " SUCCESS"); } else { throw new IOException("Error claiming interface " + i); } } reset(); opened = true; } finally { if (!opened) { close(); mConnection = null; } } } @Override public void close() throws IOException { if (mConnection == null) { throw new IOException("Already closed"); } try { mConnection.close(); } finally { mConnection = null; } } @Override public int read(byte[] dest, int timeoutMillis) throws IOException { final UsbEndpoint endpoint = mDevice.getInterface(0).getEndpoint(0); if (ENABLE_ASYNC_READS) { final int readAmt; synchronized (mReadBufferLock) { // mReadBuffer is only used for maximum read size. readAmt = Math.min(dest.length, mReadBuffer.length); } final UsbRequest request = new UsbRequest(); request.initialize(mConnection, endpoint); final ByteBuffer buf = ByteBuffer.wrap(dest); if (!request.queue(buf, readAmt)) { throw new IOException("Error queueing request."); } final UsbRequest response = mConnection.requestWait(); if (response == null) { throw new IOException("Null response"); } final int payloadBytesRead = buf.position() - MODEM_STATUS_HEADER_LENGTH; if (payloadBytesRead > 0) { Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length))); return payloadBytesRead; } else { return 0; } } else { final int totalBytesRead; synchronized (mReadBufferLock) { final int readAmt = Math.min(dest.length, mReadBuffer.length); totalBytesRead = mConnection.bulkTransfer(endpoint, mReadBuffer, readAmt, timeoutMillis); if (totalBytesRead < MODEM_STATUS_HEADER_LENGTH) { throw new IOException("Expected at least " + MODEM_STATUS_HEADER_LENGTH + " bytes"); } return filterStatusBytes(mReadBuffer, dest, totalBytesRead, endpoint.getMaxPacketSize()); } } } @Override public int write(byte[] src, int timeoutMillis) throws IOException { final UsbEndpoint endpoint = mDevice.getInterface(0).getEndpoint(1); int offset = 0; while (offset < src.length) { final int writeLength; final int amtWritten; synchronized (mWriteBufferLock) { final byte[] writeBuffer; writeLength = Math.min(src.length - offset, mWriteBuffer.length); if (offset == 0) { writeBuffer = src; } else { // bulkTransfer does not support offsets, make a copy. System.arraycopy(src, offset, mWriteBuffer, 0, writeLength); writeBuffer = mWriteBuffer; } amtWritten = mConnection.bulkTransfer(endpoint, writeBuffer, writeLength, timeoutMillis); } if (amtWritten <= 0) { throw new IOException("Error writing " + writeLength + " bytes at offset " + offset + " length=" + src.length); } Log.d(TAG, "Wrote amtWritten=" + amtWritten + " attempted=" + writeLength); offset += amtWritten; } return offset; } private int setBaudRate(int baudRate) throws IOException { long[] vals = convertBaudrate(baudRate); long actualBaudrate = vals[0]; long index = vals[1]; long value = vals[2]; int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_SET_BAUD_RATE_REQUEST, (int) value, (int) index, null, 0, USB_WRITE_TIMEOUT_MILLIS); if (result != 0) { throw new IOException("Setting baudrate failed: result=" + result); } return (int) actualBaudrate; } @Override public void setParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException { setBaudRate(baudRate); int config = dataBits; switch (parity) { case PARITY_NONE: config |= (0x00 << 8); break; case PARITY_ODD: config |= (0x01 << 8); break; case PARITY_EVEN: config |= (0x02 << 8); break; case PARITY_MARK: config |= (0x03 << 8); break; case PARITY_SPACE: config |= (0x04 << 8); break; default: throw new IllegalArgumentException("Unknown parity value: " + parity); } switch (stopBits) { case STOPBITS_1: config |= (0x00 << 11); break; case STOPBITS_1_5: config |= (0x01 << 11); break; case STOPBITS_2: config |= (0x02 << 11); break; default: throw new IllegalArgumentException("Unknown stopBits value: " + stopBits); } int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_SET_DATA_REQUEST, config, 0 /* index */, null, 0, USB_WRITE_TIMEOUT_MILLIS); if (result != 0) { throw new IOException("Setting parameters failed: result=" + result); } } private long[] convertBaudrate(int baudrate) { // TODO(mikey): Braindead transcription of libfti method. Clean up, // using more idiomatic Java where possible. int divisor = 24000000 / baudrate; int bestDivisor = 0; int bestBaud = 0; int bestBaudDiff = 0; int fracCode[] = { 0, 3, 2, 4, 1, 5, 6, 7 }; for (int i = 0; i < 2; i++) { int tryDivisor = divisor + i; int baudEstimate; int baudDiff; if (tryDivisor <= 8) { // Round up to minimum supported divisor tryDivisor = 8; } else if (mType != DeviceType.TYPE_AM && tryDivisor < 12) { // BM doesn't support divisors 9 through 11 inclusive tryDivisor = 12; } else if (divisor < 16) { // AM doesn't support divisors 9 through 15 inclusive tryDivisor = 16; } else { if (mType == DeviceType.TYPE_AM) { // TODO } else { if (tryDivisor > 0x1FFFF) { // Round down to maximum supported divisor value (for // BM) tryDivisor = 0x1FFFF; } } } // Get estimated baud rate (to nearest integer) baudEstimate = (24000000 + (tryDivisor / 2)) / tryDivisor; // Get absolute difference from requested baud rate if (baudEstimate < baudrate) { baudDiff = baudrate - baudEstimate; } else { baudDiff = baudEstimate - baudrate; } if (i == 0 || baudDiff < bestBaudDiff) { // Closest to requested baud rate so far bestDivisor = tryDivisor; bestBaud = baudEstimate; bestBaudDiff = baudDiff; if (baudDiff == 0) { // Spot on! No point trying break; } } } // Encode the best divisor value long encodedDivisor = (bestDivisor >> 3) | (fracCode[bestDivisor & 7] << 14); // Deal with special cases for encoded value if (encodedDivisor == 1) { encodedDivisor = 0; // 3000000 baud } else if (encodedDivisor == 0x4001) { encodedDivisor = 1; // 2000000 baud (BM only) } // Split into "value" and "index" values long value = encodedDivisor & 0xFFFF; long index; if (mType == DeviceType.TYPE_2232C || mType == DeviceType.TYPE_2232H || mType == DeviceType.TYPE_4232H) { index = (encodedDivisor >> 8) & 0xffff; index &= 0xFF00; index |= 0 /* TODO mIndex */; } else { index = (encodedDivisor >> 16) & 0xffff; } // Return the nearest baud rate return new long[] { bestBaud, index, value }; } @Override public boolean getCD() throws IOException { return false; } @Override public boolean getCTS() throws IOException { return false; } @Override public boolean getDSR() throws IOException { return false; } @Override public boolean getDTR() throws IOException { return false; } @Override public void setDTR(boolean value) throws IOException { } @Override public boolean getRI() throws IOException { return false; } @Override public boolean getRTS() throws IOException { return false; } @Override public void setRTS(boolean value) throws IOException { } @Override public boolean purgeHwBuffers(boolean purgeReadBuffers, boolean purgeWriteBuffers) throws IOException { if (purgeReadBuffers) { int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST, SIO_RESET_PURGE_RX, 0 /* index */, null, 0, USB_WRITE_TIMEOUT_MILLIS); if (result != 0) { throw new IOException("Flushing RX failed: result=" + result); } } if (purgeWriteBuffers) { int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST, SIO_RESET_PURGE_TX, 0 /* index */, null, 0, USB_WRITE_TIMEOUT_MILLIS); if (result != 0) { throw new IOException("Flushing RX failed: result=" + result); } } return true; } } public static Map getSupportedDevices() { final Map supportedDevices = new LinkedHashMap(); supportedDevices.put(Integer.valueOf(UsbId.VENDOR_FTDI), new int[] { UsbId.FTDI_FT232R, UsbId.FTDI_FT231X, }); return supportedDevices; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/usbserial/driver/ProbeTable.java ================================================ /* Copyright 2011-2013 Google Inc. * Copyright 2013 mike wakerly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Project home page: https://github.com/mik3y/usb-serial-for-android */ package com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver; import android.util.Pair; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.LinkedHashMap; import java.util.Map; /** * Maps (vendor id, product id) pairs to the corresponding serial driver. * * @author mike wakerly (opensource@hoho.com) */ public class ProbeTable { private final Map, Class> mProbeTable = new LinkedHashMap, Class>(); /** * Adds or updates a (vendor, product) pair in the table. * * @param vendorId the USB vendor id * @param productId the USB product id * @param driverClass the driver class responsible for this pair * @return {@code this}, for chaining */ public ProbeTable addProduct(int vendorId, int productId, Class driverClass) { mProbeTable.put(Pair.create(vendorId, productId), driverClass); return this; } /** * Internal method to add all supported products from * {@code getSupportedProducts} static method. * * @param driverClass * @return */ @SuppressWarnings("unchecked") ProbeTable addDriver(Class driverClass) { final Method method; try { method = driverClass.getMethod("getSupportedDevices"); } catch (SecurityException e) { throw new RuntimeException(e); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } final Map devices; try { devices = (Map) method.invoke(null); } catch (IllegalArgumentException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } for (Map.Entry entry : devices.entrySet()) { final int vendorId = entry.getKey().intValue(); for (int productId : entry.getValue()) { addProduct(vendorId, productId, driverClass); } } return this; } /** * Returns the driver for the given (vendor, product) pair, or {@code null} * if no match. * * @param vendorId the USB vendor id * @param productId the USB product id * @return the driver class matching this pair, or {@code null} */ public Class findDriver(int vendorId, int productId) { final Pair pair = Pair.create(vendorId, productId); return mProbeTable.get(pair); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/usbserial/driver/ProlificSerialDriver.java ================================================ /* This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Project home page: https://github.com/mik3y/usb-serial-for-android */ /* * Ported to usb-serial-for-android * by Felix Hädicke * * Based on the pyprolific driver written * by Emmanuel Blot * See https://github.com/eblot/pyftdi */ package com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver; import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbRequest; import android.util.Log; import java.io.IOException; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; public class ProlificSerialDriver implements UsbSerialDriver { private final String TAG = ProlificSerialDriver.class.getSimpleName(); private final UsbDevice mDevice; private final UsbSerialPort mPort; public ProlificSerialDriver(UsbDevice device) { mDevice = device; mPort = new ProlificSerialPort(mDevice, 0); } @Override public List getPorts() { return Collections.singletonList(mPort); } @Override public UsbDevice getDevice() { return mDevice; } class ProlificSerialPort extends CommonUsbSerialPort { private static final int USB_READ_TIMEOUT_MILLIS = 1000; private static final int USB_WRITE_TIMEOUT_MILLIS = 5000; private static final int USB_RECIP_INTERFACE = 0x01; private static final int PROLIFIC_VENDOR_READ_REQUEST = 0x01; private static final int PROLIFIC_VENDOR_WRITE_REQUEST = 0x01; private static final int PROLIFIC_VENDOR_OUT_REQTYPE = UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_VENDOR; private static final int PROLIFIC_VENDOR_IN_REQTYPE = UsbConstants.USB_DIR_IN | UsbConstants.USB_TYPE_VENDOR; private static final int PROLIFIC_CTRL_OUT_REQTYPE = UsbConstants.USB_DIR_OUT | UsbConstants.USB_TYPE_CLASS | USB_RECIP_INTERFACE; private static final int WRITE_ENDPOINT = 0x02; private static final int READ_ENDPOINT = 0x83; private static final int INTERRUPT_ENDPOINT = 0x81; private static final int FLUSH_RX_REQUEST = 0x08; private static final int FLUSH_TX_REQUEST = 0x09; private static final int SET_LINE_REQUEST = 0x20; private static final int SET_CONTROL_REQUEST = 0x22; private static final int CONTROL_DTR = 0x01; private static final int CONTROL_RTS = 0x02; private static final int STATUS_FLAG_CD = 0x01; private static final int STATUS_FLAG_DSR = 0x02; private static final int STATUS_FLAG_RI = 0x08; private static final int STATUS_FLAG_CTS = 0x80; private static final int STATUS_BUFFER_SIZE = 10; private static final int STATUS_BYTE_IDX = 8; private static final int DEVICE_TYPE_HX = 0; private static final int DEVICE_TYPE_0 = 1; private static final int DEVICE_TYPE_1 = 2; private int mDeviceType = DEVICE_TYPE_HX; private UsbEndpoint mReadEndpoint; private UsbEndpoint mWriteEndpoint; private UsbEndpoint mInterruptEndpoint; private int mControlLinesValue = 0; private int mBaudRate = -1, mDataBits = -1, mStopBits = -1, mParity = -1; private int mStatus = 0; private volatile Thread mReadStatusThread = null; private final Object mReadStatusThreadLock = new Object(); boolean mStopReadStatusThread = false; private IOException mReadStatusException = null; public ProlificSerialPort(UsbDevice device, int portNumber) { super(device, portNumber); } @Override public UsbSerialDriver getDriver() { return ProlificSerialDriver.this; } private final byte[] inControlTransfer(int requestType, int request, int value, int index, int length) throws IOException { byte[] buffer = new byte[length]; int result = mConnection.controlTransfer(requestType, request, value, index, buffer, length, USB_READ_TIMEOUT_MILLIS); if (result != length) { throw new IOException( String.format("ControlTransfer with value 0x%x failed: %d", value, result)); } return buffer; } private final void outControlTransfer(int requestType, int request, int value, int index, byte[] data) throws IOException { int length = (data == null) ? 0 : data.length; int result = mConnection.controlTransfer(requestType, request, value, index, data, length, USB_WRITE_TIMEOUT_MILLIS); if (result != length) { throw new IOException( String.format("ControlTransfer with value 0x%x failed: %d", value, result)); } } public int read(byte[] dest, int timeoutMillis, UsbDeviceConnection connection) throws IOException { if (false) { final UsbRequest request = new UsbRequest(); try { request.initialize(connection, mReadEndpoint); final ByteBuffer buf = ByteBuffer.wrap(dest); if (!request.queue(buf, dest.length)) { throw new IOException("Error queueing request."); } final UsbRequest response = connection.requestWait(); if (response == null) { throw new IOException("Null response"); } final int nread = buf.position(); if (nread > 0) { //Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length))); return nread; } else { return 0; } } finally { request.close(); } } final int numBytesRead; synchronized (mReadBufferLock) { int readAmt = Math.min(dest.length, mReadBuffer.length); numBytesRead = connection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt, timeoutMillis); if (numBytesRead < 0) { // This sucks: we get -1 on timeout, not 0 as preferred. // We *should* use UsbRequest, except it has a bug/api oversight // where there is no way to determine the number of bytes read // in response :\ -- http://b.android.com/28023 if (timeoutMillis == Integer.MAX_VALUE) { // Hack: Special case "~infinite timeout" as an error. return -1; } return 0; } System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead); } return numBytesRead; } private final byte[] vendorIn(int value, int index, int length) throws IOException { return inControlTransfer(PROLIFIC_VENDOR_IN_REQTYPE, PROLIFIC_VENDOR_READ_REQUEST, value, index, length); } private final void vendorOut(int value, int index, byte[] data) throws IOException { outControlTransfer(PROLIFIC_VENDOR_OUT_REQTYPE, PROLIFIC_VENDOR_WRITE_REQUEST, value, index, data); } private void resetDevice() throws IOException { purgeHwBuffers(true, true); } private final void ctrlOut(int request, int value, int index, byte[] data) throws IOException { outControlTransfer(PROLIFIC_CTRL_OUT_REQTYPE, request, value, index, data); } private void doBlackMagic() throws IOException { vendorIn(0x8484, 0, 1); vendorOut(0x0404, 0, null); vendorIn(0x8484, 0, 1); vendorIn(0x8383, 0, 1); vendorIn(0x8484, 0, 1); vendorOut(0x0404, 1, null); vendorIn(0x8484, 0, 1); vendorIn(0x8383, 0, 1); vendorOut(0, 1, null); vendorOut(1, 0, null); vendorOut(2, (mDeviceType == DEVICE_TYPE_HX) ? 0x44 : 0x24, null); } private void setControlLines(int newControlLinesValue) throws IOException { ctrlOut(SET_CONTROL_REQUEST, newControlLinesValue, 0, null); mControlLinesValue = newControlLinesValue; } private final void readStatusThreadFunction() { try { while (!mStopReadStatusThread) { byte[] buffer = new byte[STATUS_BUFFER_SIZE]; int readBytesCount = mConnection.bulkTransfer(mInterruptEndpoint, buffer, STATUS_BUFFER_SIZE, 500); if (readBytesCount > 0) { if (readBytesCount == STATUS_BUFFER_SIZE) { mStatus = buffer[STATUS_BYTE_IDX] & 0xff; } else { throw new IOException( String.format("Invalid CTS / DSR / CD / RI status buffer received, expected %d bytes, but received %d", STATUS_BUFFER_SIZE, readBytesCount)); } } } } catch (IOException e) { mReadStatusException = e; } } private final int getStatus() throws IOException { if ((mReadStatusThread == null) && (mReadStatusException == null)) { synchronized (mReadStatusThreadLock) { if (mReadStatusThread == null) { byte[] buffer = new byte[STATUS_BUFFER_SIZE]; int readBytes = mConnection.bulkTransfer(mInterruptEndpoint, buffer, STATUS_BUFFER_SIZE, 100); if (readBytes != STATUS_BUFFER_SIZE) { Log.w(TAG, "Could not read initial CTS / DSR / CD / RI status"); } else { mStatus = buffer[STATUS_BYTE_IDX] & 0xff; } mReadStatusThread = new Thread(new Runnable() { @Override public void run() { readStatusThreadFunction(); } }); mReadStatusThread.setDaemon(true); mReadStatusThread.start(); } } } /* throw and clear an exception which occured in the status read thread */ IOException readStatusException = mReadStatusException; if (mReadStatusException != null) { mReadStatusException = null; throw readStatusException; } return mStatus; } private final boolean testStatusFlag(int flag) throws IOException { return ((getStatus() & flag) == flag); } @Override public void open(UsbDeviceConnection connection) throws IOException { if (mConnection != null) { throw new IOException("Already open"); } UsbInterface usbInterface = mDevice.getInterface(0); if (!connection.claimInterface(usbInterface, true)) { throw new IOException("Error claiming Prolific interface 0"); } mConnection = connection; boolean opened = false; try { for (int i = 0; i < usbInterface.getEndpointCount(); ++i) { UsbEndpoint currentEndpoint = usbInterface.getEndpoint(i); switch (currentEndpoint.getAddress()) { case READ_ENDPOINT: mReadEndpoint = currentEndpoint; break; case WRITE_ENDPOINT: mWriteEndpoint = currentEndpoint; break; case INTERRUPT_ENDPOINT: mInterruptEndpoint = currentEndpoint; break; } } if (mDevice.getDeviceClass() == 0x02) { mDeviceType = DEVICE_TYPE_0; } else { try { Method getRawDescriptorsMethod = mConnection.getClass().getMethod("getRawDescriptors"); byte[] rawDescriptors = (byte[]) getRawDescriptorsMethod.invoke(mConnection); byte maxPacketSize0 = rawDescriptors[7]; if (maxPacketSize0 == 64) { mDeviceType = DEVICE_TYPE_HX; } else if ((mDevice.getDeviceClass() == 0x00) || (mDevice.getDeviceClass() == 0xff)) { mDeviceType = DEVICE_TYPE_1; } else { Log.w(TAG, "Could not detect PL2303 subtype, " + "Assuming that it is a HX device"); mDeviceType = DEVICE_TYPE_HX; } } catch (NoSuchMethodException e) { Log.w(TAG, "Method UsbDeviceConnection.getRawDescriptors, " + "required for PL2303 subtype detection, not " + "available! Assuming that it is a HX device"); mDeviceType = DEVICE_TYPE_HX; } catch (Exception e) { Log.e(TAG, "An unexpected exception occured while trying " + "to detect PL2303 subtype", e); } } setControlLines(mControlLinesValue); resetDevice(); doBlackMagic(); opened = true; } finally { if (!opened) { mConnection = null; connection.releaseInterface(usbInterface); } } } @Override public void close() throws IOException { if (mConnection == null) { throw new IOException("Already closed"); } try { mStopReadStatusThread = true; synchronized (mReadStatusThreadLock) { if (mReadStatusThread != null) { try { mReadStatusThread.join(); } catch (Exception e) { Log.w(TAG, "An error occured while waiting for status read thread", e); } } } resetDevice(); } finally { try { mConnection.releaseInterface(mDevice.getInterface(0)); } finally { mConnection = null; } } } @Override public int read(byte[] dest, int timeoutMillis) throws IOException { synchronized (mReadBufferLock) { int readAmt = Math.min(dest.length, mReadBuffer.length); int numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt, timeoutMillis); if (numBytesRead < 0) { return 0; } System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead); return numBytesRead; } } @Override public int write(byte[] src, int timeoutMillis) throws IOException { int offset = 0; while (offset < src.length) { final int writeLength; final int amtWritten; synchronized (mWriteBufferLock) { final byte[] writeBuffer; writeLength = Math.min(src.length - offset, mWriteBuffer.length); if (offset == 0) { writeBuffer = src; } else { // bulkTransfer does not support offsets, make a copy. System.arraycopy(src, offset, mWriteBuffer, 0, writeLength); writeBuffer = mWriteBuffer; } amtWritten = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, writeLength, timeoutMillis); } if (amtWritten <= 0) { throw new IOException("Error writing " + writeLength + " bytes at offset " + offset + " length=" + src.length); } offset += amtWritten; } return offset; } @Override public void setParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException { if ((mBaudRate == baudRate) && (mDataBits == dataBits) && (mStopBits == stopBits) && (mParity == parity)) { // Make sure no action is performed if there is nothing to change return; } byte[] lineRequestData = new byte[7]; lineRequestData[0] = (byte) (baudRate & 0xff); lineRequestData[1] = (byte) ((baudRate >> 8) & 0xff); lineRequestData[2] = (byte) ((baudRate >> 16) & 0xff); lineRequestData[3] = (byte) ((baudRate >> 24) & 0xff); switch (stopBits) { case STOPBITS_1: lineRequestData[4] = 0; break; case STOPBITS_1_5: lineRequestData[4] = 1; break; case STOPBITS_2: lineRequestData[4] = 2; break; default: throw new IllegalArgumentException("Unknown stopBits value: " + stopBits); } switch (parity) { case PARITY_NONE: lineRequestData[5] = 0; break; case PARITY_ODD: lineRequestData[5] = 1; break; case PARITY_MARK: lineRequestData[5] = 3; break; case PARITY_SPACE: lineRequestData[5] = 4; break; default: throw new IllegalArgumentException("Unknown parity value: " + parity); } lineRequestData[6] = (byte) dataBits; ctrlOut(SET_LINE_REQUEST, 0, 0, lineRequestData); resetDevice(); mBaudRate = baudRate; mDataBits = dataBits; mStopBits = stopBits; mParity = parity; } @Override public boolean getCD() throws IOException { return testStatusFlag(STATUS_FLAG_CD); } @Override public boolean getCTS() throws IOException { return testStatusFlag(STATUS_FLAG_CTS); } @Override public boolean getDSR() throws IOException { return testStatusFlag(STATUS_FLAG_DSR); } @Override public boolean getDTR() throws IOException { return ((mControlLinesValue & CONTROL_DTR) == CONTROL_DTR); } @Override public void setDTR(boolean value) throws IOException { int newControlLinesValue; if (value) { newControlLinesValue = mControlLinesValue | CONTROL_DTR; } else { newControlLinesValue = mControlLinesValue & ~CONTROL_DTR; } setControlLines(newControlLinesValue); } @Override public boolean getRI() throws IOException { return testStatusFlag(STATUS_FLAG_RI); } @Override public boolean getRTS() throws IOException { return ((mControlLinesValue & CONTROL_RTS) == CONTROL_RTS); } @Override public void setRTS(boolean value) throws IOException { int newControlLinesValue; if (value) { newControlLinesValue = mControlLinesValue | CONTROL_RTS; } else { newControlLinesValue = mControlLinesValue & ~CONTROL_RTS; } setControlLines(newControlLinesValue); } @Override public boolean purgeHwBuffers(boolean purgeReadBuffers, boolean purgeWriteBuffers) throws IOException { if (purgeReadBuffers) { vendorOut(FLUSH_RX_REQUEST, 0, null); } if (purgeWriteBuffers) { vendorOut(FLUSH_TX_REQUEST, 0, null); } return purgeReadBuffers || purgeWriteBuffers; } } public static Map getSupportedDevices() { final Map supportedDevices = new LinkedHashMap(); supportedDevices.put(Integer.valueOf(UsbId.VENDOR_PROLIFIC), new int[] { UsbId.PROLIFIC_PL2303, }); return supportedDevices; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/usbserial/driver/UsbId.java ================================================ /* Copyright 2011-2013 Google Inc. * Copyright 2013 mike wakerly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Project home page: https://github.com/mik3y/usb-serial-for-android */ package com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver; /** * Registry of USB vendor/product ID constants. * * Culled from various sources; see * usb.ids for one listing. * * @author mike wakerly (opensource@hoho.com) */ public final class UsbId { public static final int VENDOR_FTDI = 0x0403; public static final int FTDI_FT232R = 0x6001; public static final int FTDI_FT231X = 0x6015; public static final int VENDOR_ATMEL = 0x03EB; public static final int ATMEL_LUFA_CDC_DEMO_APP = 0x2044; public static final int VENDOR_ARDUINO = 0x2341; public static final int ARDUINO_UNO = 0x0001; public static final int ARDUINO_MEGA_2560 = 0x0010; public static final int ARDUINO_SERIAL_ADAPTER = 0x003b; public static final int ARDUINO_MEGA_ADK = 0x003f; public static final int ARDUINO_MEGA_2560_R3 = 0x0042; public static final int ARDUINO_UNO_R3 = 0x0043; public static final int ARDUINO_MEGA_ADK_R3 = 0x0044; public static final int ARDUINO_SERIAL_ADAPTER_R3 = 0x0044; public static final int ARDUINO_LEONARDO = 0x8036; public static final int VENDOR_VAN_OOIJEN_TECH = 0x16c0; public static final int VAN_OOIJEN_TECH_TEENSYDUINO_SERIAL = 0x0483; public static final int VENDOR_LEAFLABS = 0x1eaf; public static final int LEAFLABS_MAPLE = 0x0004; public static final int VENDOR_SILABS = 0x10c4; public static final int SILABS_CP2102 = 0xea60; public static final int SILABS_CP2105 = 0xea70; public static final int SILABS_CP2108 = 0xea71; public static final int SILABS_CP2110 = 0xea80; public static final int VENDOR_PROLIFIC = 0x067b; public static final int PROLIFIC_PL2303 = 0x2303; private UsbId() { throw new IllegalAccessError("Non-instantiable class."); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/usbserial/driver/UsbSerialDriver.java ================================================ /* Copyright 2011-2013 Google Inc. * Copyright 2013 mike wakerly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Project home page: https://github.com/mik3y/usb-serial-for-android */ package com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver; import android.hardware.usb.UsbDevice; import java.util.List; /** * * @author mike wakerly (opensource@hoho.com) */ public interface UsbSerialDriver { /** * Returns the raw {@link UsbDevice} backing this port. * * @return the device */ public UsbDevice getDevice(); /** * Returns all available ports for this device. This list must have at least * one entry. * * @return the ports */ public List getPorts(); } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/usbserial/driver/UsbSerialPort.java ================================================ /* Copyright 2011-2013 Google Inc. * Copyright 2013 mike wakerly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Project home page: https://github.com/mik3y/usb-serial-for-android */ package com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbManager; import java.io.IOException; /** * Interface for a single serial port. * * @author mike wakerly (opensource@hoho.com) */ public interface UsbSerialPort { /** 5 data bits. */ public static final int DATABITS_5 = 5; /** 6 data bits. */ public static final int DATABITS_6 = 6; /** 7 data bits. */ public static final int DATABITS_7 = 7; /** 8 data bits. */ public static final int DATABITS_8 = 8; /** No flow control. */ public static final int FLOWCONTROL_NONE = 0; /** RTS/CTS input flow control. */ public static final int FLOWCONTROL_RTSCTS_IN = 1; /** RTS/CTS output flow control. */ public static final int FLOWCONTROL_RTSCTS_OUT = 2; /** XON/XOFF input flow control. */ public static final int FLOWCONTROL_XONXOFF_IN = 4; /** XON/XOFF output flow control. */ public static final int FLOWCONTROL_XONXOFF_OUT = 8; /** No parity. */ public static final int PARITY_NONE = 0; /** Odd parity. */ public static final int PARITY_ODD = 1; /** Even parity. */ public static final int PARITY_EVEN = 2; /** Mark parity. */ public static final int PARITY_MARK = 3; /** Space parity. */ public static final int PARITY_SPACE = 4; /** 1 stop bit. */ public static final int STOPBITS_1 = 1; /** 1.5 stop bits. */ public static final int STOPBITS_1_5 = 3; /** 2 stop bits. */ public static final int STOPBITS_2 = 2; public UsbSerialDriver getDriver(); /** * Port number within driver. */ public int getPortNumber(); /** * The serial number of the underlying UsbDeviceConnection, or {@code null}. */ public String getSerial(); /** * Opens and initializes the port. Upon success, caller must ensure that * {@link #close()} is eventually called. * * @param connection an open device connection, acquired with * {@link UsbManager#openDevice(android.hardware.usb.UsbDevice)} * @throws IOException on error opening or initializing the port. */ public void open(UsbDeviceConnection connection) throws IOException; /** * Closes the port. * * @throws IOException on error closing the port. */ public void close() throws IOException; /** * Reads as many bytes as possible into the destination buffer. * * @param dest the destination byte buffer * @param timeoutMillis the timeout for reading * @return the actual number of bytes read * @throws IOException if an error occurred during reading */ public int read(final byte[] dest, final int timeoutMillis) throws IOException; public int read(final byte[] dest, final int timeoutMillis, final UsbDeviceConnection connection) throws IOException; /** * Writes as many bytes as possible from the source buffer. * * @param src the source byte buffer * @param timeoutMillis the timeout for writing * @return the actual number of bytes written * @throws IOException if an error occurred during writing */ public int write(final byte[] src, final int timeoutMillis) throws IOException; /** * Sets various serial port parameters. * * @param baudRate baud rate as an integer, for example {@code 115200}. * @param dataBits one of {@link #DATABITS_5}, {@link #DATABITS_6}, * {@link #DATABITS_7}, or {@link #DATABITS_8}. * @param stopBits one of {@link #STOPBITS_1}, {@link #STOPBITS_1_5}, or * {@link #STOPBITS_2}. * @param parity one of {@link #PARITY_NONE}, {@link #PARITY_ODD}, * {@link #PARITY_EVEN}, {@link #PARITY_MARK}, or * {@link #PARITY_SPACE}. * @throws IOException on error setting the port parameters */ public void setParameters( int baudRate, int dataBits, int stopBits, int parity) throws IOException; /** * Gets the CD (Carrier Detect) bit from the underlying UART. * * @return the current state, or {@code false} if not supported. * @throws IOException if an error occurred during reading */ public boolean getCD() throws IOException; /** * Gets the CTS (Clear To Send) bit from the underlying UART. * * @return the current state, or {@code false} if not supported. * @throws IOException if an error occurred during reading */ public boolean getCTS() throws IOException; /** * Gets the DSR (Data Set Ready) bit from the underlying UART. * * @return the current state, or {@code false} if not supported. * @throws IOException if an error occurred during reading */ public boolean getDSR() throws IOException; /** * Gets the DTR (Data Terminal Ready) bit from the underlying UART. * * @return the current state, or {@code false} if not supported. * @throws IOException if an error occurred during reading */ public boolean getDTR() throws IOException; /** * Sets the DTR (Data Terminal Ready) bit on the underlying UART, if * supported. * * @param value the value to set * @throws IOException if an error occurred during writing */ public void setDTR(boolean value) throws IOException; /** * Gets the RI (Ring Indicator) bit from the underlying UART. * * @return the current state, or {@code false} if not supported. * @throws IOException if an error occurred during reading */ public boolean getRI() throws IOException; /** * Gets the RTS (Request To Send) bit from the underlying UART. * * @return the current state, or {@code false} if not supported. * @throws IOException if an error occurred during reading */ public boolean getRTS() throws IOException; /** * Sets the RTS (Request To Send) bit on the underlying UART, if * supported. * * @param value the value to set * @throws IOException if an error occurred during writing */ public void setRTS(boolean value) throws IOException; /** * Flush non-transmitted output data and / or non-read input data * @param flushRX {@code true} to flush non-transmitted output data * @param flushTX {@code true} to flush non-read input data * @return {@code true} if the operation was successful, or * {@code false} if the operation is not supported by the driver or device * @throws IOException if an error occurred during flush */ public boolean purgeHwBuffers(boolean flushRX, boolean flushTX) throws IOException; } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/usbserial/driver/UsbSerialProber.java ================================================ /* Copyright 2011-2013 Google Inc. * Copyright 2013 mike wakerly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Project home page: https://github.com/mik3y/usb-serial-for-android */ package com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; /** * * @author mike wakerly (opensource@hoho.com) */ public class UsbSerialProber { private final ProbeTable mProbeTable; public UsbSerialProber(ProbeTable probeTable) { mProbeTable = probeTable; } public static UsbSerialProber getDefaultProber() { return new UsbSerialProber(getDefaultProbeTable()); } public static ProbeTable getDefaultProbeTable() { final ProbeTable probeTable = new ProbeTable(); probeTable.addDriver(CdcAcmSerialDriver.class); probeTable.addDriver(Cp21xxSerialDriver.class); probeTable.addDriver(FtdiSerialDriver.class); probeTable.addDriver(ProlificSerialDriver.class); return probeTable; } /** * Finds and builds all possible {@link UsbSerialDriver UsbSerialDrivers} * from the currently-attached {@link UsbDevice} hierarchy. This method does * not require permission from the Android USB system, since it does not * open any of the devices. * * @param usbManager * @return a list, possibly empty, of all compatible drivers */ public List findAllDrivers(final UsbManager usbManager) { final List result = new ArrayList(); for (final UsbDevice usbDevice : usbManager.getDeviceList().values()) { final UsbSerialDriver driver = probeDevice(usbDevice); if (driver != null) { result.add(driver); } } return result; } /** * Probes a single device for a compatible driver. * * @param usbDevice the usb device to probe * @return a new {@link UsbSerialDriver} compatible with this device, or * {@code null} if none available. */ public UsbSerialDriver probeDevice(final UsbDevice usbDevice) { final int vendorId = usbDevice.getVendorId(); final int productId = usbDevice.getProductId(); final Class driverClass = mProbeTable.findDriver(vendorId, productId); if (driverClass != null) { final UsbSerialDriver driver; try { final Constructor ctor = driverClass.getConstructor(UsbDevice.class); driver = ctor.newInstance(usbDevice); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } catch (IllegalArgumentException e) { throw new RuntimeException(e); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } return driver; } return null; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/usbserial/driver/UsbSerialRuntimeException.java ================================================ /* * Copyright 2011 Google Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver; /** * Generic unchecked exception for the usbserial package. * * @author mike wakerly (opensource@hoho.com) */ @SuppressWarnings("serial") public class UsbSerialRuntimeException extends RuntimeException { public UsbSerialRuntimeException() { super(); } public UsbSerialRuntimeException(String detailMessage, Throwable throwable) { super(detailMessage, throwable); } public UsbSerialRuntimeException(String detailMessage) { super(detailMessage); } public UsbSerialRuntimeException(Throwable throwable) { super(throwable); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/usbserial/util/HexDump.java ================================================ /* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.util; /** * Clone of Android's HexDump class, for use in debugging. Cosmetic changes * only. */ public class HexDump { private final static char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; public static String dumpHexString(byte[] array) { return dumpHexString(array, 0, array.length); } public static String dumpHexString(byte[] array, int offset, int length) { StringBuilder result = new StringBuilder(); byte[] line = new byte[16]; int lineIndex = 0; result.append("\n0x"); result.append(toHexString(offset)); for (int i = offset; i < offset + length; i++) { if (lineIndex == 16) { result.append(" "); for (int j = 0; j < 16; j++) { if (line[j] > ' ' && line[j] < '~') { result.append(new String(line, j, 1)); } else { result.append("."); } } result.append("\n0x"); result.append(toHexString(i)); lineIndex = 0; } byte b = array[i]; result.append(" "); result.append(HEX_DIGITS[(b >>> 4) & 0x0F]); result.append(HEX_DIGITS[b & 0x0F]); line[lineIndex++] = b; } if (lineIndex != 16) { int count = (16 - lineIndex) * 3; count++; for (int i = 0; i < count; i++) { result.append(" "); } for (int i = 0; i < lineIndex; i++) { if (line[i] > ' ' && line[i] < '~') { result.append(new String(line, i, 1)); } else { result.append("."); } } } return result.toString(); } public static String toHexString(byte b) { return toHexString(toByteArray(b)); } public static String toHexString(byte[] array) { return toHexString(array, 0, array.length); } public static String toHexString(byte[] array, int offset, int length) { char[] buf = new char[length * 2]; int bufIndex = 0; for (int i = offset; i < offset + length; i++) { byte b = array[i]; buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F]; buf[bufIndex++] = HEX_DIGITS[b & 0x0F]; } return new String(buf); } public static String toHexString(int i) { return toHexString(toByteArray(i)); } public static String toHexString(short i) { return toHexString(toByteArray(i)); } public static byte[] toByteArray(byte b) { byte[] array = new byte[1]; array[0] = b; return array; } public static byte[] toByteArray(int i) { byte[] array = new byte[4]; array[3] = (byte) (i & 0xFF); array[2] = (byte) ((i >> 8) & 0xFF); array[1] = (byte) ((i >> 16) & 0xFF); array[0] = (byte) ((i >> 24) & 0xFF); return array; } public static byte[] toByteArray(short i) { byte[] array = new byte[2]; array[1] = (byte) (i & 0xFF); array[0] = (byte) ((i >> 8) & 0xFF); return array; } private static int toByte(char c) { if (c >= '0' && c <= '9') return (c - '0'); if (c >= 'A' && c <= 'F') return (c - 'A' + 10); if (c >= 'a' && c <= 'f') return (c - 'a' + 10); throw new RuntimeException("Invalid hex char '" + c + "'"); } public static byte[] hexStringToByteArray(String hexString) { int length = hexString.length(); byte[] buffer = new byte[length / 2]; for (int i = 0; i < length; i += 2) { buffer[i / 2] = (byte) ((toByte(hexString.charAt(i)) << 4) | toByte(hexString .charAt(i + 1))); } return buffer; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ImportedLibraries/usbserial/util/SerialInputOutputManager.java ================================================ /* Copyright 2011-2013 Google Inc. * Copyright 2013 mike wakerly * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Project home page: https://github.com/mik3y/usb-serial-for-android */ package com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.util; import android.hardware.usb.UsbRequest; import android.util.Log; import com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver.UsbSerialPort; import java.io.IOException; import java.nio.ByteBuffer; /** * Utility class which services a {@link UsbSerialPort} in its {@link #run()} * method. * * @author mike wakerly (opensource@hoho.com) */ public class SerialInputOutputManager implements Runnable { private static final String TAG = SerialInputOutputManager.class.getSimpleName(); private static final boolean DEBUG = true; private static final int READ_WAIT_MILLIS = 200; private static final int BUFSIZ = 4096; private final UsbSerialPort mDriver; private final ByteBuffer mReadBuffer = ByteBuffer.allocate(BUFSIZ); // Synchronized by 'mWriteBuffer' private final ByteBuffer mWriteBuffer = ByteBuffer.allocate(BUFSIZ); private enum State { STOPPED, RUNNING, STOPPING } // Synchronized by 'this' private State mState = State.STOPPED; // Synchronized by 'this' private Listener mListener; public interface Listener { /** * Called when new incoming data is available. */ public void onNewData(byte[] data); /** * Called when {@link SerialInputOutputManager#run()} aborts due to an * error. */ public void onRunError(Exception e); } /** * Creates a new instance with no listener. */ public SerialInputOutputManager(UsbSerialPort driver) { this(driver, null); } /** * Creates a new instance with the provided listener. */ public SerialInputOutputManager(UsbSerialPort driver, Listener listener) { mDriver = driver; mListener = listener; } public synchronized void setListener(Listener listener) { mListener = listener; } public synchronized Listener getListener() { return mListener; } public void writeAsync(byte[] data) { synchronized (mWriteBuffer) { mWriteBuffer.put(data); } } public synchronized void stop() { if (getState() == State.RUNNING) { Log.i(TAG, "Stop requested"); mState = State.STOPPING; } } private synchronized State getState() { return mState; } /** * Continuously services the read and write buffers until {@link #stop()} is * called, or until a driver exception is raised. * * NOTE(mikey): Uses inefficient read/write-with-timeout. * TODO(mikey): Read asynchronously with {@link UsbRequest#queue(ByteBuffer, int)} */ @Override public void run() { synchronized (this) { if (getState() != State.STOPPED) { throw new IllegalStateException("Already running."); } mState = State.RUNNING; } Log.i(TAG, "Running .."); try { while (true) { if (getState() != State.RUNNING) { Log.i(TAG, "Stopping mState=" + getState()); break; } step(); } } catch (Exception e) { Log.w(TAG, "Run ending due to exception: " + e.getMessage(), e); final Listener listener = getListener(); if (listener != null) { listener.onRunError(e); } } finally { synchronized (this) { mState = State.STOPPED; Log.i(TAG, "Stopped."); } } } private void step() throws IOException { // Handle incoming data. int len = mDriver.read(mReadBuffer.array(), READ_WAIT_MILLIS); if (len > 0) { if (DEBUG) Log.d(TAG, "Read data len=" + len); final Listener listener = getListener(); if (listener != null) { final byte[] data = new byte[len]; mReadBuffer.get(data, 0, len); listener.onNewData(data); } mReadBuffer.clear(); } // Handle outgoing data. byte[] outBuff = null; synchronized (mWriteBuffer) { len = mWriteBuffer.position(); if (len > 0) { outBuff = new byte[len]; mWriteBuffer.rewind(); mWriteBuffer.get(outBuff, 0, len); mWriteBuffer.clear(); } } if (outBuff != null) { if (DEBUG) { Log.d(TAG, "Writing data len=" + len); } mDriver.write(outBuff, READ_WAIT_MILLIS); } } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Interfaces/BgReadingInterface.java ================================================ package com.eveningoutpost.dexdrip.Interfaces; import com.eveningoutpost.dexdrip.Models.BgReading; import retrofit.Callback; import retrofit.http.Body; import retrofit.http.POST; import retrofit.http.PUT; import retrofit.http.Path; /** * Created by stephenblack on 11/6/14. */ public interface BgReadingInterface { @POST("/api/v1/users/{user_uuid}/BgReadings/new") void createReading(@Path("user_uuid") String user_uuid, @Body BgReading bgReading, Callback callback); @PUT("/api/v1/users/{user_uuid}/BgReading/{bgReading_uuid}") void updateReading(@Path("user_uuid") String user_uuid, @Path("bgReading_uuid") String uuid, @Body BgReading bgReading, Callback callback); } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Interfaces/CalibrationInterface.java ================================================ package com.eveningoutpost.dexdrip.Interfaces; import com.eveningoutpost.dexdrip.Models.Calibration; import retrofit.Callback; import retrofit.http.Body; import retrofit.http.POST; import retrofit.http.Path; /** * Created by stephenblack on 11/7/14. */ public interface CalibrationInterface { @POST("/api/v1/users/{user_uuid}/calibrations/new") void createCalibration(@Path("user_uuid") String user_uuid, @Body Calibration calibration, Callback callback); } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Interfaces/SensorInterface.java ================================================ package com.eveningoutpost.dexdrip.Interfaces; import com.eveningoutpost.dexdrip.Sensor; import retrofit.Callback; import retrofit.http.Body; import retrofit.http.POST; import retrofit.http.Path; /** * Created by stephenblack on 11/7/14. */ public interface SensorInterface { @POST("/api/v1/users/{user_uuid}/sensors/new") void createSensor(@Path("user_uuid") String user_uuid, @Body Sensor sensor, Callback callback); } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Interfaces/UserInterface.java ================================================ package com.eveningoutpost.dexdrip.Interfaces; import com.eveningoutpost.dexdrip.Models.User; import retrofit.Callback; import retrofit.http.Body; import retrofit.http.POST; /** * Created by stephenblack on 11/10/14. */ public interface UserInterface { @POST("/api/v1/sessions/new") void authenticate(@Body User user, Callback callback); } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/LicenseAgreementActivity.java ================================================ package com.eveningoutpost.dexdrip; import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; import com.eveningoutpost.dexdrip.Models.BgReading; public class LicenseAgreementActivity extends Activity { boolean IUnderstand; CheckBox agreeCheckBox; Button saveButton; SharedPreferences prefs; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); IUnderstand = prefs.getBoolean("I_understand", false); setContentView(R.layout.activity_license_agreement); agreeCheckBox = (CheckBox)findViewById(R.id.agreeCheckBox); agreeCheckBox.setChecked(IUnderstand); saveButton = (Button)findViewById(R.id.saveButton); addListenerOnButton(); } public void addListenerOnButton() { saveButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { prefs.edit().putBoolean("I_understand", agreeCheckBox.isChecked()).apply(); Intent intent = new Intent(getApplicationContext(), Home.class); startActivity(intent); finish(); } }); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Models/ActiveBluetoothDevice.java ================================================ package com.eveningoutpost.dexdrip.Models; import android.provider.BaseColumns; import com.activeandroid.Model; import com.activeandroid.annotation.Column; import com.activeandroid.annotation.Table; import com.activeandroid.query.Select; /** * Created by stephenblack on 11/3/14. */ @Table(name = "ActiveBluetoothDevice", id = BaseColumns._ID) public class ActiveBluetoothDevice extends Model { @Column(name = "name") public String name; @Column(name = "address") public String address; @Column(name = "connected") public boolean connected; public static ActiveBluetoothDevice first() { return new Select() .from(ActiveBluetoothDevice.class) .orderBy("_ID asc") .executeSingle(); } public static void forget() { ActiveBluetoothDevice activeBluetoothDevice = ActiveBluetoothDevice.first(); if (activeBluetoothDevice != null) { activeBluetoothDevice.delete(); } } public static void connected() { ActiveBluetoothDevice activeBluetoothDevice = ActiveBluetoothDevice.first(); if(activeBluetoothDevice != null) { activeBluetoothDevice.connected = true; activeBluetoothDevice.save(); } } public static void disconnected() { ActiveBluetoothDevice activeBluetoothDevice = ActiveBluetoothDevice.first(); if(activeBluetoothDevice != null) { activeBluetoothDevice.connected = false; activeBluetoothDevice.save(); } } public static boolean is_connected() { ActiveBluetoothDevice activeBluetoothDevice = ActiveBluetoothDevice.first(); if(activeBluetoothDevice != null) { return activeBluetoothDevice.connected; } return false; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Models/BgReading.java ================================================ package com.eveningoutpost.dexdrip.Models; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.provider.BaseColumns; import android.util.Log; import com.activeandroid.Model; import com.activeandroid.annotation.Column; import com.activeandroid.annotation.Table; import com.activeandroid.query.Select; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.CalSubrecord; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.EGVRecord; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.SensorRecord; import com.eveningoutpost.dexdrip.Sensor; import com.eveningoutpost.dexdrip.Services.DexShareCollectionService; import com.eveningoutpost.dexdrip.UtilityModels.BgSendQueue; import com.eveningoutpost.dexdrip.UtilityModels.Constants; import com.eveningoutpost.dexdrip.UtilityModels.Notifications; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.annotations.Expose; import com.google.gson.internal.bind.DateTypeAdapter; import java.text.DecimalFormat; import java.util.Date; import java.util.List; import java.util.UUID; @Table(name = "BgReadings", id = BaseColumns._ID) public class BgReading extends Model { private final static String TAG = BgReading.class.getSimpleName(); //TODO: Have these as adjustable settings!! public final static double BESTOFFSET = (60000 * 0); // Assume readings are about x minutes off from actual! @Column(name = "sensor", index = true) public Sensor sensor; @Column(name = "calibration", index = true) public Calibration calibration; @Expose @Column(name = "timestamp", index = true) public long timestamp; @Expose @Column(name = "time_since_sensor_started") public double time_since_sensor_started; @Expose @Column(name = "raw_data") public double raw_data; @Expose @Column(name = "filtered_data") public double filtered_data; @Expose @Column(name = "age_adjusted_raw_value") public double age_adjusted_raw_value; @Expose @Column(name = "calibration_flag") public boolean calibration_flag; @Expose @Column(name = "calculated_value") public double calculated_value; @Expose @Column(name = "calculated_value_slope") public double calculated_value_slope; @Expose @Column(name = "a") public double a; @Expose @Column(name = "b") public double b; @Expose @Column(name = "c") public double c; @Expose @Column(name = "ra") public double ra; @Expose @Column(name = "rb") public double rb; @Expose @Column(name = "rc") public double rc; @Expose @Column(name = "uuid", index = true) public String uuid; @Expose @Column(name = "calibration_uuid") public String calibration_uuid; @Expose @Column(name = "sensor_uuid", index = true) public String sensor_uuid; @Column(name = "snyced") public boolean synced; @Column(name = "raw_calculated") public double raw_calculated; @Column(name = "hide_slope") public boolean hide_slope; @Column(name = "noise") public String noise; public double calculated_value_mmol() { return mmolConvert(calculated_value); } public double mmolConvert(double mgdl) { return mgdl * Constants.MGDL_TO_MMOLL; } public String displayValue(Context context) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); String unit = prefs.getString("units", "mgdl"); DecimalFormat df = new DecimalFormat("#"); df.setMaximumFractionDigits(0); if (calculated_value >= 400) { return "HIGH"; } else if (calculated_value >= 40) { if(unit.compareTo("mgdl") == 0) { df.setMaximumFractionDigits(0); return df.format(calculated_value); } else { df.setMaximumFractionDigits(1); return df.format(calculated_value_mmol()); } } else { return "LOW"; } } public static double activeSlope() { BgReading bgReading = BgReading.lastNoSenssor(); double slope = (2 * bgReading.a * (new Date().getTime() + BESTOFFSET)) + bgReading.b; Log.w(TAG, "ESTIMATE SLOPE" + slope); return slope; } public static double activePrediction() { BgReading bgReading = BgReading.lastNoSenssor(); if (bgReading != null) { double currentTime = new Date().getTime(); if (currentTime >= bgReading.timestamp + (60000 * 7)) { currentTime = bgReading.timestamp + (60000 * 7); } double time = currentTime + BESTOFFSET; return ((bgReading.a * time * time) + (bgReading.b * time) + bgReading.c); } return 0; } //*******CLASS METHODS***********// public static void create(EGVRecord[] egvRecords, long addativeOffset, Context context) { for(EGVRecord egvRecord : egvRecords) { BgReading.create(egvRecord, addativeOffset, context); } } public static void create(SensorRecord[] sensorRecords, long addativeOffset, Context context) { for(SensorRecord sensorRecord : sensorRecords) { BgReading.create(sensorRecord, addativeOffset, context); } } public static void create(SensorRecord sensorRecord, long addativeOffset, Context context) { Log.w(TAG, "gonna make some sensor records: " + sensorRecord.getUnfiltered()); if(BgReading.is_new(sensorRecord, addativeOffset)) { BgReading bgReading = new BgReading(); Sensor sensor = Sensor.currentSensor(); Calibration calibration = Calibration.getForTimestamp(sensorRecord.getSystemTime().getTime() + addativeOffset); if(sensor != null && calibration != null) { bgReading.sensor = sensor; bgReading.sensor_uuid = sensor.uuid; bgReading.calibration = calibration; bgReading.calibration_uuid = calibration.uuid; bgReading.raw_data = (sensorRecord.getUnfiltered() / 1000); bgReading.filtered_data = (sensorRecord.getFiltered() / 1000); bgReading.timestamp = sensorRecord.getSystemTime().getTime() + addativeOffset; if(bgReading.timestamp > new Date().getTime()) { return; } bgReading.uuid = UUID.randomUUID().toString(); bgReading.time_since_sensor_started = bgReading.timestamp - sensor.started_at; bgReading.synced = false; bgReading.calculateAgeAdjustedRawValue(); bgReading.save(); } } } public static void create(EGVRecord egvRecord, long addativeOffset, Context context) { BgReading bgReading = BgReading.getForTimestamp(egvRecord.getSystemTime().getTime() + addativeOffset); Log.w(TAG, "Looking for BG reading to tag this thing to: " + egvRecord.getBGValue()); if(bgReading != null) { bgReading.calculated_value = egvRecord.getBGValue(); if (egvRecord.getBGValue() <= 13) { Calibration calibration = bgReading.calibration; double firstAdjSlope = calibration.first_slope + (calibration.first_decay * (Math.ceil(new Date().getTime() - calibration.timestamp)/(1000 * 60 * 10))); double calSlope = (calibration.first_scale / firstAdjSlope)*1000; double calIntercept = ((calibration.first_scale * calibration.first_intercept) / firstAdjSlope)*-1; bgReading.raw_calculated = (((calSlope * bgReading.raw_data) + calIntercept) - 5); bgReading.noise = egvRecord.noiseValue(); } Log.w(TAG, "NEW VALUE CALCULATED AT: " + bgReading.calculated_value); bgReading.calculated_value_slope = bgReading.slopefromName(egvRecord.getTrend().friendlyTrendName()); if(egvRecord.getTrend().friendlyTrendName().compareTo("NOT_COMPUTABLE") == 0 || egvRecord.getTrend().friendlyTrendName().compareTo("OUT_OF_RANGE") == 0) { bgReading.hide_slope = true; } bgReading.save(); bgReading.find_new_curve(); bgReading.find_new_raw_curve(); Notifications.notificationSetter(context); BgSendQueue.addToQueue(bgReading, "create", context); } } public static BgReading getForTimestamp(double timestamp) { Sensor sensor = Sensor.currentSensor(); if(sensor != null) { BgReading bgReading = new Select() .from(BgReading.class) .where("Sensor = ? ", sensor.getId()) .where("timestamp <= ?", (timestamp + (60*1000))) // 1 minute padding (should never be that far off, but why not) .where("calculated_value = 0") .where("raw_calculated = 0") .orderBy("timestamp desc") .executeSingle(); if(bgReading != null && Math.abs(bgReading.timestamp - timestamp) < (3*60*1000)) { //cool, so was it actually within 4 minutes of that bg reading? Log.w(TAG, "Found a BG timestamp match"); return bgReading; } } Log.w(TAG, "No luck finding a BG timestamp match"); return null; } public static boolean is_new(SensorRecord sensorRecord, long addativeOffset) { double timestamp = sensorRecord.getSystemTime().getTime() + addativeOffset; Sensor sensor = Sensor.currentSensor(); if(sensor != null) { BgReading bgReading = new Select() .from(BgReading.class) .where("Sensor = ? ", sensor.getId()) .where("timestamp <= ?", (timestamp + (60*1000))) // 1 minute padding (should never be that far off, but why not) .orderBy("timestamp desc") .executeSingle(); if(bgReading != null && Math.abs(bgReading.timestamp - timestamp) < (3*60*1000)) { //cool, so was it actually within 4 minutes of that bg reading? Log.w(TAG, "Old Reading"); return false; } } Log.w(TAG, "New Reading"); return true; } public static BgReading create(double raw_data, Context context, Long timestamp) { BgReading bgReading = new BgReading(); Sensor sensor = Sensor.currentSensor(); if (sensor != null) { Calibration calibration = Calibration.last(); if (calibration == null) { bgReading.sensor = sensor; bgReading.sensor_uuid = sensor.uuid; bgReading.raw_data = (raw_data / 1000); bgReading.filtered_data = (raw_data / 1000); bgReading.timestamp = timestamp; bgReading.uuid = UUID.randomUUID().toString(); bgReading.time_since_sensor_started = bgReading.timestamp - sensor.started_at; bgReading.synced = false; bgReading.calibration_flag = false; bgReading.calculateAgeAdjustedRawValue(); bgReading.save(); bgReading.perform_calculations(); } else { bgReading.sensor = sensor; bgReading.sensor_uuid = sensor.uuid; bgReading.calibration = calibration; bgReading.calibration_uuid = calibration.uuid; bgReading.raw_data = (raw_data/1000); bgReading.filtered_data = (raw_data/1000); bgReading.timestamp = timestamp; bgReading.uuid = UUID.randomUUID().toString(); bgReading.time_since_sensor_started = bgReading.timestamp - sensor.started_at; bgReading.synced = false; bgReading.calculateAgeAdjustedRawValue(); if(calibration.check_in) { double firstAdjSlope = calibration.first_slope + (calibration.first_decay * (Math.ceil(new Date().getTime() - calibration.timestamp)/(1000 * 60 * 10))); double calSlope = (calibration.first_scale / firstAdjSlope)*1000; double calIntercept = ((calibration.first_scale * calibration.first_intercept) / firstAdjSlope)*-1; bgReading.calculated_value = (((calSlope * bgReading.raw_data) + calIntercept) - 5); } else { BgReading lastBgReading = BgReading.last(); if (lastBgReading != null && lastBgReading.calibration != null) { if (lastBgReading.calibration_flag == true && ((lastBgReading.timestamp + (60000 * 20)) > bgReading.timestamp) && ((lastBgReading.calibration.timestamp + (60000 * 20)) > bgReading.timestamp)) { lastBgReading.calibration.rawValueOverride(BgReading.weightedAverageRaw(lastBgReading.timestamp, bgReading.timestamp, lastBgReading.calibration.timestamp, lastBgReading.age_adjusted_raw_value, bgReading.age_adjusted_raw_value), context); } } bgReading.calculated_value = ((calibration.slope * bgReading.age_adjusted_raw_value) + calibration.intercept); } bgReading.calculated_value = Math.min(400, Math.max(40, bgReading.calculated_value)); Log.w(TAG, "NEW VALUE CALCULATED AT: " + bgReading.calculated_value); bgReading.save(); bgReading.perform_calculations(); Notifications.notificationSetter(context); BgSendQueue.addToQueue(bgReading, "create", context); } } Log.w("BG GSON: ",bgReading.toS()); return bgReading; } public static String slopeArrow() { double slope = (float) (BgReading.activeSlope() * 60000); return slopeArrow(slope); } public static String slopeArrow(double slope) { String arrow; if (slope <= (-3.5)) { arrow = "\u21ca"; } else if (slope <= (-2)) { arrow = "\u2193"; } else if (slope <= (-1)) { arrow = "\u2198"; } else if (slope <= (1)) { arrow = "\u2192"; } else if (slope <= (2)) { arrow = "\u2197"; } else if (slope <= (3.5)) { arrow = "\u2191"; } else { arrow = "\u21c8"; } return arrow; } public String slopeName() { double slope_by_minute = calculated_value_slope * 60000; String arrow = "NONE"; if (slope_by_minute <= (-3.5)) { arrow = "DoubleDown"; } else if (slope_by_minute <= (-2)) { arrow = "SingleDown"; } else if (slope_by_minute <= (-1)) { arrow = "FortyFiveDown"; } else if (slope_by_minute <= (1)) { arrow = "Flat"; } else if (slope_by_minute <= (2)) { arrow = "FortyFiveUp"; } else if (slope_by_minute <= (3.5)) { arrow = "SingleUp"; } else if (slope_by_minute <= (40)) { arrow = "DoubleUp"; } if(hide_slope) { arrow = "9"; } return arrow; } public double slopefromName(String slope_name) { double slope_by_minute = 0; if (slope_name.compareTo("DoubleDown") == 0) { slope_by_minute = -3.5; } else if (slope_name.compareTo("SingleDown") == 0) { slope_by_minute = -2; } else if (slope_name.compareTo("FortyFiveDown") == 0) { slope_by_minute = -1; } else if (slope_name.compareTo("Flat") == 0) { slope_by_minute = 0; } else if (slope_name.compareTo("FortyFiveUp") == 0) { slope_by_minute = 2; } else if (slope_name.compareTo("SingleUp") == 0) { slope_by_minute = 2; } else if (slope_name.compareTo("DoubleUp") == 0) { slope_by_minute = 4; } else if (slope_name.compareTo("NOT_COMPUTABLE") == 0 || slope_name.compareTo("OUT_OF_RANGE") == 0) { slope_by_minute = 0; } return slope_by_minute /60000; } public static BgReading last() { Sensor sensor = Sensor.currentSensor(); if (sensor != null) { return new Select() .from(BgReading.class) .where("Sensor = ? ", sensor.getId()) .where("calculated_value != 0") .where("raw_data != 0") .orderBy("timestamp desc") .executeSingle(); } return null; } public static List latest_by_size(int number) { Sensor sensor = Sensor.currentSensor(); return new Select() .from(BgReading.class) .where("Sensor = ? ", sensor.getId()) .where("raw_data != 0") .orderBy("timestamp desc") .limit(number) .execute(); } public static BgReading lastNoSenssor() { return new Select() .from(BgReading.class) .where("calculated_value != 0") .where("raw_data != 0") .orderBy("timestamp desc") .executeSingle(); } public static List latest(int number) { Sensor sensor = Sensor.currentSensor(); if (sensor == null) { return null; } return new Select() .from(BgReading.class) .where("Sensor = ? ", sensor.getId()) .where("calculated_value != 0") .where("raw_data != 0") .orderBy("timestamp desc") .limit(number) .execute(); } public static List latestUnCalculated(int number) { Sensor sensor = Sensor.currentSensor(); if (sensor == null) { return null; } return new Select() .from(BgReading.class) .where("Sensor = ? ", sensor.getId()) .where("raw_data != 0") .orderBy("timestamp desc") .limit(number) .execute(); } public static List latestForGraph(int number, double startTime) { DecimalFormat df = new DecimalFormat("#"); df.setMaximumFractionDigits(1); return new Select() .from(BgReading.class) .where("timestamp >= " + df.format(startTime)) .where("calculated_value != 0") .where("raw_data != 0") .orderBy("timestamp desc") .limit(number) .execute(); } public static List last30Minutes() { double timestamp = (new Date().getTime()) - (60000 * 30); return new Select() .from(BgReading.class) .where("timestamp >= " + timestamp) .where("calculated_value != 0") .where("raw_data != 0") .orderBy("timestamp desc") .execute(); } public static double estimated_bg(double timestamp) { timestamp = timestamp + BESTOFFSET; BgReading latest = BgReading.last(); if (latest == null) { return 0; } else { return (latest.a * timestamp * timestamp) + (latest.b * timestamp) + latest.c; } } public static double estimated_raw_bg(double timestamp) { timestamp = timestamp + BESTOFFSET; double estimate; BgReading latest = BgReading.last(); if (latest == null) { Log.w(TAG, "No data yet, assume perfect!"); estimate = 160; } else { estimate = (latest.ra * timestamp * timestamp) + (latest.rb * timestamp) + latest.rc; } Log.w(TAG, "ESTIMATE RAW BG" + estimate); return estimate; } //*******INSTANCE METHODS***********// public void perform_calculations() { find_new_curve(); find_new_raw_curve(); find_slope(); } public void find_slope() { List last_2 = BgReading.latest(2); if (last_2.size() == 2) { BgReading second_latest = last_2.get(1); double y1 = calculated_value; double x1 = timestamp; double y2 = second_latest.calculated_value; double x2 = second_latest.timestamp; if(y1 == y2) { calculated_value_slope = 0; } else { calculated_value_slope = (y2 - y1)/(x2 - x1); } save(); } else if (last_2.size() == 1) { calculated_value_slope = 0; save(); } else { Log.w(TAG, "NO BG? COULDNT FIND SLOPE!"); } } public void find_new_curve() { List last_3 = BgReading.latest(3); if (last_3.size() == 3) { BgReading second_latest = last_3.get(1); BgReading third_latest = last_3.get(2); double y3 = calculated_value; double x3 = timestamp; double y2 = second_latest.calculated_value; double x2 = second_latest.timestamp; double y1 = third_latest.calculated_value; double x1 = third_latest.timestamp; a = y1/((x1-x2)*(x1-x3))+y2/((x2-x1)*(x2-x3))+y3/((x3-x1)*(x3-x2)); b = (-y1*(x2+x3)/((x1-x2)*(x1-x3))-y2*(x1+x3)/((x2-x1)*(x2-x3))-y3*(x1+x2)/((x3-x1)*(x3-x2))); c = (y1*x2*x3/((x1-x2)*(x1-x3))+y2*x1*x3/((x2-x1)*(x2-x3))+y3*x1*x2/((x3-x1)*(x3-x2))); Log.w(TAG, "BG PARABOLIC RATES: "+a+"x^2 + "+b+"x + "+c); save(); } else if (last_3.size() == 2) { Log.w(TAG, "Not enough data to calculate parabolic rates - assume Linear"); BgReading latest = last_3.get(0); BgReading second_latest = last_3.get(1); double y2 = latest.calculated_value; double x2 = timestamp; double y1 = second_latest.calculated_value; double x1 = second_latest.timestamp; if(y1 == y2) { b = 0; } else { b = (y2 - y1)/(x2 - x1); } a = 0; c = -1 * ((latest.b * x1) - y1); Log.w(TAG, ""+latest.a+"x^2 + "+latest.b+"x + "+latest.c); save(); } else { Log.w(TAG, "Not enough data to calculate parabolic rates - assume static data"); a = 0; b = 0; c = calculated_value; Log.w(TAG, ""+a+"x^2 + "+b+"x + "+c); save(); } } public void calculateAgeAdjustedRawValue(){ double adjust_for = (86400000 * 1.9) - time_since_sensor_started; if (adjust_for > 0) { age_adjusted_raw_value = (((.45) * (adjust_for / (86400000 * 1.9))) * raw_data) + raw_data; Log.w("RAW VALUE ADJUSTMENT: ", "FROM:" + raw_data + " TO: " + age_adjusted_raw_value); } else { age_adjusted_raw_value = raw_data; } } public void find_new_raw_curve() { List last_3 = BgReading.latest(3); if (last_3.size() == 3) { BgReading second_latest = last_3.get(1); BgReading third_latest = last_3.get(2); double y3 = age_adjusted_raw_value; double x3 = timestamp; double y2 = second_latest.age_adjusted_raw_value; double x2 = second_latest.timestamp; double y1 = third_latest.age_adjusted_raw_value; double x1 = third_latest.timestamp; ra = y1/((x1-x2)*(x1-x3))+y2/((x2-x1)*(x2-x3))+y3/((x3-x1)*(x3-x2)); rb = (-y1*(x2+x3)/((x1-x2)*(x1-x3))-y2*(x1+x3)/((x2-x1)*(x2-x3))-y3*(x1+x2)/((x3-x1)*(x3-x2))); rc = (y1*x2*x3/((x1-x2)*(x1-x3))+y2*x1*x3/((x2-x1)*(x2-x3))+y3*x1*x2/((x3-x1)*(x3-x2))); Log.w(TAG, "RAW PARABOLIC RATES: "+ra+"x^2 + "+rb+"x + "+rc); save(); } else if (last_3.size() == 2) { BgReading latest = last_3.get(0); BgReading second_latest = last_3.get(1); double y2 = latest.age_adjusted_raw_value; double x2 = timestamp; double y1 = second_latest.age_adjusted_raw_value; double x1 = second_latest.timestamp; if(y1 == y2) { rb = 0; } else { rb = (y2 - y1)/(x2 - x1); } ra = 0; rc = -1 * ((latest.rb * x1) - y1); Log.w(TAG, "Not enough data to calculate parabolic rates - assume Linear data"); Log.w(TAG, "RAW PARABOLIC RATES: "+ra+"x^2 + "+rb+"x + "+rc); save(); } else { Log.w(TAG, "Not enough data to calculate parabolic rates - assume static data"); BgReading latest_entry = BgReading.lastNoSenssor(); ra = 0; rb = 0; rc = latest_entry.age_adjusted_raw_value; save(); } } public static double weightedAverageRaw(double timeA, double timeB, double calibrationTime, double rawA, double rawB) { double relativeSlope = (rawB - rawA)/(timeB - timeA); double relativeIntercept = rawA - (relativeSlope * timeA); return ((relativeSlope * calibrationTime) + relativeIntercept); } public String toS() { Gson gson = new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() .registerTypeAdapter(Date.class, new DateTypeAdapter()) .serializeSpecialFloatingPointValues() .create(); return gson.toJson(this); } public String noiseValue() { if(noise == null || noise.compareTo("") == 0) { return "1"; } else { return String.valueOf(noise); } } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Models/Calibration.java ================================================ package com.eveningoutpost.dexdrip.Models; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.provider.BaseColumns; import android.util.Log; import com.activeandroid.Model; import com.activeandroid.annotation.Column; import com.activeandroid.annotation.Table; import com.activeandroid.query.Select; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.CalRecord; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.CalSubrecord; import com.eveningoutpost.dexdrip.Sensor; import com.eveningoutpost.dexdrip.UtilityModels.BgSendQueue; import com.eveningoutpost.dexdrip.UtilityModels.CalibrationSendQueue; import com.eveningoutpost.dexdrip.UtilityModels.Constants; import com.eveningoutpost.dexdrip.UtilityModels.Notifications; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.annotations.Expose; import com.google.gson.internal.bind.DateTypeAdapter; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.UUID; /** * Created by stephenblack on 10/29/14. */ @Table(name = "Calibration", id = BaseColumns._ID) public class Calibration extends Model { private final static String TAG = Calibration.class.getSimpleName(); @Expose @Column(name = "timestamp", index = true) public double timestamp; @Expose @Column(name = "sensor_age_at_time_of_estimation") public double sensor_age_at_time_of_estimation; @Column(name = "sensor", index = true) public Sensor sensor; @Expose @Column(name = "bg") public double bg; @Expose @Column(name = "raw_value") public double raw_value; // // @Expose // @Column(name = "filtered_value") // public double filtered_value; @Expose @Column(name = "adjusted_raw_value") public double adjusted_raw_value; @Expose @Column(name = "sensor_confidence") public double sensor_confidence; @Expose @Column(name = "slope_confidence") public double slope_confidence; @Expose @Column(name = "raw_timestamp") public double raw_timestamp; @Expose @Column(name = "slope") public double slope; @Expose @Column(name = "intercept") public double intercept; @Expose @Column(name = "distance_from_estimate") public double distance_from_estimate; @Expose @Column(name = "estimate_raw_at_time_of_calibration") public double estimate_raw_at_time_of_calibration; @Expose @Column(name = "estimate_bg_at_time_of_calibration") public double estimate_bg_at_time_of_calibration; @Expose @Column(name = "uuid", index = true) public String uuid; @Expose @Column(name = "sensor_uuid", index = true) public String sensor_uuid; @Expose @Column(name = "possible_bad") public Boolean possible_bad; @Expose @Column(name = "check_in") public boolean check_in; @Expose @Column(name = "first_decay") public double first_decay; @Expose @Column(name = "second_decay") public double second_decay; @Expose @Column(name = "first_slope") public double first_slope; @Expose @Column(name = "second_slope") public double second_slope; @Expose @Column(name = "first_intercept") public double first_intercept; @Expose @Column(name = "second_intercept") public double second_intercept; @Expose @Column(name = "first_scale") public double first_scale; @Expose @Column(name = "second_scale") public double second_scale; public static void initialCalibration(double bg1, double bg2, Context context) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); String unit = prefs.getString("units", "mgdl"); if(unit.compareTo("mgdl") != 0 ) { bg1 = bg1 * Constants.MMOLL_TO_MGDL; bg2 = bg2 * Constants.MMOLL_TO_MGDL; } clear_all_existing_calibrations(); Calibration higherCalibration = new Calibration(); Calibration lowerCalibration = new Calibration(); Sensor sensor = Sensor.currentSensor(); List bgReadings = BgReading.latest_by_size(2); BgReading bgReading1 = bgReadings.get(0); BgReading bgReading2 = bgReadings.get(1); BgReading highBgReading; BgReading lowBgReading; double higher_bg = Math.max(bg1, bg2); double lower_bg = Math.min(bg1, bg2); if (bgReading1.raw_data > bgReading2.raw_data) { highBgReading = bgReading1; lowBgReading = bgReading2; } else { highBgReading = bgReading2; lowBgReading = bgReading1; } higherCalibration.bg = higher_bg; higherCalibration.slope = 1; higherCalibration.intercept = higher_bg; higherCalibration.sensor = sensor; higherCalibration.estimate_raw_at_time_of_calibration = highBgReading.age_adjusted_raw_value; higherCalibration.adjusted_raw_value = highBgReading.age_adjusted_raw_value; higherCalibration.raw_value = highBgReading.raw_data; higherCalibration.raw_timestamp = highBgReading.timestamp; higherCalibration.save(); highBgReading.calculated_value = higher_bg; highBgReading.calibration_flag = true; highBgReading.calibration = higherCalibration; highBgReading.save(); higherCalibration.save(); lowerCalibration.bg = lower_bg; lowerCalibration.slope = 1; lowerCalibration.intercept = lower_bg; lowerCalibration.sensor = sensor; lowerCalibration.estimate_raw_at_time_of_calibration = lowBgReading.age_adjusted_raw_value; lowerCalibration.adjusted_raw_value = lowBgReading.age_adjusted_raw_value; lowerCalibration.raw_value = lowBgReading.raw_data; lowerCalibration.raw_timestamp = lowBgReading.timestamp; lowerCalibration.save(); lowBgReading.calculated_value = lower_bg; lowBgReading.calibration_flag = true; lowBgReading.calibration = lowerCalibration; lowBgReading.save(); lowerCalibration.save(); highBgReading.find_new_curve(); highBgReading.find_new_raw_curve(); lowBgReading.find_new_curve(); lowBgReading.find_new_raw_curve(); List calibrations = new ArrayList(); calibrations.add(lowerCalibration); calibrations.add(higherCalibration); for(Calibration calibration : calibrations) { calibration.timestamp = new Date().getTime(); calibration.sensor_uuid = sensor.uuid; calibration.slope_confidence = .5; calibration.distance_from_estimate = 0; calibration.check_in = false; calibration.sensor_confidence = ((-0.0018 * calibration.bg * calibration.bg) + (0.6657 * calibration.bg) + 36.7505) / 100; calibration.sensor_age_at_time_of_estimation = calibration.timestamp - sensor.started_at; calibration.uuid = UUID.randomUUID().toString(); calibration.save(); calculate_w_l_s(); CalibrationSendQueue.addToQueue(calibration, context); } adjustRecentBgReadings(5); CalibrationRequest.createOffset(lowerCalibration.bg, 35); Notifications.notificationSetter(context); } //Create Calibration Checkin public static void create(CalRecord[] calRecords, long addativeOffset, Context context) { create(calRecords, context, false, addativeOffset); } public static void create(CalRecord[] calRecords, Context context) { create(calRecords, context, false, 0); } public static void create(CalRecord[] calRecords, Context context, boolean override, long addativeOffset) { //TODO: Change calibration.last and other queries to order calibrations by timestamp rather than ID Log.w("CALIBRATION-CHECK-IN: ", "Creating Calibration Record"); Sensor sensor = Sensor.currentSensor(); CalRecord firstCalRecord = calRecords[0]; CalRecord secondCalRecord = calRecords[0]; // CalRecord secondCalRecord = calRecords[calRecords.length - 1]; //TODO: Figgure out how the ratio between the two is determined double calSlope = ((secondCalRecord.getScale() / secondCalRecord.getSlope()) + (3 * firstCalRecord.getScale() / firstCalRecord.getSlope())) * 250; double calIntercept = (((secondCalRecord.getScale() * secondCalRecord.getIntercept()) / secondCalRecord.getSlope()) + ((3 * firstCalRecord.getScale() * firstCalRecord.getIntercept()) / firstCalRecord.getSlope())) / -4; if (sensor != null) { for(int i = 0; i < firstCalRecord.getCalSubrecords().length - 1; i++) { if (((firstCalRecord.getCalSubrecords()[i] != null && Calibration.is_new(firstCalRecord.getCalSubrecords()[i], addativeOffset))) || (i == 0 && override)) { CalSubrecord calSubrecord = firstCalRecord.getCalSubrecords()[i]; Calibration calibration = new Calibration(); calibration.bg = calSubrecord.getCalBGL(); calibration.timestamp = calSubrecord.getDateEntered().getTime() + addativeOffset; if (calibration.timestamp > new Date().getTime()) { Log.e(TAG, "ERROR - Calibration timestamp is from the future, wont save!"); return; } calibration.raw_value = calSubrecord.getCalRaw() / 1000; calibration.slope = calSlope; calibration.intercept = calIntercept; calibration.sensor_confidence = ((-0.0018 * calibration.bg * calibration.bg) + (0.6657 * calibration.bg) + 36.7505) / 100; if (calibration.sensor_confidence <= 0) { calibration.sensor_confidence = 0; } calibration.slope_confidence = 0.8; //TODO: query backwards to find this value near the timestamp calibration.estimate_raw_at_time_of_calibration = calSubrecord.getCalRaw() / 1000; calibration.sensor = sensor; calibration.sensor_age_at_time_of_estimation = calibration.timestamp - sensor.started_at; calibration.uuid = UUID.randomUUID().toString(); calibration.sensor_uuid = sensor.uuid; calibration.check_in = true; calibration.first_decay = firstCalRecord.getDecay(); calibration.second_decay = secondCalRecord.getDecay(); calibration.first_slope = firstCalRecord.getSlope(); calibration.second_slope = secondCalRecord.getSlope(); calibration.first_scale = firstCalRecord.getScale(); calibration.second_scale = secondCalRecord.getScale(); calibration.first_intercept = firstCalRecord.getIntercept(); calibration.second_intercept = secondCalRecord.getIntercept(); calibration.save(); CalibrationSendQueue.addToQueue(calibration, context); Calibration.requestCalibrationIfRangeTooNarrow(); } } if(firstCalRecord.getCalSubrecords()[0] != null && firstCalRecord.getCalSubrecords()[2] == null) { if(Calibration.latest(2).size() == 1) { Calibration.create(calRecords, context, true, 0); } } Notifications.notificationSetter(context); } } public static boolean is_new(CalSubrecord calSubrecord, long addativeOffset) { Sensor sensor = Sensor.currentSensor(); Calibration calibration = new Select() .from(Calibration.class) .where("Sensor = ? ", sensor.getId()) .where("timestamp <= ?", calSubrecord.getDateEntered().getTime() + addativeOffset + (1000 * 60 * 2)) .orderBy("timestamp desc") .executeSingle(); if(calibration != null && Math.abs(calibration.timestamp - (calSubrecord.getDateEntered().getTime() + addativeOffset)) < (4*60*1000)) { Log.d("CAL CHECK IN ", "Already have that calibration!"); return false; } else { Log.d("CAL CHECK IN ", "Looks like a new calibration!"); return true; } } public static Calibration getForTimestamp(double timestamp) { Sensor sensor = Sensor.currentSensor(); return new Select() .from(Calibration.class) .where("Sensor = ? ", sensor.getId()) .where("slope_confidence != 0") .where("sensor_confidence != 0") .where("timestamp < ?", timestamp) .orderBy("timestamp desc") .executeSingle(); } public static Calibration create(double bg, Context context) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); String unit = prefs.getString("units", "mgdl"); if(unit.compareTo("mgdl") != 0 ) { bg = bg * Constants.MMOLL_TO_MGDL; } CalibrationRequest.clearAll(); Calibration calibration = new Calibration(); Sensor sensor = Sensor.currentSensor(); if (sensor != null) { BgReading bgReading = BgReading.last(); if (bgReading != null) { calibration.sensor = sensor; calibration.bg = bg; calibration.check_in = false; calibration.timestamp = new Date().getTime(); calibration.raw_value = bgReading.raw_data; calibration.adjusted_raw_value = bgReading.age_adjusted_raw_value; calibration.sensor_uuid = sensor.uuid; calibration.slope_confidence = Math.min(Math.max(((4 - Math.abs((bgReading.calculated_value_slope) * 60000))/4), 0), 1); double estimated_raw_bg = BgReading.estimated_raw_bg(new Date().getTime()); calibration.raw_timestamp = bgReading.timestamp; if (Math.abs(estimated_raw_bg - bgReading.age_adjusted_raw_value) > 20) { calibration.estimate_raw_at_time_of_calibration = bgReading.age_adjusted_raw_value; } else { calibration.estimate_raw_at_time_of_calibration = estimated_raw_bg; } calibration.distance_from_estimate = Math.abs(calibration.bg - bgReading.calculated_value); calibration.sensor_confidence = Math.max(((-0.0018 * bg * bg) + (0.6657 * bg) + 36.7505) / 100, 0); calibration.sensor_age_at_time_of_estimation = calibration.timestamp - sensor.started_at; calibration.uuid = UUID.randomUUID().toString(); calibration.save(); bgReading.calibration = calibration; bgReading.calibration_flag = true; bgReading.save(); BgSendQueue.addToQueue(bgReading, "update", context); calculate_w_l_s(); adjustRecentBgReadings(); CalibrationSendQueue.addToQueue(calibration, context); Notifications.notificationSetter(context); Calibration.requestCalibrationIfRangeTooNarrow(); } } else { Log.w("CALIBRATION", "No sensor, cant save!"); } return Calibration.last(); } public static List allForSensorInLastFiveDays() { Sensor sensor = Sensor.currentSensor(); if (sensor == null) { return null; } return new Select() .from(Calibration.class) .where("Sensor = ? ", sensor.getId()) .where("slope_confidence != 0") .where("sensor_confidence != 0") .where("timestamp > ?", (new Date().getTime() - (60000 * 60 * 24 * 5))) .orderBy("timestamp desc") .execute(); } public static void calculate_w_l_s() { if (Sensor.isActive()) { double l = 0; double m = 0; double n = 0; double p = 0; double q = 0; double w; List calibrations = allForSensorInLastFourDays(); //5 days was a bit much, dropped this to 4 if (calibrations.size() == 1) { Calibration calibration = Calibration.last(); calibration.slope = 1; calibration.intercept = calibration.bg - (calibration.raw_value * calibration.slope); calibration.save(); } else { for (Calibration calibration : calibrations) { w = calibration.calculateWeight(); l += (w); m += (w * calibration.estimate_raw_at_time_of_calibration); n += (w * calibration.estimate_raw_at_time_of_calibration * calibration.estimate_raw_at_time_of_calibration); p += (w * calibration.bg); q += (w * calibration.estimate_raw_at_time_of_calibration * calibration.bg); } Calibration last_calibration = Calibration.last(); w = (last_calibration.calculateWeight() * (calibrations.size() * 0.14)); l += (w); m += (w * last_calibration.estimate_raw_at_time_of_calibration); n += (w * last_calibration.estimate_raw_at_time_of_calibration * last_calibration.estimate_raw_at_time_of_calibration); p += (w * last_calibration.bg); q += (w * last_calibration.estimate_raw_at_time_of_calibration * last_calibration.bg); double d = (l * n) - (m * m); Calibration calibration = Calibration.last(); calibration.intercept = ((n * p) - (m * q)) / d; calibration.slope = ((l * q) - (m * p)) / d; if ((calibrations.size() == 2 && calibration.slope < 0.95) || (calibration.slope < 0.85)) { // I have not seen a case where a value below 7.5 proved to be accurate but we should keep an eye on this calibration.slope = calibration.slopeOOBHandler(0); if(calibrations.size() > 2) { calibration.possible_bad = true; } calibration.intercept = calibration.bg - (calibration.estimate_raw_at_time_of_calibration * calibration.slope); CalibrationRequest.createOffset(calibration.bg, 25); } if ((calibrations.size() == 2 && calibration.slope > 1.3) || (calibration.slope > 1.4)) { calibration.slope = calibration.slopeOOBHandler(1); if(calibrations.size() > 2) { calibration.possible_bad = true; } calibration.intercept = calibration.bg - (calibration.estimate_raw_at_time_of_calibration * calibration.slope); CalibrationRequest.createOffset(calibration.bg, 25); } Log.d(TAG, "Calculated Calibration Slope: " + calibration.slope); Log.d(TAG, "Calculated Calibration intercept: " + calibration.intercept); calibration.save(); } } else { Log.w(TAG, "NO Current active sensor found!!"); } } private double slopeOOBHandler(int status) { // If the last slope was reasonable and reasonably close, use that, otherwise use a slope that may be a little steep, but its best to play it safe when uncertain List calibrations = Calibration.latest(3); Calibration thisCalibration = calibrations.get(0); if(status == 0) { if (calibrations.size() == 3) { if ((Math.abs(thisCalibration.bg - thisCalibration.estimate_bg_at_time_of_calibration) < 30) && (calibrations.get(1).possible_bad != null && calibrations.get(1).possible_bad == true)) { return calibrations.get(1).slope; } else { return Math.max(((-0.048) * (thisCalibration.sensor_age_at_time_of_estimation / (60000 * 60 * 24))) + 1.1, 1.08); } } else if (calibrations.size() == 2) { return Math.max(((-0.048) * (thisCalibration.sensor_age_at_time_of_estimation / (60000 * 60 * 24))) + 1.1, 1.15); } return 1; } else { if (calibrations.size() == 3) { if ((Math.abs(thisCalibration.bg - thisCalibration.estimate_bg_at_time_of_calibration) < 30) && (calibrations.get(1).possible_bad != null && calibrations.get(1).possible_bad == true)) { return calibrations.get(1).slope; } else { return 1.3; } } else if (calibrations.size() == 2) { return 1.2; } } return 1; } private static List calibrations_for_sensor(Sensor sensor) { return new Select() .from(Calibration.class) .where("Sensor = ?", sensor.getId()) .where("slope_confidence != 0") .where("sensor_confidence != 0") .orderBy("timestamp desc") .execute(); } private double calculateWeight() { double firstTimeStarted = Calibration.first().sensor_age_at_time_of_estimation; double lastTimeStarted = Calibration.last().sensor_age_at_time_of_estimation; double time_percentage = Math.min(((sensor_age_at_time_of_estimation - firstTimeStarted) / (lastTimeStarted - firstTimeStarted)) / (.85), 1); time_percentage = (time_percentage + .01); Log.w(TAG, "CALIBRATIONS TIME PERCENTAGE WEIGHT: " + time_percentage); return Math.max((((((slope_confidence + sensor_confidence) * (time_percentage))) / 2) * 100), 1); } public static void adjustRecentBgReadings() {// This just adjust the last 30 bg readings transition from one calibration point to the next adjustRecentBgReadings(30); } public static void adjustRecentBgReadings(int adjustCount) { //TODO: add some handling around calibration overrides as they come out looking a bit funky List calibrations = Calibration.latest(3); List bgReadings = BgReading.latestUnCalculated(adjustCount); if (calibrations.size() == 3) { int denom = bgReadings.size(); Calibration latestCalibration = calibrations.get(0); int i = 0; for (BgReading bgReading : bgReadings) { double oldYValue = bgReading.calculated_value; double newYvalue = (bgReading.age_adjusted_raw_value * latestCalibration.slope) + latestCalibration.intercept; bgReading.calculated_value = ((newYvalue * (denom - i)) + (oldYValue * ( i ))) / denom; bgReading.save(); i += 1; } } else if (calibrations.size() == 2) { Calibration latestCalibration = calibrations.get(0); for (BgReading bgReading : bgReadings) { double newYvalue = (bgReading.age_adjusted_raw_value * latestCalibration.slope) + latestCalibration.intercept; bgReading.calculated_value = newYvalue; bgReading.save(); } } bgReadings.get(0).find_new_raw_curve(); bgReadings.get(0).find_new_curve(); } public void rawValueOverride(double rawValue, Context context) { estimate_raw_at_time_of_calibration = rawValue; save(); calculate_w_l_s(); CalibrationSendQueue.addToQueue(this, context); } public static void requestCalibrationIfRangeTooNarrow() { double max = Calibration.max_recent(); double min = Calibration.min_recent(); if ((max - min) < 55) { double avg = ((min + max) / 2); double dist = max - avg; CalibrationRequest.createOffset(avg, dist + 20); } } public static void clear_all_existing_calibrations() { CalibrationRequest.clearAll(); List pastCalibrations = Calibration.allForSensor(); if (pastCalibrations != null) { for(Calibration calibration : pastCalibrations){ calibration.slope_confidence = 0; calibration.sensor_confidence = 0; calibration.save(); } } } public String toS() { Gson gson = new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() .registerTypeAdapter(Date.class, new DateTypeAdapter()) .serializeSpecialFloatingPointValues() .create(); return gson.toJson(this); } //COMMON SCOPES! public static Calibration last() { Sensor sensor = Sensor.currentSensor(); return new Select() .from(Calibration.class) .where("Sensor = ? ", sensor.getId()) .orderBy("timestamp desc") .executeSingle(); } public static Calibration first() { Sensor sensor = Sensor.currentSensor(); return new Select() .from(Calibration.class) .where("Sensor = ? ", sensor.getId()) .where("slope_confidence != 0") .where("sensor_confidence != 0") .orderBy("timestamp asc") .executeSingle(); } public static double max_recent() { Sensor sensor = Sensor.currentSensor(); Calibration calibration = new Select() .from(Calibration.class) .where("Sensor = ? ", sensor.getId()) .where("slope_confidence != 0") .where("sensor_confidence != 0") .where("timestamp > ?", (new Date().getTime() - (60000 * 60 * 24 * 4))) .orderBy("bg desc") .executeSingle(); if(calibration != null) { return calibration.bg; } else { return 120; } } public static double min_recent() { Sensor sensor = Sensor.currentSensor(); Calibration calibration = new Select() .from(Calibration.class) .where("Sensor = ? ", sensor.getId()) .where("slope_confidence != 0") .where("sensor_confidence != 0") .where("timestamp > ?", (new Date().getTime() - (60000 * 60 * 24 * 4))) .orderBy("bg asc") .executeSingle(); if(calibration != null) { return calibration.bg; } else { return 100; } } public static List latest(int number) { Sensor sensor = Sensor.currentSensor(); if (sensor == null) { return null; } return new Select() .from(Calibration.class) .where("Sensor = ? ", sensor.getId()) .orderBy("timestamp desc") .limit(number) .execute(); } public static List allForSensor() { Sensor sensor = Sensor.currentSensor(); if (sensor == null) { return null; } return new Select() .from(Calibration.class) .where("Sensor = ? ", sensor.getId()) .where("slope_confidence != 0") .where("sensor_confidence != 0") .orderBy("timestamp desc") .execute(); } public static List allForSensorInLastFourDays() { Sensor sensor = Sensor.currentSensor(); if (sensor == null) { return null; } return new Select() .from(Calibration.class) .where("Sensor = ? ", sensor.getId()) .where("slope_confidence != 0") .where("sensor_confidence != 0") .where("timestamp > ?", (new Date().getTime() - (60000 * 60 * 24 * 4))) .orderBy("timestamp desc") .execute(); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Models/CalibrationRequest.java ================================================ package com.eveningoutpost.dexdrip.Models; import android.provider.BaseColumns; import com.activeandroid.Model; import com.activeandroid.annotation.Column; import com.activeandroid.annotation.Table; import com.activeandroid.query.Select; import java.util.List; /** * Created by stephenblack on 12/9/14. */ @Table(name = "CalibrationRequest", id = BaseColumns._ID) public class CalibrationRequest extends Model { private static final int max = 250; private static final int min = 70; @Column(name = "requestIfAbove") public double requestIfAbove; @Column(name = "requestIfBelow") public double requestIfBelow; public static void createRange(double low, double high) { CalibrationRequest calibrationRequest = new CalibrationRequest(); calibrationRequest.requestIfAbove = low; calibrationRequest.requestIfBelow = high; calibrationRequest.save(); } public static void createOffset(double center, double distance) { CalibrationRequest calibrationRequest = new CalibrationRequest(); calibrationRequest.requestIfAbove = center + distance; calibrationRequest.requestIfBelow = max; calibrationRequest.save(); calibrationRequest.requestIfAbove = min; calibrationRequest.requestIfBelow = center - distance; calibrationRequest.save(); } public static void clearAll(){ List calibrationRequests = new Select() .from(CalibrationRequest.class) .execute(); if (calibrationRequests.size() >=1) { for (CalibrationRequest calibrationRequest : calibrationRequests) { calibrationRequest.delete(); } } } public static boolean shouldRequestCalibration(BgReading bgReading){ CalibrationRequest calibrationRequest = new Select() .from(CalibrationRequest.class) .where("requestIfAbove < ?", bgReading.calculated_value) .where("requestIfBelow > ?", bgReading.calculated_value) .executeSingle(); if (calibrationRequest != null && Math.abs(bgReading.calculated_value_slope * 60000) < 1.8) { return true; } else { return false; } } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Models/TransmitterData.java ================================================ package com.eveningoutpost.dexdrip.Models; import android.provider.BaseColumns; import android.util.Log; import com.activeandroid.Model; import com.activeandroid.annotation.Column; import com.activeandroid.annotation.Table; import com.activeandroid.query.Select; import java.util.Date; import java.util.UUID; /** * Created by stephenblack on 11/6/14. */ @Table(name = "TransmitterData", id = BaseColumns._ID) public class TransmitterData extends Model { private final static String TAG = BgReading.class.getSimpleName(); @Column(name = "timestamp", index = true) public long timestamp; @Column(name = "raw_data") public double raw_data; // // @Column(name = "filtered_data") // public double filtered_data; @Column(name = "sensor_battery_level") public int sensor_battery_level; @Column(name = "uuid", index = true) public String uuid; public static TransmitterData create(byte[] buffer, int len, Long timestamp) { StringBuilder data_string = new StringBuilder(); if (len < 6) { return null; }; for (int i = 0; i < len; ++i) { data_string.append((char) buffer[i]); } String[] data = data_string.toString().split("\\s+"); randomDelay(100, 2000); TransmitterData lastTransmitterData = TransmitterData.last(); if (lastTransmitterData != null && lastTransmitterData.raw_data == Integer.parseInt(data[0]) && Math.abs(lastTransmitterData.timestamp - timestamp) < (10000)) { //Stop allowing duplicate data, its bad! return null; } TransmitterData transmitterData = new TransmitterData(); if(data.length > 1) { transmitterData.sensor_battery_level = Integer.parseInt(data[1]); } if (Integer.parseInt(data[0]) < 1000) { return null; } // Sometimes the HM10 sends the battery level and readings in separate transmissions, filter out these incomplete packets! transmitterData.raw_data = Integer.parseInt(data[0]); transmitterData.timestamp = timestamp; transmitterData.uuid = UUID.randomUUID().toString(); transmitterData.save(); return transmitterData; } public static TransmitterData create(int raw_data ,int sensor_battery_level, long timestamp) { randomDelay(100, 2000); TransmitterData lastTransmitterData = TransmitterData.last(); if (lastTransmitterData != null && lastTransmitterData.raw_data == raw_data && Math.abs(lastTransmitterData.timestamp - new Date().getTime()) < (10000)) { //Stop allowing duplicate data, its bad! return null; } TransmitterData transmitterData = new TransmitterData(); transmitterData.sensor_battery_level = sensor_battery_level; transmitterData.raw_data = raw_data ; transmitterData.timestamp = timestamp; transmitterData.uuid = UUID.randomUUID().toString(); transmitterData.save(); return transmitterData; } public static TransmitterData last() { return new Select() .from(TransmitterData.class) .orderBy("_ID desc") .executeSingle(); } public static void randomDelay(float min, float max){ int random = (int)(max * Math.random() + min); try { Log.d("Sleeping ", "for " + random + "ms"); Thread.sleep(random); } catch (InterruptedException e) { Log.e("Random Delay ", "INTERUPTED"); } } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Models/User.java ================================================ package com.eveningoutpost.dexdrip.Models; import android.provider.BaseColumns; import android.util.Log; import com.activeandroid.Model; import com.activeandroid.annotation.Column; import com.activeandroid.annotation.Table; import com.activeandroid.query.Select; import com.eveningoutpost.dexdrip.Interfaces.UserInterface; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; import com.google.gson.annotations.Expose; import com.google.gson.internal.bind.DateTypeAdapter; import java.util.Date; import retrofit.Callback; import retrofit.RestAdapter; import retrofit.RetrofitError; import retrofit.client.Response; import retrofit.converter.GsonConverter; /** * Created by stephenblack on 11/7/14. */ @Table(name = "User", id = BaseColumns._ID) public class User extends Model { private static final String baseUrl = "http://10.0.2.2:3000"; public static Gson gson = new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() .registerTypeAdapter(Date.class, new DateTypeAdapter()) .create(); @Expose @Column(name = "email") public String email; @Expose @Column(name = "password") public String password; @Expose @Column(name = "token") public String token; @Expose @Column(name = "token_expiration") public double token_expiration; @Expose @Column(name = "uuid", index = true) public String uuid; public static User currentUser() { User user = new Select() .from(User.class) .orderBy("_ID desc") .limit(1) .executeSingle(); return user; } //TODO: Add refresh token attempt instance method!! public static void authenticate() { final User user = User.currentUser(); userInterface().authenticate(user, new Callback() { @Override public void success(String gsonResponse, Response response) { JsonObject jobj = new Gson().fromJson(gsonResponse, JsonObject.class); user.token = jobj.get("token").getAsString(); user.token_expiration = jobj.get("expiration").getAsDouble(); user.save(); } @Override public void failure(RetrofitError error) { Response response = error.getResponse(); Log.w("REST CALL ERROR:", "****************"); Log.w("REST CALL STATUS:", "" + response.getStatus()); Log.w("REST CALL REASON:", response.getReason()); } } ); } public static UserInterface userInterface() { RestAdapter adapter = adapterBuilder().build(); UserInterface userInterface = adapter.create(UserInterface.class); return userInterface(); } public static RestAdapter.Builder adapterBuilder() { RestAdapter.Builder adapterBuilder = new RestAdapter.Builder(); adapterBuilder .setEndpoint(baseUrl) .setConverter(new GsonConverter(gson)); return adapterBuilder; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Models/UserNotification.java ================================================ package com.eveningoutpost.dexdrip.Models; import android.provider.BaseColumns; import com.activeandroid.Model; import com.activeandroid.annotation.Column; import com.activeandroid.annotation.Table; import com.activeandroid.query.Select; import java.util.Date; /** * Created by stephenblack on 11/29/14. */ @Table(name = "Notifications", id = BaseColumns._ID) public class UserNotification extends Model { @Column(name = "timestamp", index = true) public double timestamp; @Column(name = "message") public String message; @Column(name = "bg_alert") public boolean bg_alert; @Column(name = "calibration_alert") public boolean calibration_alert; @Column(name = "double_calibration_alert") public boolean double_calibration_alert; @Column(name = "extra_calibration_alert") public boolean extra_calibration_alert; public static UserNotification lastBgAlert() { return new Select() .from(UserNotification.class) .where("bg_alert = ?", true) .orderBy("_ID desc") .executeSingle(); } public static UserNotification lastCalibrationAlert() { return new Select() .from(UserNotification.class) .where("calibration_alert = ?", true) .orderBy("_ID desc") .executeSingle(); } public static UserNotification lastDoubleCalibrationAlert() { return new Select() .from(UserNotification.class) .where("double_calibration_alert = ?", true) .orderBy("_ID desc") .executeSingle(); } public static UserNotification lastExtraCalibrationAlert() { return new Select() .from(UserNotification.class) .where("extra_calibration_alert = ?", true) .orderBy("_ID desc") .executeSingle(); } public static UserNotification create(String message, String type) { UserNotification userNotification = new UserNotification(); userNotification.timestamp = new Date().getTime(); userNotification.message = message; if (type == "bg_alert") { userNotification.bg_alert = true; } else if (type == "calibration_alert") { userNotification.calibration_alert = true; } else if (type == "double_calibration_alert") { userNotification.double_calibration_alert = true; } else if (type == "extra_calibration_alert") { userNotification.extra_calibration_alert = true; } userNotification.save(); return userNotification; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/NavDrawerBuilder.java ================================================ package com.eveningoutpost.dexdrip; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.preference.Preference; import android.preference.PreferenceManager; import com.eveningoutpost.dexdrip.Models.BgReading; import com.eveningoutpost.dexdrip.Models.Calibration; import com.eveningoutpost.dexdrip.Tables.BgReadingTable; import com.eveningoutpost.dexdrip.Tables.CalibrationDataTable; import com.eveningoutpost.dexdrip.UtilityModels.CollectionServiceStarter; import com.eveningoutpost.dexdrip.utils.Preferences; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * Created by stephenblack on 11/5/14. */ public class NavDrawerBuilder { public final List last_two_calibrations = Calibration.latest(2); public final List last_two_bgReadings = BgReading.latestUnCalculated(2); public final List bGreadings_in_last_30_mins = BgReading.last30Minutes(); public final boolean is_active_sensor = Sensor.isActive(); public final double time_now = new Date().getTime(); public List nav_drawer_intents; public List nav_drawer_options; public Context context; public NavDrawerBuilder(Context aContext) { context = aContext; this.nav_drawer_intents = nav_drawer_intents(); this.nav_drawer_options = nav_drawer_options(); } private final List nav_drawer_options() { List options = new ArrayList(); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); boolean IUnderstand = prefs.getBoolean("I_understand", false); if(IUnderstand == false) { options.add("Settings"); return options; } options.add("xDrip"); if(is_active_sensor) { options.add("Calibration Graph"); } options.add("BG Data Table"); options.add("Calibration Data Table"); // options.add("Sensor Data Table"); if(is_active_sensor) { if(!CollectionServiceStarter.isBTShare(context)) { if (last_two_bgReadings.size() > 1) { if (last_two_calibrations.size() > 1) { if (bGreadings_in_last_30_mins.size() >= 2) { if (time_now - last_two_calibrations.get(0).timestamp < (1000 * 60 * 60)) { //Put steps in place to discourage over calibration options.add("Override Calibration"); } else { options.add("Add Calibration"); } } else { options.add("Cannot Calibrate right now"); } if (last_two_calibrations.get(0).slope >= 1.4 || last_two_calibrations.get(0).slope <= 0.5) { options.add("Add Double Calibration"); } } else { options.add("Add Double Calibration"); } } } options.add("Stop Sensor"); } else { options.add("Start Sensor"); } if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) { if(CollectionServiceStarter.isBTWixel(context) || CollectionServiceStarter.isBTShare(context)) { options.add("Scan for BT"); } } options.add("System Status"); options.add("Settings"); // options.add("Fake Numbers"); // options.add("Add Double Calibration"); // options.add("Share Test"); return options; } private List nav_drawer_intents() { List options = new ArrayList(); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); boolean IUnderstand = prefs.getBoolean("I_understand", false); if(IUnderstand == false) { options.add(new Intent(context, SettingsActivity.class)); return options; } options.add(new Intent(context, Home.class)); if(is_active_sensor) { options.add(new Intent(context, CalibrationGraph.class)); } options.add(new Intent(context, BgReadingTable.class)); options.add(new Intent(context, CalibrationDataTable.class)); // options.add(new Intent(context, SensorDataTable.class)); if(is_active_sensor) { if(!CollectionServiceStarter.isBTShare(context)) { if (last_two_bgReadings.size() > 1) { if (last_two_calibrations.size() > 1) { if (bGreadings_in_last_30_mins.size() >= 2) { if (time_now - last_two_calibrations.get(0).timestamp < (1000 * 60 * 60)) { //Put steps in place to discourage over calibration options.add(new Intent(context, CalibrationOverride.class)); } else { options.add(new Intent(context, AddCalibration.class)); } } else { options.add(new Intent(context, Home.class)); } if (last_two_calibrations.get(0).slope >= 1.4 || last_two_calibrations.get(0).slope <= 0.5) { options.add(new Intent(context, DoubleCalibrationActivity.class)); } } else { options.add(new Intent(context, DoubleCalibrationActivity.class)); } } } options.add(new Intent(context, StopSensor.class)); } else { options.add(new Intent(context, StartNewSensor.class)); } if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) { if(CollectionServiceStarter.isBTWixel(context) || CollectionServiceStarter.isBTShare(context)) { options.add(new Intent(context, BluetoothScan.class)); } } options.add(new Intent(context, SystemStatus.class)); // options.add(new Intent(context, SettingsActivity.class)); options.add(new Intent(context, Preferences.class)); // options.add(new Intent(context, FakeNumbers.class)); // options.add(new Intent(context, DoubleCalibrationActivity.class)); // options.add(new Intent(context, ShareTest.class)); return options; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/NavigationDrawerFragment.java ================================================ package com.eveningoutpost.dexdrip; import android.app.ActionBar; import android.app.Activity; import android.app.Fragment; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v4.app.ActionBarDrawerToggle; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; import java.util.List; public class NavigationDrawerFragment extends Fragment { private static final String STATE_SELECTED_POSITION = "selected_navigation_drawer_position"; private static final String PREF_USER_LEARNED_DRAWER = "navigation_drawer_learned"; private NavigationDrawerCallbacks mCallbacks; private ActionBarDrawerToggle mDrawerToggle; private DrawerLayout mDrawerLayout; private ListView mDrawerListView; private View mFragmentContainerView; private int mCurrentSelectedPosition = 0; private boolean mFromSavedInstanceState; private boolean mUserLearnedDrawer; private String menu_name; public NavDrawerBuilder navDrawerBuilder; private int menu_position; private List menu_option_list; private List intent_list; public NavigationDrawerFragment() { } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity()); sp.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).apply(); mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false); if (savedInstanceState != null) { mCurrentSelectedPosition = 0; mFromSavedInstanceState = true; } } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setHasOptionsMenu(true); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mDrawerListView = (ListView) inflater.inflate( R.layout.fragment_navigation_drawer, container, false); mDrawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { selectItem(position); } }); navDrawerBuilder = new NavDrawerBuilder(getActivity()); List menu_option_list = navDrawerBuilder.nav_drawer_options; String[] menu_options = menu_option_list.toArray(new String[menu_option_list.size()]); mDrawerListView.setAdapter(new ArrayAdapter( getActionBar().getThemedContext(), android.R.layout.simple_list_item_activated_1, android.R.id.text1, menu_options )); return mDrawerListView; } public boolean isDrawerOpen() { return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(mFragmentContainerView); } public void setUp(int fragmentId, DrawerLayout drawerLayout, String current_activity, Context context) { navDrawerBuilder = new NavDrawerBuilder(context); menu_name = current_activity; menu_option_list = navDrawerBuilder.nav_drawer_options; String[] menu_options = menu_option_list.toArray(new String[menu_option_list.size()]); menu_position = menu_option_list.indexOf(menu_name); intent_list = navDrawerBuilder.nav_drawer_intents; mFragmentContainerView = getActivity().findViewById(fragmentId); mDrawerLayout = drawerLayout; mCurrentSelectedPosition = menu_position; mDrawerListView.setItemChecked(mCurrentSelectedPosition, true); mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); ActionBar actionBar = getActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setHomeButtonEnabled(true); // mDrawerListView = (ListView) inflater.inflate( // R.layout.fragment_navigation_drawer, container, false); // mDrawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { // @Override // public void onItemClick(AdapterView parent, View view, int position, long id) { // selectItem(position); // } // }); // navDrawerBuilder = new NavDrawerBuilder(); // List menu_option_list = navDrawerBuilder.nav_drawer_options(); // String[] menu_options = menu_option_list.toArray(new String[menu_option_list.size()]); mDrawerListView.setAdapter(new ArrayAdapter( getActionBar().getThemedContext(), android.R.layout.simple_list_item_activated_1, android.R.id.text1, menu_options )); mDrawerToggle = new ActionBarDrawerToggle( getActivity(), mDrawerLayout, R.drawable.ic_drawer, R.string.navigation_drawer_open, R.string.navigation_drawer_close ) { @Override public void onDrawerClosed(View drawerView) { super.onDrawerClosed(drawerView); if (!isAdded()) { return; } getActivity().invalidateOptionsMenu(); } @Override public void onDrawerOpened(View drawerView) { super.onDrawerOpened(drawerView); if (!isAdded()) { return; } if (!mUserLearnedDrawer) { mUserLearnedDrawer = true; SharedPreferences sp = PreferenceManager .getDefaultSharedPreferences(getActivity()); sp.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).apply(); } getActivity().invalidateOptionsMenu(); // calls onPrepareOptionsMenu() } }; if (!mUserLearnedDrawer && !mFromSavedInstanceState) { mDrawerLayout.openDrawer(mFragmentContainerView); } mDrawerLayout.post(new Runnable() { @Override public void run() { mDrawerToggle.syncState(); } }); mDrawerLayout.setDrawerListener(mDrawerToggle); } private void selectItem(int position) { mCurrentSelectedPosition = position; if (mDrawerListView != null) { mDrawerListView.setItemChecked(position, true); } if (mDrawerLayout != null) { mDrawerLayout.closeDrawer(mFragmentContainerView); } if (mCallbacks != null) { mCallbacks.onNavigationDrawerItemSelected(position); } } @Override public void onAttach(Activity activity) { super.onAttach(activity); try { mCallbacks = (NavigationDrawerCallbacks) activity; } catch (ClassCastException e) { throw new ClassCastException("Activity must implement NavigationDrawerCallbacks."); } } @Override public void onDetach() { super.onDetach(); mCallbacks = null; } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt(STATE_SELECTED_POSITION, mCurrentSelectedPosition); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); mDrawerToggle.onConfigurationChanged(newConfig); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { if (mDrawerLayout != null && isDrawerOpen()) { inflater.inflate(R.menu.global, menu); showGlobalContextActionBar(); } super.onCreateOptionsMenu(menu, inflater); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (mDrawerToggle.onOptionsItemSelected(item)) { return true; } return super.onOptionsItemSelected(item); } private void showGlobalContextActionBar() { ActionBar actionBar = getActionBar(); actionBar.setDisplayShowTitleEnabled(true); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); actionBar.setTitle(R.string.app_name); } private ActionBar getActionBar() { return getActivity().getActionBar(); } public static interface NavigationDrawerCallbacks { void onNavigationDrawerItemSelected(int position); } public void swapContext(int position) { if (position != menu_position) { Intent[] intent_array = intent_list.toArray(new Intent[intent_list.size()]); startActivity(intent_array[position]); if(menu_position != 0) { getActivity().finish(); } } } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Sensor.java ================================================ package com.eveningoutpost.dexdrip; import android.provider.BaseColumns; import android.util.Log; import com.activeandroid.Model; import com.activeandroid.annotation.Column; import com.activeandroid.annotation.Table; import com.activeandroid.query.Select; import com.eveningoutpost.dexdrip.UtilityModels.SensorSendQueue; import java.util.UUID; /** * Created by stephenblack on 10/29/14. */ @Table(name = "Sensors", id = BaseColumns._ID) public class Sensor extends Model { // @Expose @Column(name = "started_at", index = true) public double started_at; // @Expose @Column(name = "stopped_at") public double stopped_at; // @Expose @Column(name = "latest_battery_level") public int latest_battery_level; // @Expose @Column(name = "uuid", index = true) public String uuid; public static Sensor create(double started_at) { Sensor sensor = new Sensor(); sensor.started_at = started_at; sensor.uuid = UUID.randomUUID().toString(); sensor.save(); SensorSendQueue.addToQueue(sensor); Log.w("SENSOR MODEL:", sensor.toString()); return sensor; } public static Sensor currentSensor() { Sensor sensor = new Select() .from(Sensor.class) .where("started_at != 0") .where("stopped_at = 0") .orderBy("_ID desc") .limit(1) .executeSingle(); return sensor; } public static boolean isActive() { Sensor sensor = new Select() .from(Sensor.class) .where("started_at != 0") .where("stopped_at = 0") .orderBy("_ID desc") .limit(1) .executeSingle(); if(sensor == null) { return false; } else { return true; } } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Services/ComunicationHeader.java ================================================ package com.eveningoutpost.dexdrip.Services; import java.io.Serializable; import com.google.gson.annotations.Expose; // This is a struct that is supsoed to tell the protocol version and is the first that the client is sending // The complete protocol is: // 1) the client connects // 2) send this message. // 3) the server will send numberOfRecords of type ???? that it has. public class ComunicationHeader { /** * */ int version; int numberOfRecords; // String message; // byte reserved[]; } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Services/DexCollectionService.java ================================================ /* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.eveningoutpost.dexdrip.Services; import android.annotation.TargetApi; import android.app.AlarmManager; import android.app.PendingIntent; import android.app.Service; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Binder; import android.os.Build; import android.os.IBinder; import android.os.PowerManager; import android.preference.PreferenceManager; import android.util.Log; import com.activeandroid.query.Select; import com.eveningoutpost.dexdrip.Models.ActiveBluetoothDevice; import com.eveningoutpost.dexdrip.Models.BgReading; import com.eveningoutpost.dexdrip.Sensor; import com.eveningoutpost.dexdrip.UtilityModels.CollectionServiceStarter; import com.eveningoutpost.dexdrip.UtilityModels.ForegroundServiceStarter; import com.eveningoutpost.dexdrip.UtilityModels.HM10Attributes; import com.eveningoutpost.dexdrip.Models.TransmitterData; import java.util.Calendar; import java.util.Date; import java.util.UUID; @TargetApi(Build.VERSION_CODES.KITKAT) public class DexCollectionService extends Service { private final static String TAG = DexCollectionService.class.getSimpleName(); private String mDeviceName; private String mDeviceAddress; private boolean is_connected = false; SharedPreferences prefs; public DexCollectionService dexCollectionService; private BluetoothManager mBluetoothManager; private BluetoothAdapter mBluetoothAdapter; private String mBluetoothDeviceAddress; private BluetoothGatt mBluetoothGatt; private ForegroundServiceStarter foregroundServiceStarter; private int mConnectionState = STATE_DISCONNECTED; private BluetoothDevice device; private Context mContext = null; private static final int STATE_DISCONNECTED = BluetoothProfile.STATE_DISCONNECTED; private static final int STATE_DISCONNECTING = BluetoothProfile.STATE_DISCONNECTING; private static final int STATE_CONNECTING = BluetoothProfile.STATE_CONNECTING; private static final int STATE_CONNECTED = BluetoothProfile.STATE_CONNECTED; public final static String ACTION_DATA_AVAILABLE = "com.example.bluetooth.le.ACTION_DATA_AVAILABLE"; public final static UUID xDripDataService = UUID.fromString(HM10Attributes.HM_10_SERVICE); public final static UUID xDripDataCharacteristic = UUID.fromString(HM10Attributes.HM_RX_TX); @Override public IBinder onBind(Intent intent) { throw new UnsupportedOperationException("Not yet implemented"); } @Override public void onCreate() { foregroundServiceStarter = new ForegroundServiceStarter(getApplicationContext(), this); foregroundServiceStarter.start(); mContext = getApplicationContext(); dexCollectionService = this; prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); listenForChangeInSettings(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT){ stopSelf(); return START_NOT_STICKY; } if (CollectionServiceStarter.isBTWixel(getApplicationContext())) { setFailoverTimer(); } else { stopSelf(); return START_NOT_STICKY; } attemptConnection(); return START_STICKY; } @Override public void onDestroy() { super.onDestroy(); close(); foregroundServiceStarter.stop(); setRetryTimer(); Log.w(TAG, "SERVICE STOPPED"); } public SharedPreferences.OnSharedPreferenceChangeListener prefListener = new SharedPreferences.OnSharedPreferenceChangeListener() { public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { if(key.compareTo("run_service_in_foreground") == 0) { Log.e("FOREGROUND", "run_service_in_foreground changed!"); if (prefs.getBoolean("run_service_in_foreground", false)) { foregroundServiceStarter = new ForegroundServiceStarter(getApplicationContext(), dexCollectionService); foregroundServiceStarter.start(); Log.w(TAG, "Moving to foreground"); } else { dexCollectionService.stopForeground(true); Log.w(TAG, "Removing from foreground"); } } } }; public void listenForChangeInSettings() { prefs.registerOnSharedPreferenceChangeListener(prefListener); } public void setRetryTimer() { if (CollectionServiceStarter.isBTWixel(getApplicationContext())) { long retry_in = (1000 * 60 * 2); Log.d(TAG, "Restarting in: " + (retry_in / (60 * 1000)) + " minutes"); Calendar calendar = Calendar.getInstance(); AlarmManager alarm = (AlarmManager) getSystemService(ALARM_SERVICE); alarm.setExact(alarm.RTC_WAKEUP, calendar.getTimeInMillis() + retry_in, PendingIntent.getService(this, 0, new Intent(this, DexCollectionService.class), 0)); } } public void setFailoverTimer() { //Sometimes it gets stuck in limbo on 4.4, this should make it try again if (CollectionServiceStarter.isBTWixel(getApplicationContext())) { long retry_in = (1000 * 60 * 5); Log.d(TAG, "Fallover Restarting in: " + (retry_in / (60 * 1000)) + " minutes"); Calendar calendar = Calendar.getInstance(); AlarmManager alarm = (AlarmManager) getSystemService(ALARM_SERVICE); alarm.set(alarm.RTC_WAKEUP, calendar.getTimeInMillis() + retry_in, PendingIntent.getService(this, 0, new Intent(this, DexCollectionService.class), 0)); } else { stopSelf(); } } public void attemptConnection() { mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); if (mBluetoothManager != null) { mBluetoothAdapter = mBluetoothManager.getAdapter(); if (mBluetoothAdapter != null) { if (device != null) { mConnectionState = STATE_DISCONNECTED; for (BluetoothDevice bluetoothDevice : mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT)) { if (bluetoothDevice.getAddress().compareTo(device.getAddress()) == 0) { mConnectionState = STATE_CONNECTED; } } } Log.w(TAG, "Connection state: " + mConnectionState); if (mConnectionState == STATE_DISCONNECTED || mConnectionState == STATE_DISCONNECTING) { ActiveBluetoothDevice btDevice = ActiveBluetoothDevice.first(); if (btDevice != null) { mDeviceName = btDevice.name; mDeviceAddress = btDevice.address; if (mBluetoothAdapter.isEnabled() && mBluetoothAdapter.getRemoteDevice(mDeviceAddress) != null) { connect(mDeviceAddress); return; } } } else if (mConnectionState == STATE_CONNECTED) { //WOOO, we are good to go, nothing to do here! Log.w(TAG, "Looks like we are already connected, going to read!"); return; } } } setRetryTimer(); } private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (newState == BluetoothProfile.STATE_CONNECTED) { mConnectionState = STATE_CONNECTED; ActiveBluetoothDevice.connected(); Log.w(TAG, "Connected to GATT server."); mBluetoothGatt.discoverServices(); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { mConnectionState = STATE_DISCONNECTED; ActiveBluetoothDevice.disconnected(); Log.w(TAG, "Disconnected from GATT server."); setRetryTimer(); } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { BluetoothGattService gattService = mBluetoothGatt.getService(xDripDataService); if (gattService != null) { BluetoothGattCharacteristic gattCharacteristic = gattService.getCharacteristic(xDripDataCharacteristic); if (gattCharacteristic != null ) { final int charaProp = gattCharacteristic.getProperties(); if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) { mBluetoothGatt.setCharacteristicNotification(gattCharacteristic, true); } else { Log.e(TAG, "characteristic " + gattCharacteristic.getUuid() + " doesn't have notify properties"); } } else { Log.e(TAG, "characteristic " + xDripDataCharacteristic + " not found"); } } else { Log.e(TAG, "service " + xDripDataCharacteristic + " not found"); } } } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { final byte[] data = characteristic.getValue(); if (data != null && data.length > 0) { setSerialDataToTransmitterRawData(data, data.length); } } }; public boolean connect(final String address) { Log.w(TAG, "going to connect to device at address" + address); if (mBluetoothAdapter == null || address == null) { Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); setRetryTimer(); return false; } if (mBluetoothGatt != null) { Log.w(TAG, "BGatt isnt null, Closing."); mBluetoothGatt.close(); mBluetoothGatt = null; } device = mBluetoothAdapter.getRemoteDevice(address); if (device == null) { Log.w(TAG, "Device not found. Unable to connect."); setRetryTimer(); return false; } Log.w(TAG, "Trying to create a new connection."); mBluetoothGatt = device.connectGatt(getApplicationContext(), true, mGattCallback); mConnectionState = STATE_CONNECTING; return true; } public void disconnect() { if ( mBluetoothGatt == null) { return; } mBluetoothGatt.disconnect(); Log.d(TAG, "Gatt Disconnect"); } public void close() { if (mBluetoothGatt == null) { return; } mBluetoothGatt.close(); setRetryTimer(); mBluetoothGatt = null; mConnectionState = STATE_DISCONNECTED; } public void setSerialDataToTransmitterRawData(byte[] buffer, int len) { Log.w(TAG, "received some data!"); PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ReceivedReading"); wakeLock.acquire(); Long timestamp = new Date().getTime(); TransmitterData transmitterData = TransmitterData.create(buffer, len, timestamp); if (transmitterData != null) { Sensor sensor = Sensor.currentSensor(); if (sensor != null) { sensor.latest_battery_level = transmitterData.sensor_battery_level; sensor.save(); BgReading.create(transmitterData.raw_data, this, timestamp); } else { Log.w(TAG, "No Active Sensor, Data only stored in Transmitter Data"); } } wakeLock.release(); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Services/DexShareCollectionService.java ================================================ package com.eveningoutpost.dexdrip.Services; import android.annotation.TargetApi; import android.app.AlarmManager; import android.app.PendingIntent; import android.app.Service; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.preference.PreferenceManager; import android.util.Log; import android.widget.Toast; import com.activeandroid.query.Select; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.ReadDataShare; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.CalRecord; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.EGVRecord; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.SensorRecord; import com.eveningoutpost.dexdrip.Models.ActiveBluetoothDevice; import com.eveningoutpost.dexdrip.Models.BgReading; import com.eveningoutpost.dexdrip.Models.Calibration; import com.eveningoutpost.dexdrip.Sensor; import com.eveningoutpost.dexdrip.UtilityModels.CollectionServiceStarter; import com.eveningoutpost.dexdrip.UtilityModels.DexShareAttributes; import com.eveningoutpost.dexdrip.UtilityModels.ForegroundServiceStarter; import com.eveningoutpost.dexdrip.UtilityModels.HM10Attributes; import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.UUID; import java.util.logging.Logger; import rx.Observable; import rx.functions.Action1; @TargetApi(Build.VERSION_CODES.KITKAT) public class DexShareCollectionService extends Service { private final static String TAG = DexShareCollectionService.class.getSimpleName(); private ForegroundServiceStarter foregroundServiceStarter; private String mDeviceAddress; private String mDeviceName; private boolean is_connected = false; private boolean reconnecting = false; private BluetoothManager mBluetoothManager; private BluetoothAdapter mBluetoothAdapter; private BluetoothGatt mBluetoothGatt; private String mBluetoothDeviceAddress; private int mConnectionState = STATE_DISCONNECTED; private BluetoothDevice device; private static final int STATE_DISCONNECTED = BluetoothProfile.STATE_DISCONNECTED; private static final int STATE_DISCONNECTING = BluetoothProfile.STATE_DISCONNECTING; private static final int STATE_CONNECTING = BluetoothProfile.STATE_CONNECTING; private static final int STATE_CONNECTED = BluetoothProfile.STATE_CONNECTED; private BluetoothGattService mShareService; private BluetoothGattCharacteristic mAuthenticationCharacteristic; private BluetoothGattCharacteristic mSendDataCharacteristic; private BluetoothGattCharacteristic mReceiveDataCharacteristic; private BluetoothGattCharacteristic mCommandCharacteristic; private BluetoothGattCharacteristic mResponseCharacteristic; private BluetoothGattCharacteristic mHeartBeatCharacteristic; //Gatt Tasks public final int GATT_NOTHING = 0; public final int GATT_SETUP = 1; public final int GATT_WRITING_COMMANDS = 2; public final int GATT_READING_RESPONSE = 3; public int successfulWrites; //RXJAVA FUN Action1 mDataResponseListener; public int currentGattTask; public int step; public List writePackets; public int recordType; SharedPreferences prefs; ReadDataShare readData; public boolean state_authSucess = false; public boolean state_authInProgress = false; public boolean state_notifSetupSucess = false; public boolean shouldDisconnect = false; public boolean share2 = false; public Service service; @Override public void onCreate() { super.onCreate(); readData = new ReadDataShare(this); service = this; foregroundServiceStarter = new ForegroundServiceStarter(getApplicationContext(), service); foregroundServiceStarter.start(); final IntentFilter bondintent = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED); registerReceiver(mPairReceiver, bondintent); prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); listenForChangeInSettings(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT){ stopSelf(); return START_NOT_STICKY; } if (CollectionServiceStarter.isBTShare(getApplicationContext())) { setFailoverTimer(); } else { stopSelf(); return START_NOT_STICKY; } if (Sensor.currentSensor() == null) { setRetryTimer(); return START_NOT_STICKY; } Log.w(TAG, "STARTING SERVICE"); attemptConnection(); return START_STICKY; } @Override public void onDestroy() { super.onDestroy(); close(); setRetryTimer(); foregroundServiceStarter.stop(); unregisterReceiver(mPairReceiver); Log.w(TAG, "SERVICE STOPPED"); } public SharedPreferences.OnSharedPreferenceChangeListener prefListener = new SharedPreferences.OnSharedPreferenceChangeListener() { public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { if(key.compareTo("run_service_in_foreground") == 0) { Log.e("FOREGROUND", "run_service_in_foreground changed!"); if (prefs.getBoolean("run_service_in_foreground", false)) { foregroundServiceStarter = new ForegroundServiceStarter(getApplicationContext(), service); foregroundServiceStarter.start(); Log.w(TAG, "Moving to foreground"); } else { service.stopForeground(true); Log.w(TAG, "Removing from foreground"); } } } }; public void listenForChangeInSettings() { prefs.registerOnSharedPreferenceChangeListener(prefListener); } public void setRetryTimer() { if (CollectionServiceStarter.isBTShare(getApplicationContext())) { BgReading bgReading = BgReading.last(); long retry_in; if (bgReading != null) { retry_in = Math.min(Math.max((1000 * 30), (1000 * 60 * 5) - (new Date().getTime() - bgReading.timestamp) - (1000 * 15)), (1000 * 60 * 5)); } else { retry_in = (1000 * 20); } Log.d(TAG, "Restarting in: " + (retry_in / (60 * 1000)) + " minutes"); Calendar calendar = Calendar.getInstance(); AlarmManager alarm = (AlarmManager) getSystemService(ALARM_SERVICE); alarm.setExact(alarm.RTC_WAKEUP, calendar.getTimeInMillis() + retry_in, PendingIntent.getService(this, 0, new Intent(this, DexShareCollectionService.class), 0)); } } public void setFailoverTimer() { //Sometimes it gets stuck in limbo on 4.4, this should make it try again if (CollectionServiceStarter.isBTShare(getApplicationContext())) { long retry_in = (1000 * 60 * 5); Log.d(TAG, "Fallover Restarting in: " + (retry_in / (60 * 1000)) + " minutes"); Calendar calendar = Calendar.getInstance(); AlarmManager alarm = (AlarmManager) getSystemService(ALARM_SERVICE); alarm.set(alarm.RTC_WAKEUP, calendar.getTimeInMillis() + retry_in, PendingIntent.getService(this, 0, new Intent(this, DexShareCollectionService.class), 0)); } else { stopSelf(); } } @Override public IBinder onBind(Intent intent) { throw new UnsupportedOperationException("Not yet implemented"); } public void attemptConnection() { mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); if (mBluetoothManager != null) { if (device != null) { mConnectionState = STATE_DISCONNECTED; for (BluetoothDevice bluetoothDevice : mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT)) { if (bluetoothDevice.getAddress().compareTo(device.getAddress()) == 0) { mConnectionState = STATE_CONNECTED; } } } Log.w(TAG, "Connection state: " + mConnectionState); if (mConnectionState == STATE_DISCONNECTED || mConnectionState == STATE_DISCONNECTING) { ActiveBluetoothDevice btDevice = ActiveBluetoothDevice.first(); if (btDevice != null) { mDeviceName = btDevice.name; mDeviceAddress = btDevice.address; mBluetoothAdapter = mBluetoothManager.getAdapter(); if (mBluetoothAdapter.isEnabled() && mBluetoothAdapter.getRemoteDevice(mDeviceAddress) != null) { connect(mDeviceAddress); return; } else { Log.w(TAG, "Bluetooth is disabled or BT device cant be found"); setRetryTimer(); return; } } else { Log.w(TAG, "No bluetooth device to try and connect to"); setRetryTimer(); return; } } else if (mConnectionState == STATE_CONNECTED) { Log.w(TAG, "Looks like we are already connected, going to read!"); attemptRead(); return; } else { setRetryTimer(); return; } } else { setRetryTimer(); return; } } public void attemptRead() { Log.d(TAG, "Attempting to read data"); final Action1 systemTimeListener = new Action1() { @Override public void call(Long s) { if (s != null) { Log.d(TAG, "Made the full round trip, got " + s + " as the system time"); final long addativeSystemTimeOffset = new Date().getTime() - s; final Action1 dislpayTimeListener = new Action1() { @Override public void call(Long s) { if (s != null) { Log.d(TAG, "Made the full round trip, got " + s + " as the display time offset"); final long addativeDisplayTimeOffset = addativeSystemTimeOffset - (s*1000); Log.d(TAG, "Making " + addativeDisplayTimeOffset + " the the total time offset"); final Action1 evgRecordListener = new Action1() { @Override public void call(EGVRecord[] egvRecords) { if (egvRecords != null) { Log.d(TAG, "Made the full round trip, got " + egvRecords.length + " EVG Records"); BgReading.create(egvRecords, addativeSystemTimeOffset, getApplicationContext()); if (shouldDisconnect) { stopSelf(); } else { setRetryTimer(); } } } }; final Action1 sensorRecordListener = new Action1() { @Override public void call(SensorRecord[] sensorRecords) { if (sensorRecords != null) { Log.d(TAG, "Made the full round trip, got " + sensorRecords.length + " Sensor Records"); BgReading.create(sensorRecords, addativeSystemTimeOffset, getApplicationContext()); readData.getRecentEGVs(evgRecordListener); } } }; final Action1 calRecordListener = new Action1() { @Override public void call(CalRecord[] calRecords) { if (calRecords != null) { Log.d(TAG, "Made the full round trip, got " + calRecords.length + " Cal Records"); Calibration.create(calRecords, addativeDisplayTimeOffset, getApplicationContext()); readData.getRecentSensorRecords(sensorRecordListener); } } }; readData.getRecentCalRecords(calRecordListener); } } }; readData.readDisplayTimeOffset(dislpayTimeListener); } } }; readData.readSystemTime(systemTimeListener); } public boolean connect(final String address) { Log.w(TAG, "going to connect to device at address" + address); if (mBluetoothAdapter == null || address == null) { Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); setRetryTimer(); return false; } if (mBluetoothGatt != null) { Log.w(TAG, "BGatt isnt null, Closing."); mBluetoothGatt.close(); mBluetoothGatt = null; } for (BluetoothDevice bluetoothDevice : mBluetoothAdapter.getBondedDevices()) { if (bluetoothDevice.getAddress().compareTo(address) == 0) { Log.v(TAG, "Device found, already bonded, going to connect"); if(mBluetoothAdapter.getRemoteDevice(bluetoothDevice.getAddress()) != null) { device = bluetoothDevice; mBluetoothGatt = device.connectGatt(getApplicationContext(), false, mGattCallback); return true; } } } device = mBluetoothAdapter.getRemoteDevice(address); if (device == null) { Log.w(TAG, "Device not found. Unable to connect."); setRetryTimer(); return false; } Log.w(TAG, "Trying to create a new connection."); mBluetoothGatt = device.connectGatt(getApplicationContext(), false, mGattCallback); mConnectionState = STATE_CONNECTING; return true; } public void authenticateConnection() { Log.w(TAG, "Trying to auth"); String receiverSn = prefs.getString("share_key", "SM00000000").toUpperCase() + "000000"; if(receiverSn.compareTo("SM00000000000000") == 0) { // They havnt set their serial number, dont bond! setRetryTimer(); return; } byte[] bondkey = (receiverSn).getBytes(StandardCharsets.US_ASCII); if (mBluetoothGatt != null) { if (mShareService != null) { if(!share2) { mAuthenticationCharacteristic = mShareService.getCharacteristic(DexShareAttributes.AuthenticationCode); } else { mAuthenticationCharacteristic = mShareService.getCharacteristic(DexShareAttributes.AuthenticationCode2); } if (mAuthenticationCharacteristic != null) { Log.v(TAG, "Auth Characteristic found: " + mAuthenticationCharacteristic.toString()); if (mAuthenticationCharacteristic.setValue(bondkey)) { mBluetoothGatt.writeCharacteristic(mAuthenticationCharacteristic); } else { setRetryTimer(); } } else { Log.w(TAG, "Authentication Characteristic IS NULL"); setRetryTimer(); } } else { Log.w(TAG, "CRADLE SERVICE IS NULL"); } } else { setRetryTimer(); } } public void assignCharacteristics() { if(!share2) { Log.d(TAG, "Setting #1 characteristics"); mSendDataCharacteristic = mShareService.getCharacteristic(DexShareAttributes.ShareMessageReceiver); mReceiveDataCharacteristic = mShareService.getCharacteristic(DexShareAttributes.ShareMessageResponse); mCommandCharacteristic = mShareService.getCharacteristic(DexShareAttributes.Command); mResponseCharacteristic = mShareService.getCharacteristic(DexShareAttributes.Response); mHeartBeatCharacteristic = mShareService.getCharacteristic(DexShareAttributes.HeartBeat); } else { Log.d(TAG, "Setting #1 characteristics"); mSendDataCharacteristic = mShareService.getCharacteristic(DexShareAttributes.ShareMessageReceiver2); mReceiveDataCharacteristic = mShareService.getCharacteristic(DexShareAttributes.ShareMessageResponse2); mCommandCharacteristic = mShareService.getCharacteristic(DexShareAttributes.Command2); mResponseCharacteristic = mShareService.getCharacteristic(DexShareAttributes.Response2); mHeartBeatCharacteristic = mShareService.getCharacteristic(DexShareAttributes.HeartBeat2); } } public void setListeners(int listener_number) { Log.w(TAG, "Setting Listener: #" + listener_number); if (listener_number == 1) { step = 2; setCharacteristicIndication(mReceiveDataCharacteristic); } else { step = 3; attemptRead(); } } public void close() { if (mBluetoothGatt == null) { return; } mBluetoothGatt.close(); setRetryTimer(); mBluetoothGatt = null; mConnectionState = STATE_DISCONNECTED; Log.w(TAG, "bt Disconnected"); } public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic) { setCharacteristicNotification(characteristic, true); } public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) { Log.w(TAG, "Characteristic setting notification"); mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(HM10Attributes.CLIENT_CHARACTERISTIC_CONFIG)); Log.w(TAG, "Descriptor found: " + descriptor.getUuid()); descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); mBluetoothGatt.writeDescriptor(descriptor); } public void setCharacteristicIndication(BluetoothGattCharacteristic characteristic) { setCharacteristicIndication(characteristic, true); } public void setCharacteristicIndication(BluetoothGattCharacteristic characteristic, boolean enabled) { Log.w(TAG, "Characteristic setting indication"); mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(HM10Attributes.CLIENT_CHARACTERISTIC_CONFIG)); Log.w(TAG, "Descriptor found: " + descriptor.getUuid()); descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE); mBluetoothGatt.writeDescriptor(descriptor); } public void writeCommand(List packets, int aRecordType, Action1 dataResponseListener) { mDataResponseListener = dataResponseListener; successfulWrites = 0; writePackets = packets; recordType = aRecordType; step = 0; currentGattTask = GATT_WRITING_COMMANDS; gattWritingStep(); } public void clearGattTask() { currentGattTask = GATT_NOTHING; step = 0; } private void gattSetupStep() { step = 1; if(share2) { assignCharacteristics(); } setListeners(1); } private void gattWritingStep() { Log.d(TAG, "Writing command to the Gatt, step: " + step); int index = step; if (index <= (writePackets.size() - 1)) { Log.d(TAG, "Writing: " + writePackets.get(index) + " index: " + index); if(mSendDataCharacteristic != null && writePackets != null) { mSendDataCharacteristic.setValue(writePackets.get(index)); if (mBluetoothGatt.writeCharacteristic(mSendDataCharacteristic)) { Log.d(TAG, "Wrote Successfully"); } } } else { Log.d(TAG, "Done Writing commands"); clearGattTask(); } } private final BroadcastReceiver mPairReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); final BluetoothDevice bondDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (mBluetoothGatt != null && mBluetoothGatt.getDevice() != null && bondDevice != null) { if (!bondDevice.getAddress().equals(mBluetoothGatt.getDevice().getAddress())) { Log.d(TAG, "Bond state wrong device"); return; // That wasnt a device we care about!! } } if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) { final int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR); if (state == BluetoothDevice.BOND_BONDED) { Log.d(TAG, "CALLBACK RECIEVED Bonded"); authenticateConnection(); } else if (state == BluetoothDevice.BOND_NONE) { Log.d(TAG, "CALLBACK RECIEVED: Not Bonded"); } else if (state == BluetoothDevice.BOND_BONDING) { Log.d(TAG, "CALLBACK RECIEVED: Trying to bond"); } } } }; private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { Log.w(TAG, "Gatt state change status: " + status + " new state: " + newState); writeStatusConnectionFailures(status); if (status == 133) { Log.e(TAG, "Got the status 133 bug, GROSS!!"); } if (newState == BluetoothProfile.STATE_CONNECTED) { mBluetoothGatt = gatt; device = mBluetoothGatt.getDevice(); mConnectionState = STATE_CONNECTED; ActiveBluetoothDevice.connected(); Log.w(TAG, "Connected to GATT server."); Log.w(TAG, "discovering services"); currentGattTask = GATT_SETUP; if (!mBluetoothGatt.discoverServices()) { Log.w(TAG, "discovering failed"); if(shouldDisconnect) { stopSelf(); } else { setRetryTimer(); } } } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { mConnectionState = STATE_DISCONNECTED; ActiveBluetoothDevice.disconnected(); if(shouldDisconnect) { stopSelf(); } else { setRetryTimer(); } Log.w(TAG, "Disconnected from GATT server."); } else { Log.w(TAG, "Gatt callback... strange state."); } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { Log.d(TAG, "services discovered " + status); if (status == BluetoothGatt.GATT_SUCCESS) { mShareService = mBluetoothGatt.getService(DexShareAttributes.CradleService); if(mShareService == null) { mShareService = mBluetoothGatt.getService(DexShareAttributes.CradleService2); share2 = true; } else { share2 = false; } assignCharacteristics(); authenticateConnection(); gattSetupStep(); } else { Log.w(TAG, "No Services Discovered!"); } } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { Log.v(TAG, "Characteristic Read " + characteristic.getUuid()); if(mHeartBeatCharacteristic.getUuid().equals(characteristic.getUuid())) { Log.v(TAG, "Characteristic Read " + characteristic.getUuid() + " " + characteristic.getValue()); setCharacteristicNotification(mHeartBeatCharacteristic); } mBluetoothGatt.readCharacteristic(mHeartBeatCharacteristic); } else { Log.e(TAG, "Characteristic failed to read"); } } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { UUID charUuid = characteristic.getUuid(); Log.d(TAG, "Characteristic Update Received: " + charUuid); if (charUuid.compareTo(mReceiveDataCharacteristic.getUuid()) == 0) { Log.d(TAG, "mCharReceiveData Update"); byte[] value = characteristic.getValue(); if (value != null) { Observable.just(characteristic.getValue()).subscribe(mDataResponseListener); } } } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); Log.d(TAG, "Characteristic onDescriptorWrite ch " + characteristic.getUuid()); if(mHeartBeatCharacteristic.getUuid().equals(characteristic.getUuid())) { state_notifSetupSucess = true; setCharacteristicIndication(mReceiveDataCharacteristic); } if(mReceiveDataCharacteristic.getUuid().equals(characteristic.getUuid())) { setCharacteristicIndication(mResponseCharacteristic); } if(mResponseCharacteristic.getUuid().equals(characteristic.getUuid())) { attemptRead(); } } else if ((status & BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) != 0 || (status & BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION) != 0) { if (gatt.getDevice().getBondState() == BluetoothDevice.BOND_NONE) { device = gatt.getDevice(); state_authInProgress = true; bondDevice(); } else { Log.e(TAG, "The phone is trying to read from paired device without encryption. Android Bug?"); } } else { Log.e(TAG, "Unknown error writing descriptor"); } } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { Log.d(TAG, "characteristic wrote " + status); if (status == BluetoothGatt.GATT_SUCCESS) { Log.d(TAG, "Wrote a characteristic successfully " + characteristic.getUuid()); if (mAuthenticationCharacteristic.getUuid().equals(characteristic.getUuid())) { state_authSucess = true; mBluetoothGatt.readCharacteristic(mHeartBeatCharacteristic); } } else if ((status & BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) != 0 || (status & BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION) != 0) { if (gatt.getDevice().getBondState() == BluetoothDevice.BOND_NONE) { device = gatt.getDevice(); state_authInProgress = true; bondDevice(); } else { Log.e(TAG, "The phone is trying to read from paired device without encryption. Android Bug?"); } } else { Log.e(TAG, "Unknown error writing Characteristic"); } } }; public void bondDevice() { final IntentFilter bondintent = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED); registerReceiver(mPairReceiver, bondintent); if(!share2){ device.setPin("000000".getBytes()); } device.createBond(); } private void writeStatusConnectionFailures(int status) { if(status != 0) { Log.e(TAG, "ERRR: GATT_WRITE_NOT_PERMITTED " + (status & BluetoothGatt.GATT_WRITE_NOT_PERMITTED)); Log.e(TAG, "ERRR: GATT_INSUFFICIENT_AUTHENTICATION " + (status & BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION)); Log.e(TAG, "ERRR: GATT_REQUEST_NOT_SUPPORTED " + (status & BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED)); Log.e(TAG, "ERRR: GATT_INSUFFICIENT_ENCRYPTION " + (status & BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION)); Log.e(TAG, "ERRR: GATT_INVALID_OFFSET " + (status & BluetoothGatt.GATT_INVALID_OFFSET)); Log.e(TAG, "ERRR: GATT_FAILURE " + (status & BluetoothGatt.GATT_FAILURE)); Log.e(TAG, "ERRR: GATT_INVALID_ATTRIBUTE_LENGTH " + (status & BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH)); Log.e(TAG, "ERRR: GATT_READ_NOT_PERMITTED" + (status & BluetoothGatt.GATT_READ_NOT_PERMITTED)); } } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Services/MongoWrapper.java ================================================ package com.eveningoutpost.dexdrip.Services; import java.io.IOException; import java.net.UnknownHostException; import android.util.Log; import com.mongodb.BasicDBObject; import com.mongodb.DB; import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.DBObject; import com.mongodb.MongoClient; import com.mongodb.MongoException; import com.mongodb.MongoClientURI; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Set; public class MongoWrapper { MongoClient mongoClient_; String dbUriStr_; String dbName_; String collection_; String index_; String machineName_; private final static String TAG = WixelReader.class.getName(); public MongoWrapper(String dbUriStr, String collection, String index, String machineName) { dbUriStr_ = dbUriStr; // dbName is the last part of the string starting with /dbname dbName_ = dbUriStr.substring(dbUriStr.lastIndexOf('/') + 1); collection_ = collection; index_ = index; machineName_ = machineName; } // Unfortunately, this also throws other exceptions that are not documetned... public DBCollection openMongoDb() throws UnknownHostException { MongoClientURI dbUri = new MongoClientURI(dbUriStr_); //?? thros mongoClient_ = new MongoClient(dbUri); DB db = mongoClient_.getDB( dbName_ ); DBCollection coll = db.getCollection(collection_); coll.createIndex(new BasicDBObject(index_, 1)); // create index on "i", ascending return coll; } public void closeMongoDb() { if(mongoClient_ != null) { mongoClient_.close(); } } public boolean WriteDebugDataToMongo(String message) { String complete = machineName_ + " " + new Date().toLocaleString() + " " + message; BasicDBObject doc = new BasicDBObject("DebugMessage", complete); return WriteToMongo(doc); } public boolean WriteToMongo(TransmitterRawData trd) { BasicDBObject bdbo = trd.toDbObj(machineName_ + " " + new Date(trd.CaptureDateTime).toLocaleString()); return WriteToMongo(bdbo); } public boolean WriteToMongo(BasicDBObject bdbo) { DBCollection coll; try { coll = openMongoDb(); coll.insert(bdbo); } catch (UnknownHostException e) { Log.e(TAG, "WriteToMongo cought UnknownHostException! ",e); return false; } catch (MongoException e) { Log.e(TAG, "WriteToMongo cought MongoException! ", e); return false; } catch (Exception e) { Log.e(TAG, "WriteToMongo cought Exception! ", e); closeMongoDb(); return false; } finally { closeMongoDb(); } return true; } // records will be marked by their timestamp public List ReadFromMongo(int numberOfRecords) { System.out.println( "Starting to read from mongodb"); List trd_list = new LinkedList(); DBCollection coll; TransmitterRawData lastTrd = null; try { coll = openMongoDb(); BasicDBObject query = new BasicDBObject("RawValue", new BasicDBObject("$exists", true)); DBCursor cursor = coll.find(query); cursor.sort(new BasicDBObject("CaptureDateTime", -1)); try { while(cursor.hasNext() && trd_list.size() < numberOfRecords) { //System.out.println(cursor.next()); Log.e(TAG, "Read an object from mongodb"); TransmitterRawData trd = new TransmitterRawData((BasicDBObject)cursor.next()); // Do our best to fix the relative time... trd.RelativeTime = new Date().getTime() - trd.CaptureDateTime; // since we are reading it from the db, it was uploaded... trd.Uploaded = 1; if(lastTrd == null) { trd_list.add(0,trd); lastTrd = trd; System.out.println( trd.toTableString()); } else if(!WixelReader.almostEquals(lastTrd, trd)) { lastTrd = trd; trd_list.add(0,trd); System.out.println( trd.toTableString()); } } } finally { cursor.close(); } } catch (UnknownHostException e) { Log.e(TAG, "ReadFromMongo: cought UnknownHostException! ", e); return null; } catch (MongoException e) { Log.e(TAG, "ReadFromMongo: cought MongoException! " , e); return trd_list; } catch (Exception e) { Log.e(TAG, "ReadFromMongo: cought Exception! " , e); closeMongoDb(); return null; }finally { closeMongoDb(); } return trd_list; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Services/SyncService.java ================================================ package com.eveningoutpost.dexdrip.Services; import android.app.AlarmManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.IBinder; import android.preference.PreferenceManager; import android.util.Log; import com.eveningoutpost.dexdrip.Models.BgReading; import com.eveningoutpost.dexdrip.Models.Calibration; import com.eveningoutpost.dexdrip.UtilityModels.BgSendQueue; import com.eveningoutpost.dexdrip.UtilityModels.CalibrationSendQueue; import com.eveningoutpost.dexdrip.UtilityModels.MongoSendTask; import com.eveningoutpost.dexdrip.UtilityModels.NightscoutUploader; import com.eveningoutpost.dexdrip.UtilityModels.RestCalls; import com.eveningoutpost.dexdrip.UtilityModels.SensorSendQueue; import java.util.ArrayList; import java.util.Calendar; import java.util.List; public class SyncService extends Service { int mStartMode; private Context mContext; private Boolean enableRESTUpload; private Boolean enableMongoUpload; private SharedPreferences prefs; @Override public void onCreate() { Log.w("SYNC SERVICE:", "STARTING SERVICE"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { attemptSend(); return START_STICKY; } @Override public void onDestroy() { setRetryTimer(); Log.w("SYNC SERVICE", "SERVICE STOPPED"); } @Override public IBinder onBind(Intent intent) { throw new UnsupportedOperationException("Not yet implemented"); } public void attemptSend() { mContext = getApplicationContext(); prefs = PreferenceManager.getDefaultSharedPreferences(mContext); enableRESTUpload = prefs.getBoolean("cloud_storage_api_enable", false); enableMongoUpload = prefs.getBoolean("cloud_storage_mongodb_enable", false); if (enableRESTUpload || enableMongoUpload) { syncToMogoDb(); } if (false) { for (SensorSendQueue job : SensorSendQueue.queue()) { RestCalls.sendSensor(job); } for (CalibrationSendQueue job : CalibrationSendQueue.queue()) { RestCalls.sendCalibration(job); } for (BgSendQueue job : BgSendQueue.queue()) { RestCalls.sendBgReading(job); } } setRetryTimer(); } public void setRetryTimer() { Calendar calendar = Calendar.getInstance(); AlarmManager alarm = (AlarmManager)getSystemService(ALARM_SERVICE); alarm.set(alarm.RTC_WAKEUP, calendar.getTimeInMillis() + (1000 * 30 * 5), PendingIntent.getService(this, 0, new Intent(this, SyncService.class), 0)); } public void syncToMogoDb() { MongoSendTask task = new MongoSendTask(getApplicationContext()); task.execute(); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Services/TransmitterRawData.java ================================================ package com.eveningoutpost.dexdrip.Services; import java.util.Date; import com.mongodb.BasicDBObject; /** * Created by John Costik on 6/7/14. */ public class TransmitterRawData { private long _id; public String Id; public int TransmissionId; public String TransmitterId; public int RawValue; public int FilteredValue; public int BatteryLife; public int ReceivedSignalStrength; public long CaptureDateTime; public int Uploaded; public int UploadAttempts; public int UploaderBatteryLife; // When sending set this value to the relative time... // The time between the capture and now... public long RelativeTime; public int getTransmissionId() { return TransmissionId; } public void setTransmissionId(int transmissionId) { TransmissionId = transmissionId; } public int getUploaded() { return Uploaded; } public void setUploaded(int uploaded) { Uploaded = uploaded; } public int getUploadAttempts() { return UploadAttempts; } public void setUploadAttempts(int uploadAttempts) { UploadAttempts = uploadAttempts; } public int getUploaderBatteryLife() { return UploaderBatteryLife; } public void setUploaderBatteryLife(int uploaderBatteryLife) { UploaderBatteryLife = uploaderBatteryLife; } public int getBatteryLife() { return BatteryLife; } public void setBatteryLife(int batteryLife) { BatteryLife = batteryLife; } public int getReceivedSignalStrength() { return ReceivedSignalStrength; } public void setReceivedSignalStrength(int receivedSignalStrength) { ReceivedSignalStrength = receivedSignalStrength; } public String getTransmitterId() { return TransmitterId; } public void setTransmitterId(String transmitterId) { TransmitterId = transmitterId; } public int getRawValue() { return RawValue; } public void setRawValue(int rawValue) { RawValue = rawValue; } public int getFilteredValue() { return FilteredValue; } public void setFilteredValue(int filteredValue) { FilteredValue = filteredValue; } public long getCaptureDateTime() { return CaptureDateTime; } public void setCaptureDateTime(long captureDateTime) { CaptureDateTime = captureDateTime; } public long get_id() { return _id; } public void set_id(long _id) { this._id = _id; } private Long getRelativeTime() { return RelativeTime; } public TransmitterRawData(){ } /* public TransmitterRawData(String id, String raw, String filter, String battery, String rssi, int uploaderBattery){ RawValue = Integer.parseInt(raw); FilteredValue = Integer.parseInt(filter); TransmitterId = id; BatteryLife = Integer.parseInt(battery); ReceivedSignalStrength = Integer.parseInt(rssi); CaptureDateTime = new Date().getTime(); UploaderBatteryLife = uploaderBattery; Uploaded = 0; UploadAttempts = 1; } public TransmitterRawData(byte[] buffer, int len, Context context){ StringBuilder toParse = new StringBuilder(); for (int i = 0; i < len; ++i) { toParse.append((char) buffer[i]); } String[] parsed = toParse.toString().split("\\s+"); RawValue = Integer.parseInt(parsed[1]); FilteredValue = Integer.parseInt(parsed[2]); TransmitterId = parsed[0]; BatteryLife = Integer.parseInt(parsed[3]); ReceivedSignalStrength = Integer.parseInt(parsed[4]); TransmissionId = Integer.parseInt(parsed[5]); CaptureDateTime = new Date().getTime(); Intent i = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); UploaderBatteryLife = i.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); Uploaded = 0; UploadAttempts = 1; } */ public String toTableString() { String displayDt = new Date(getCaptureDateTime()).toLocaleString() + System.getProperty("line.separator"); String transmitterId = "Transmitter Id: " + getTransmitterId() + System.getProperty("line.separator"); String transmissionId = "Transmission Id: " + getTransmissionId() + System.getProperty("line.separator"); String rawVal = "Raw Value: " + getRawValue() + System.getProperty("line.separator"); String filterVal = "Filtered Value: " + getFilteredValue() + System.getProperty("line.separator"); String batteryVal = "Transmitter Battery: " + getBatteryLife() + " " + System.getProperty("line.separator"); String signalVal = "RSSI: " + getReceivedSignalStrength() + " " + System.getProperty("line.separator"); String uploadDeviceBatteryVal = "Uploader Battery: " + getUploaderBatteryLife() + " " + System.getProperty("line.separator"); String uploaded = "Uploaded: " + getUploaded() + " " + System.getProperty("line.separator"); String RelativeTime = "relateive time (seconds): " + getRelativeTime() / 1000 + " "+ System.getProperty("line.separator"); return displayDt + transmitterId + transmissionId + rawVal + filterVal + batteryVal + signalVal + uploadDeviceBatteryVal + uploaded + RelativeTime; } public BasicDBObject toDbObj(String DebugInfo) { BasicDBObject doc = new BasicDBObject("TransmissionId", TransmissionId). append("TransmitterId", TransmitterId). append("RawValue", RawValue). append("FilteredValue", FilteredValue). append("BatteryLife", BatteryLife). append("ReceivedSignalStrength", ReceivedSignalStrength). append("CaptureDateTime", CaptureDateTime). append("UploaderBatteryLife", UploaderBatteryLife). append("DebugInfo", DebugInfo); return doc; } public TransmitterRawData(BasicDBObject src) { TransmissionId = src.getInt("TransmissionId"); TransmitterId = src.getString("TransmitterId"); RawValue = src.getInt("RawValue"); FilteredValue = src.getInt("FilteredValue"); BatteryLife = src.getInt("BatteryLife"); ReceivedSignalStrength = src.getInt("ReceivedSignalStrength"); CaptureDateTime = src.getLong("CaptureDateTime"); UploaderBatteryLife = src.getInt("UploaderBatteryLife"); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Services/WixelReader.java ================================================ package com.eveningoutpost.dexdrip.Services; import java.io.IOException; import java.util.Date; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.net.SocketTimeoutException; import java.util.LinkedList; import java.util.List; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.util.Log; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.eveningoutpost.dexdrip.Sensor; import com.eveningoutpost.dexdrip.Models.BgReading; import com.eveningoutpost.dexdrip.Models.TransmitterData; public class WixelReader extends Thread { private final static String TAG = WixelReader.class.getName(); private static WixelReader singleton; public synchronized static WixelReader getInstance(Context ctx) { if(singleton == null) { singleton = new WixelReader(ctx); } return singleton; } private final Context mContext; private volatile boolean mStop = false; private static boolean sStarted = false; public WixelReader(Context ctx) { mContext = ctx.getApplicationContext(); } public static void sStart(Context ctx) { if(sStarted) { return; } WixelReader theWixelReader = getInstance(ctx); theWixelReader.start(); sStarted = true; } public static void sStop() { if(!sStarted) { return; } WixelReader theWixelReader = getInstance(null); theWixelReader.Stop(); try { theWixelReader.join(); } catch (InterruptedException e) { Log.e(TAG, "cought InterruptedException, could not wait for the wixel thread to exit", e); } sStarted = false; // A stopped thread can not start again, so we need to kill it and will start a new one // on demand singleton = null; } public static boolean IsConfigured(Context ctx) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx); String recieversIpAddresses = prefs.getString("wifi_recievers_addresses", ""); if(recieversIpAddresses == null || recieversIpAddresses.equals("") ) { return false; } return true; } public static boolean almostEquals( TransmitterRawData e1, TransmitterRawData e2) { if (e1 == null || e2==null) { return false; } // relative time is in ms if ((Math.abs(e1.CaptureDateTime - e2.CaptureDateTime) < 120 * 1000 ) && (e1.TransmissionId == e2.TransmissionId)) { return true; } return false; } // last in the array, is first in time public static List Merge2Lists(List list1 , List list2) { List merged = new LinkedList (); while (true) { if(list1.size() == 0 && list2.size() == 0) { break; } if (list1.size() == 0) { merged.addAll(list2); break; } if (list2.size() == 0) { merged.addAll(list1); break; } if (almostEquals(list1.get(0), list2.get(0))) { list2.remove(0); merged.add(list1.remove(0)); continue; } if(list1.get(0).RelativeTime > list2.get(0).RelativeTime) { merged.add(list1.remove(0)); } else { merged.add(list2.remove(0)); } } return merged; } public static List MergeLists(List > allTransmitterRawData) { List MergedList; MergedList = allTransmitterRawData.remove(0); for (List it : allTransmitterRawData) { MergedList = Merge2Lists(MergedList, it); } return MergedList; } public static List ReadHost(String hostAndIp, int numberOfRecords) { int port; System.out.println("Reading From " + hostAndIp); Log.i(TAG,"Reading From " + hostAndIp); String []hosts = hostAndIp.split(":"); if(hosts.length != 2) { System.out.println("Invalid hostAndIp " + hostAndIp); Log.e(TAG, "Invalid hostAndIp " + hostAndIp); return null; } try { port = Integer.parseInt(hosts[1]); } catch (NumberFormatException nfe) { System.out.println("Invalid port " +hosts[1]); Log.e(TAG, "Invalid hostAndIp " + hostAndIp, nfe); return null; } if (port < 10 || port > 65536) { System.out.println("Invalid port " +hosts[1]); Log.e(TAG, "Invalid hostAndIp " + hostAndIp); return null; } System.out.println("Reading from " + hosts[0] + " " + port); List ret; try { ret = Read(hosts[0], port, numberOfRecords); } catch(Exception e) { // We had some error, need to move on... System.out.println("read from host failed cought expation" + hostAndIp); Log.e(TAG, "read from host failed " + hostAndIp, e); return null; } return ret; } public static List ReadFromMongo(String dbury, int numberOfRecords) { Log.i(TAG,"Reading From " + dbury); List tmpList; // format is dburi/db/collection. We need to find the collection and strip it from the dburi. int indexOfSlash = dbury.lastIndexOf('/'); if(indexOfSlash == -1) { // We can not find a collection name Log.e(TAG, "Error bad dburi. Did not find a collection name starting with / " + dbury); // in order for the user to understand that there is a problem, we return null return null; } String collection = dbury.substring(indexOfSlash + 1); dbury = dbury.substring(0, indexOfSlash); // Make sure that we have another /, since this is used in the constructor. indexOfSlash = dbury.lastIndexOf('/'); if(indexOfSlash == -1) { // We can not find a collection name Log.e(TAG, "Error bad dburi. Did not find a collection name starting with / " + dbury); // in order for the user to understand that there is a problem, we return null return null; } MongoWrapper mt = new MongoWrapper(dbury, collection, "CaptureDateTime", "MachineNameNotUsed"); return mt.ReadFromMongo(numberOfRecords); } // format of string is ip1:port1,ip2:port2; public static TransmitterRawData[] Read(String hostsNames, int numberOfRecords) { String []hosts = hostsNames.split(","); if(hosts.length == 0) { Log.e(TAG, "Error no hosts were found " + hostsNames); return null; } List > allTransmitterRawData = new LinkedList >(); // go over all hosts and read data from them for(String host : hosts) { List tmpList; if (host.startsWith("mongodb://")) { tmpList = ReadFromMongo(host ,numberOfRecords); } else { tmpList = ReadHost(host, numberOfRecords); } if(tmpList != null && tmpList.size() > 0) { allTransmitterRawData.add(tmpList); } } // merge the information if (allTransmitterRawData.size() == 0) { System.out.println("Could not read anything from " + hostsNames); Log.e(TAG, "Could not read anything from " + hostsNames); return null; } List mergedData= MergeLists(allTransmitterRawData); int retSize = Math.min(numberOfRecords, mergedData.size()); TransmitterRawData []trd_array = new TransmitterRawData[retSize]; mergedData.subList(mergedData.size() - retSize, mergedData.size()).toArray(trd_array); System.out.println("Final Results========================================================================"); for (int i= 0; i < trd_array.length; i++) { // System.out.println( trd_array[i].toTableString()); } return trd_array; } public static List Read(String hostName,int port, int numberOfRecords) { List trd_list = new LinkedList(); try { Log.i(TAG, "Read called"); Gson gson = new GsonBuilder().create(); // An example of using gson. ComunicationHeader ch = new ComunicationHeader(); ch.version = 1; ch.numberOfRecords = numberOfRecords; String flat = gson.toJson(ch); ComunicationHeader ch2 = gson.fromJson(flat, ComunicationHeader.class); System.out.println("Results code" + flat + ch2.version); // Real client code Socket MySocket = new Socket(hostName, port); System.out.println("After the new socket \n"); MySocket.setSoTimeout(2000); System.out.println("client connected... " ); PrintWriter out = new PrintWriter(MySocket.getOutputStream(), true); BufferedReader in = new BufferedReader(new InputStreamReader(MySocket.getInputStream())); out.println(flat); while(true) { String data = in.readLine(); if(data == null) { System.out.println("recieved null exiting"); break; } if(data.equals("")) { System.out.println("recieved \"\" exiting"); break; } //System.out.println( "data size " +data.length() + " data = "+ data); TransmitterRawData trd = gson.fromJson(data, TransmitterRawData.class); trd.CaptureDateTime = System.currentTimeMillis() - trd.RelativeTime; trd_list.add(0,trd); // System.out.println( trd.toTableString()); if(trd_list.size() == numberOfRecords) { // We have the data we want, let's get out break; } } MySocket.close(); return trd_list; }catch(SocketTimeoutException s) { Log.e(TAG, "Socket timed out! ", s); } catch(IOException e) { Log.e(TAG, "cought IOException! ", e); } return trd_list; } public void run() { Long LastReportedTime = new Date().getTime(); TransmitterRawData LastReportedReading = null; Log.e(TAG, "Starting... LastReportedReading " + LastReportedReading); try { while (!mStop && !interrupted()) { // try to read one object... TransmitterRawData[] LastReadingArr = null; if(WixelReader.IsConfigured(mContext)) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); String recieversIpAddresses = prefs.getString("wifi_recievers_addresses", ""); LastReadingArr = Read(recieversIpAddresses ,1); } if (LastReadingArr != null && LastReadingArr.length > 0) { // Last in the array is the most updated reading we have. TransmitterRawData LastReading = LastReadingArr[LastReadingArr.length -1]; //if (LastReading.CaptureDateTime > LastReportedReading + 5000) { // Make sure we do not report packets from the far future... if ((LastReading.CaptureDateTime > LastReportedTime ) && (!almostEquals(LastReading, LastReportedReading)) && LastReading.CaptureDateTime < new Date().getTime() + 12000) { // We have a real new reading... Log.e(TAG, "calling setSerialDataToTransmitterRawData " + LastReading.RawValue + " LastReading.CaptureDateTime " + LastReading.CaptureDateTime + " " + LastReading.TransmissionId); setSerialDataToTransmitterRawData(LastReading.RawValue , LastReading.BatteryLife, LastReading.CaptureDateTime); LastReportedReading = LastReading; LastReportedTime = LastReading.CaptureDateTime; } } // let's sleep (right now for 30 seconds) Thread.sleep(30000); } } catch (InterruptedException e) { Log.e(TAG, "cought InterruptedException! ", e); // time to get out... } } // this function is only a test function. It is used to set many points fast in order to allow // faster testing without real data. public void runFake() { // let's start by faking numbers.... int i = 0; int added = 5; while (!mStop) { try { for (int j = 0 ; j < 3; j++) { Thread.sleep(1000); if(mStop ) { // we were asked to leave, so do it.... return; } } i+=added; if (i==50) { added = -5; } if (i==0) { added = 5; } int fakedRaw = 150000 + i * 1000; Log.e(TAG, "calling setSerialDataToTransmitterRawData " + fakedRaw); setSerialDataToTransmitterRawData(fakedRaw, 100, new Date().getTime()); } catch (InterruptedException e) { // time to get out... Log.e(TAG, "cought InterruptedException! ", e); break; } } } public void Stop() { mStop = true; interrupt(); } public void setSerialDataToTransmitterRawData(int raw_data ,int sensor_battery_leve, Long CaptureTime) { TransmitterData transmitterData = TransmitterData.create(raw_data, sensor_battery_leve, CaptureTime); if (transmitterData != null) { Sensor sensor = Sensor.currentSensor(); if (sensor != null) { BgReading bgReading = BgReading.create(transmitterData.raw_data, mContext, CaptureTime); sensor.latest_battery_level = transmitterData.sensor_battery_level; sensor.save(); } else { Log.w(TAG, "No Active Sensor, Data only stored in Transmitter Data"); } } } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/SettingsActivity.java ================================================ package com.eveningoutpost.dexdrip; import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; import android.os.Bundle; import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceActivity; import android.preference.PreferenceCategory; import android.preference.PreferenceManager; import android.preference.RingtonePreference; import android.text.TextUtils; public class SettingsActivity extends PreferenceActivity { @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); setupSimplePreferencesScreen(); } private void setupSimplePreferencesScreen() { addPreferencesFromResource(R.xml.pref_license); PreferenceCategory fakeHeader = new PreferenceCategory(this); getPreferenceScreen().addPreference(fakeHeader); addPreferencesFromResource(R.xml.pref_general); fakeHeader = new PreferenceCategory(this); getPreferenceScreen().addPreference(fakeHeader); addPreferencesFromResource(R.xml.pref_notifications); fakeHeader = new PreferenceCategory(this); getPreferenceScreen().addPreference(fakeHeader); addPreferencesFromResource(R.xml.pref_data_source); fakeHeader = new PreferenceCategory(this); getPreferenceScreen().addPreference(fakeHeader); addPreferencesFromResource(R.xml.pref_data_sync); bindPreferenceSummaryToValue(findPreference("dex_collection_method")); bindPreferenceSummaryToValue(findPreference("units")); bindPreferenceSummaryToValue(findPreference("share_key")); } private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object value) { String stringValue = value.toString(); if (preference instanceof ListPreference) { ListPreference listPreference = (ListPreference) preference; int index = listPreference.findIndexOfValue(stringValue); preference.setSummary( index >= 0 ? listPreference.getEntries()[index] : null); } else if (preference instanceof RingtonePreference) { if (TextUtils.isEmpty(stringValue)) { preference.setSummary(R.string.pref_ringtone_silent); } else { Ringtone ringtone = RingtoneManager.getRingtone( preference.getContext(), Uri.parse(stringValue)); if (ringtone == null) { preference.setSummary(null); } else { String name = ringtone.getTitle(preference.getContext()); preference.setSummary(name); } } } else { preference.setSummary(stringValue); } return true; } }; private static void bindPreferenceSummaryToValue(Preference preference) { preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener); sBindPreferenceSummaryToValueListener.onPreferenceChange(preference, PreferenceManager .getDefaultSharedPreferences(preference.getContext()) .getString(preference.getKey(), "")); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ShareModels/DexcomShareInterface.java ================================================ package com.eveningoutpost.dexdrip.ShareModels; import java.util.Map; import retrofit.Callback; import retrofit.client.Response; import retrofit.http.Body; import retrofit.http.POST; import retrofit.http.QueryMap; /** * Created by stephenblack on 3/16/15. */ public interface DexcomShareInterface { @POST("/Publisher/ReadPublisherLatestGlucoseValues") ShareGlucose[] getShareBg(@QueryMap Map options); @POST("/General/LoginPublisherAccountByName") void getSessionId(@Body ShareAuthenticationBody body, Callback callback); //Since this seems to respond with a string we need a callback that will parse the response body //new String(((TypedByteArray) response.getBody()).getBytes()); @POST("/Publisher/IsRemoteMonitoringSessionActive") void checkSessionActive(@QueryMap Map options, Callback callback); // needs ?sessionId={YourSessionId} // returns true or false @POST("/Publisher/PostReceiverEgvRecords") void uploadBGRecords(@QueryMap Map options, @Body ShareUploadPayload payload, Callback callback); // needs ?sessionId={YourSessionId} // body ShareUploadPayload // status code } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ShareModels/Egv.java ================================================ package com.eveningoutpost.dexdrip.ShareModels; import com.eveningoutpost.dexdrip.Models.BgReading; import com.google.gson.annotations.Expose; /** * Created by stephenblack on 3/19/15. */ public class Egv { @Expose public int Trend; @Expose public int Value; @Expose public String ST; @Expose public String DT; public Egv(BgReading bg) { this.Value = (int) bg.calculated_value; this.DT = toDateString(bg.timestamp); this.ST = toDateString(bg.timestamp); this.Trend = slopeOrdinal(bg); } public String toDateString(double timestamp) { long shortened = (long) Math.floor((timestamp/1000)); return "/Date(" + Long.toString(shortened*1000) + ")/"; } public int slopeOrdinal(BgReading bg) { double slope_by_minute = bg.calculated_value_slope * 60000; int arrow = 0; if (slope_by_minute <= (-3.5)) { arrow = 7; } else if (slope_by_minute <= (-2)) { arrow = 6; } else if (slope_by_minute <= (-1)) { arrow = 5; } else if (slope_by_minute <= (1)) { arrow = 4; } else if (slope_by_minute <= (2)) { arrow = 3; } else if (slope_by_minute <= (3.5)) { arrow = 2; } else { arrow = 1; } if(bg.hide_slope) { arrow = 9; } return arrow; } // { // // "Trend":4, // "ST":"\/Date(1426783106000 - 1426754317000)\/", // "DT":"\/Date(1426754317000)\/", // "Value":97 // } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ShareModels/ShareAuthenticationBody.java ================================================ package com.eveningoutpost.dexdrip.ShareModels; import com.google.gson.annotations.Expose; /** * Created by stephenblack on 3/16/15. */ public class ShareAuthenticationBody { @Expose public String password; @Expose public String applicationId; @Expose public String accountName; public ShareAuthenticationBody(String aPassword, String aAccountName) { this.password = aPassword; this.accountName = aAccountName; this.applicationId = "d89443d2-327c-4a6f-89e5-496bbb0317db"; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ShareModels/ShareGlucose.java ================================================ package com.eveningoutpost.dexdrip.ShareModels; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.BatteryManager; import android.provider.BaseColumns; import android.util.Log; import com.activeandroid.Model; import com.activeandroid.annotation.Column; import com.activeandroid.annotation.Table; import com.google.gson.annotations.Expose; /** * Created by stephenblack on 3/16/15. */ @Table(name = "ShareGlucose", id = BaseColumns._ID) public class ShareGlucose extends Model { public Context mContext; @Expose @Column(name = "DT") public String DT; @Expose @Column(name = "ST") public String ST; @Expose @Column(name = "Trend") public double Trend; @Expose @Column(name = "Value") public double Value; @Expose @Column(name = "WT") public String WT; public void processShareData(Context context) { Log.d("SHARE", "Share Data being processed!"); // TODO maybe set this up?? // mContext = context; // Log.d("SHARE", "Timestamp before parsing: " + WT); // Log.d("SHARE", "Timestamp before parsing: " + WT.replaceAll("[^\\d.]", "")); // // double timestamp = (Double.parseDouble(WT.replaceAll("[^\\d.]", ""))); // Log.d("SHARE", "Timestamp: " + timestamp); // if (!Bg.alreadyExists(timestamp)) { // Log.d("SHARE", "Data looks new!!"); // Bg bg = new Bg(); // bg.direction = slopeDirection(); // bg.battery = Integer.toString(getBatteryLevel()); // bg.bgdelta = calculateDelta(timestamp, Value); // bg.datetime = timestamp; // bg.sgv = Integer.toString((int) Value); // bg.save(); // DataCollectionService.newDataArrived(mContext, true); // Log.d("SHARE", "Share Data Processed Successfully!"); // } else { // Log.d("SHARE", "A Bg Value similar to this timestamp already exists."); // } } public String slopeDirection() { switch((int) Trend) { case 1: return "DoubleUp"; case 2: return "SingleUp"; case 3: return "FortyFiveUp"; case 4: return "Flat"; case 5: return "FortyFiveDown"; case 6: return "SingleDown"; case 7: return "DoubleDown"; default: return ""; } } public int getBatteryLevel() { Intent batteryIntent = mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); if(level == -1 || scale == -1) { return 50; } return (int)(((float)level / (float)scale) * 100.0f); } public double calculateDelta(double timestamp, double currentValue) { // Bg bg = Bg.mostRecentBefore(timestamp); // if (bg != null && Math.abs(bg.datetime - timestamp) < (60*1000*15)) { // return (bg.sgv_double() - currentValue); // } else { return 0; // } } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ShareModels/ShareRest.java ================================================ package com.eveningoutpost.dexdrip.ShareModels; import android.content.Context; import android.content.SharedPreferences; import android.os.AsyncTask; import android.preference.PreferenceManager; import android.util.Log; import com.eveningoutpost.dexdrip.Models.BgReading; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.squareup.okhttp.OkHttpClient; import java.security.cert.CertificateException; import java.util.Date; import java.util.HashMap; import java.util.Map; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import retrofit.Callback; import retrofit.RequestInterceptor; import retrofit.RestAdapter; import retrofit.RetrofitError; import retrofit.android.AndroidLog; import retrofit.client.OkClient; import retrofit.client.Response; import retrofit.converter.GsonConverter; import retrofit.mime.TypedByteArray; /** * Created by stephenblack on 12/26/14. */ public class ShareRest { private Context mContext; private String login; private String password; private SharedPreferences prefs; OkClient client; public static Gson gson = new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() .create(); public ShareRest(Context context) { client = getOkClient(); mContext = context; prefs = PreferenceManager.getDefaultSharedPreferences(context); login = prefs.getString("dexcom_account_name", ""); password = prefs.getString("dexcom_account_password", ""); } public boolean getBgData() { if (prefs.getBoolean("share_poll", false) && login.compareTo("") != 0 && password.compareTo("") != 0) { return loginAndGetData(); } else { return false; } } public boolean sendBgData(BgReading bg) { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mContext); String receiverSn = preferences.getString("share_key", "SM00000000").toUpperCase(); if (prefs.getBoolean("share_upload", false) && login.compareTo("") != 0 && password.compareTo("") != 0 && receiverSn.compareTo("SM00000000") != 0) { return loginAndSendData(bg); } else { return false; } } private boolean loginAndGetData() { try { dexcomShareAuthorizeInterface().getSessionId(new ShareAuthenticationBody(password, login), new Callback() { @Override public void success(Object o, Response response) { Log.d("ShareRest", "Success!! got a response on auth."); String returnedSessionId = new String(((TypedByteArray) response.getBody()).getBytes()).replace("\"", ""); getBgData(returnedSessionId); } @Override public void failure(RetrofitError retrofitError) { Log.e("RETROFIT ERROR: ", ""+retrofitError.toString()); } }); return true; } catch (Exception e) { Log.e("REST CALL ERROR: ", "BOOOO"); return false; } } private boolean loginAndSendData(final BgReading bg) { try { dexcomShareAuthorizeInterface().getSessionId(new ShareAuthenticationBody(password, login), new Callback() { @Override public void success(Object o, Response response) { Log.d("ShareRest", "Success!! got a response on auth."); String returnedSessionId = new String(((TypedByteArray) response.getBody()).getBytes()).replace("\"", ""); sendBgData(returnedSessionId, bg); } @Override public void failure(RetrofitError retrofitError) { Log.e("RETROFIT ERROR: ", ""+retrofitError.toString()); } }); return true; } catch (Exception e) { Log.e("REST CALL ERROR: ", "BOOOO"); return false; } } private void getBgData(String sessionId) { DataFetcher dataFetcher = new DataFetcher(mContext, sessionId); dataFetcher.execute((Void) null); } private void sendBgData(String sessionId, BgReading bg) { DataSender dataSender = new DataSender(mContext, sessionId, bg); dataSender.execute((Void) null); } private DexcomShareInterface dexcomShareAuthorizeInterface() { RestAdapter adapter = authoirizeAdapterBuilder().build(); DexcomShareInterface dexcomShareInterface = adapter.create(DexcomShareInterface.class); return dexcomShareInterface; } private DexcomShareInterface dexcomShareGetBgInterface() { RestAdapter adapter = getBgAdapterBuilder().build(); DexcomShareInterface dexcomShareInterface = adapter.create(DexcomShareInterface.class); return dexcomShareInterface; } private DexcomShareInterface dexcomShareSendBgInterface() { RestAdapter adapter = authoirizeAdapterBuilder().build(); DexcomShareInterface dexcomShareInterface = adapter.create(DexcomShareInterface.class); return dexcomShareInterface; } private DexcomShareInterface checkSessionActive() { RestAdapter adapter = getBgAdapterBuilder().build(); DexcomShareInterface checkSessionActive = adapter.create(DexcomShareInterface.class); return checkSessionActive; } private RestAdapter.Builder authoirizeAdapterBuilder() { RestAdapter.Builder adapterBuilder = new RestAdapter.Builder(); adapterBuilder .setClient(client) .setLogLevel(RestAdapter.LogLevel.FULL).setLog(new AndroidLog("SHAREREST")) .setEndpoint("https://share1.dexcom.com/ShareWebServices/Services") .setRequestInterceptor(authorizationRequestInterceptor) .setConverter(new GsonConverter(new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() .create())); return adapterBuilder; } private RestAdapter.Builder getBgAdapterBuilder() { RestAdapter.Builder adapterBuilder = new RestAdapter.Builder(); adapterBuilder .setClient(client) .setLogLevel(RestAdapter.LogLevel.FULL).setLog(new AndroidLog("SHAREREST")) .setEndpoint("https://share1.dexcom.com/ShareWebServices/Services") .setRequestInterceptor(getBgRequestInterceptor) .setConverter(new GsonConverter(new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() .create())); return adapterBuilder; } RequestInterceptor authorizationRequestInterceptor = new RequestInterceptor() { @Override public void intercept(RequestInterceptor.RequestFacade request) { request.addHeader("User-Agent", "Dexcom Share/3.0.2.11 CFNetwork/711.2.23 Darwin/14.0.0"); request.addHeader("Content-Type", "application/json"); request.addHeader("Accept", "application/json"); } }; RequestInterceptor getBgRequestInterceptor = new RequestInterceptor() { @Override public void intercept(RequestInterceptor.RequestFacade request) { request.addHeader("User-Agent", "Dexcom Share/3.0.2.11 CFNetwork/711.2.23 Darwin/14.0.0"); request.addHeader("Content-Type", "application/json"); request.addHeader("Content-Length", "0"); request.addHeader("Accept", "application/json"); } }; public OkHttpClient getOkHttpClient() { try { final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { @Override public void checkClientTrusted( java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted( java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { } @Override public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } } }; final SSLContext sslContext = SSLContext.getInstance("SSL"); sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); OkHttpClient okHttpClient = new OkHttpClient(); okHttpClient.setSslSocketFactory(sslSocketFactory); okHttpClient.setHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }); return okHttpClient; } catch (Exception e) { throw new RuntimeException(e); } } public OkClient getOkClient (){ OkHttpClient client1 = getOkHttpClient(); OkClient _client = new OkClient(client1); return _client; } public Map queryParamMap(String sessionId) { Map map = new HashMap(); map.put("sessionID", sessionId); map.put("minutes", String.valueOf(minutesCount())); map.put("maxCount", String.valueOf(requestCount())); return map; } public class DataFetcher extends AsyncTask { Context mContext; String mSessionId; DataFetcher(Context context, String sessionId) { mContext = context; mSessionId = sessionId; } @Override protected Boolean doInBackground(Void... params) { try { try { final ShareGlucose[] shareGlucoses = dexcomShareGetBgInterface().getShareBg(queryParamMap(mSessionId)); Log.d("REST Success: ", "YAY!"); if(shareGlucoses != null && shareGlucoses.length > 0) { for (ShareGlucose shareGlucose : shareGlucoses) { shareGlucose.processShareData(mContext); } return true; } return false; } catch (Exception e) { Log.d("REST CALL ERROR: ", "BOOOO"); return false; } } catch (RetrofitError e) { Log.d("Retrofit Error: ", "BOOOO"); } catch (Exception ex) { Log.d("Unrecognized Error: ", "BOOOO"); } return false; } } public class DataSender extends AsyncTask { Context mContext; String mSessionId; BgReading mBg; DataSender(Context context, String sessionId, BgReading bg) { mContext = context; mSessionId = sessionId; mBg = bg; } @Override protected Boolean doInBackground(Void... params) { try { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mContext); String receiverSn = preferences.getString("share_key", "SM00000000").toUpperCase(); dexcomShareSendBgInterface().uploadBGRecords(querySessionMap(mSessionId), new ShareUploadPayload(receiverSn, mBg), new Callback() { @Override public void success(Object o, Response response) { Log.d("ShareRest", "Success!! Uploaded!!"); } @Override public void failure(RetrofitError retrofitError) { Log.e("RETROFIT ERROR: ", ""+retrofitError.toString()); } }); } catch (RetrofitError e) { Log.d("Retrofit Error: ", "BOOOO"); } catch (Exception ex) { Log.d("Unrecognized Error: ", "BOOOO"); } return false; } } public int requestCount() { BgReading bg = BgReading.last(); if(bg != null) { return 20; } else if (bg.timestamp < new Date().getTime()) { return Math.min((int) Math.ceil(((new Date().getTime() - bg.timestamp) / (5 * 1000 * 60))), 10); } else { return 1; } } public int minutesCount() { BgReading bg = BgReading.last(); if(bg != null && bg.timestamp < new Date().getTime()) { return Math.min((int) Math.ceil(((new Date().getTime() - bg.timestamp) / (1000 * 60))), 1440); } else { return 1440; } } public Map querySessionMap(String sessionId) { Map map = new HashMap(); map.put("sessionID", sessionId); return map; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ShareModels/ShareUploadPayload.java ================================================ package com.eveningoutpost.dexdrip.ShareModels; import com.eveningoutpost.dexdrip.Models.BgReading; import com.google.gson.annotations.Expose; import java.util.ArrayList; import java.util.List; /** * Created by stephenblack on 3/19/15. */ public class ShareUploadPayload { @Expose public String SN; @Expose public Egv[] Egvs; @Expose public long TA = -5; public ShareUploadPayload(String sn, BgReading bg) { this.SN = sn; List egvList = new ArrayList(); egvList.add(new Egv(bg)); this.Egvs = egvList.toArray(new Egv[egvList.size()]); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/ShareTest.java ================================================ package com.eveningoutpost.dexdrip; import android.app.Activity; import android.app.AlarmManager; import android.app.PendingIntent; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; import android.text.TextUtils; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import com.activeandroid.query.Select; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.PacketBuilder; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.ReadData; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.ReadDataShare; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.CalRecord; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.EGVRecord; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.records.SensorRecord; import com.eveningoutpost.dexdrip.Models.ActiveBluetoothDevice; import com.eveningoutpost.dexdrip.Models.BgReading; import com.eveningoutpost.dexdrip.Models.Calibration; import com.eveningoutpost.dexdrip.UtilityModels.DexShareAttributes; import com.eveningoutpost.dexdrip.UtilityModels.ForegroundServiceStarter; import com.eveningoutpost.dexdrip.UtilityModels.HM10Attributes; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.UUID; import rx.Observable; import rx.functions.Action1; public class ShareTest extends Activity { private final static String TAG = ShareTest.class.getSimpleName(); Button button; Button closeButton; Button readButton; Button bondButton; TextView details; private String mDeviceName; private String mDeviceAddress; private boolean is_connected = false; private boolean reconnecting = false; SharedPreferences prefs; private BluetoothManager mBluetoothManager; private BluetoothAdapter mBluetoothAdapter; private String mBluetoothDeviceAddress; private BluetoothGatt mBluetoothGatt; private ForegroundServiceStarter foregroundServiceStarter; private int mConnectionState = STATE_DISCONNECTED; private BluetoothDevice device; int mStartMode; private Context mContext = null; private static final int STATE_DISCONNECTED = BluetoothProfile.STATE_DISCONNECTED; private static final int STATE_DISCONNECTING = BluetoothProfile.STATE_DISCONNECTING; private static final int STATE_CONNECTING = BluetoothProfile.STATE_CONNECTING; private static final int STATE_CONNECTED = BluetoothProfile.STATE_CONNECTED; private BluetoothGattService mShareService; private BluetoothGattCharacteristic mAuthenticationCharacteristic; private BluetoothGattCharacteristic mSendDataCharacteristic; private BluetoothGattCharacteristic mReceiveDataCharacteristic; private BluetoothGattCharacteristic mHeartBeatCharacteristic; private BluetoothGattCharacteristic mCommandCharacteristic; private BluetoothGattCharacteristic mResponseCharacteristic; //Gatt Tasks public final int GATT_NOTHING = 0; public final int GATT_SETUP = 1; public final int GATT_WRITING_COMMANDS = 2; public final int GATT_READING_RESPONSE = 3; public int successfulWrites; //RXJAVA FUN Action1 mDataResponseListener; public ReadDataShare mReadDataShare; public int currentGattTask; public int step; public List writePackets; public int recordType; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_share_test); button = (Button) findViewById(R.id.connect); closeButton = (Button) findViewById(R.id.closeConnect); bondButton = (Button) findViewById(R.id.bond); readButton = (Button) findViewById(R.id.read); details = (TextView) findViewById(R.id.connection_details); addListenerOnButton(); addListenerOnCloseButton(); IntentFilter intent = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED); registerReceiver(mPairReceiver, intent); } @Override public void onDestroy() { super.onDestroy(); close(); Log.w(TAG, "CLOSING CONNECTION"); } public void addListenerOnButton() { button.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { attemptConnection(); } }); readButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { attemptRead(); } }); bondButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { bond(mBluetoothGatt); } }); } public void addListenerOnCloseButton() { closeButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { close(); details.setText(""); } }); } private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (newState == BluetoothProfile.STATE_CONNECTED) { mBluetoothGatt = gatt; mConnectionState = STATE_CONNECTED; ActiveBluetoothDevice.connected(); Log.w(TAG, "Connected to GATT server."); Log.w(TAG, "Connection state: Bonded - " + device.getBondState()); if (device.getBondState() == BluetoothDevice.BOND_BONDED) { currentGattTask = GATT_SETUP; mBluetoothGatt.discoverServices(); } else { device.setPin("000000".getBytes()); device.createBond(); } } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { mConnectionState = STATE_DISCONNECTED; ActiveBluetoothDevice.disconnected(); Log.w(TAG, "Disconnected from GATT server."); } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { Log.w(TAG, "Services Discovered: " + status); authenticateConnection(gatt); } } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { Log.w(TAG, "Characteristic Read"); byte[] value = characteristic.getValue(); if(value != null) { Log.w(TAG, "VALUE" + value); } else { Log.w(TAG, "Characteristic was null"); } nextGattStep(); } else { Log.w(TAG, "Characteristic failed to read"); } } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { Log.w(TAG, "Characteristic changed"); UUID charUuid = characteristic.getUuid(); Log.w(TAG, "Characteristic Update Received: " + charUuid); if(charUuid.compareTo(mResponseCharacteristic.getUuid()) == 0) { Log.w(TAG, "mResponseCharacteristic Update"); } if(charUuid.compareTo(mCommandCharacteristic.getUuid()) == 0) { Log.w(TAG, "mCommandCharacteristic Update"); } if(charUuid.compareTo(mHeartBeatCharacteristic.getUuid()) == 0) { Log.w(TAG, "mHeartBeatCharacteristic Update"); } if(charUuid.compareTo(mReceiveDataCharacteristic.getUuid()) == 0) { Log.w(TAG, "mReceiveDataCharacteristic Update"); byte[] value = characteristic.getValue(); if(value != null) { Log.w(TAG, "Characteristic: " + value); Log.w(TAG, "Characteristic: " + value.toString()); Log.w(TAG, "Characteristic getstring: " + characteristic.getStringValue(0)); Log.w(TAG, "SUBSCRIBED TO RESPONSE LISTENER"); Observable.just(characteristic.getValue()).subscribe(mDataResponseListener); } else { Log.w(TAG, "Characteristic was null"); } } Log.w(TAG, "NEW VALUE: " + characteristic.getValue().toString()); } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { Log.w(TAG, "Wrote a discriptor, status: " + status); if(step == 2 && currentGattTask == GATT_SETUP) { setListeners(2); } else if(step == 3) { setListeners(3); } else if(step == 4) { setListeners(4); } else if(step == 5) { Log.w(TAG, "Done setting Listeners"); } } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { Log.w(TAG, "Wrote a characteristic: " + status); nextGattStep(); } }; public void attemptConnection() { mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); if (device != null) { details.append("\nConnection state: " + " Device is not null"); mConnectionState = mBluetoothManager.getConnectionState(device, BluetoothProfile.GATT); } Log.w(TAG, "Connection state: " + mConnectionState); details.append("\nConnection state: " + mConnectionState); if (mConnectionState == STATE_DISCONNECTED || mConnectionState == STATE_DISCONNECTING) { ActiveBluetoothDevice btDevice = new Select().from(ActiveBluetoothDevice.class) .orderBy("_ID desc") .executeSingle(); if (btDevice != null) { details.append("\nBT Device: " + btDevice.name); mDeviceName = btDevice.name; mDeviceAddress = btDevice.address; mBluetoothAdapter = mBluetoothManager.getAdapter(); boolean newConnection = true; if(newConnection) { is_connected = connect(mDeviceAddress); details.append("\nConnecting...: "); } } } } public void attemptRead() { final ReadDataShare readData = new ReadDataShare(this); final Action1 systemTimeListener = new Action1() { @Override public void call(Long s) { Log.d(TAG, "Made the full round trip, got " + s + " as the system time"); Log.d("SYSTTIME", "Made the full round trip, got " + s + " as the system time"); final long addativeSystemTimeOffset = new Date().getTime() - s; Log.d(TAG, "Made the full round trip, got " + addativeSystemTimeOffset + " offset"); Log.d("SYSTTIME", "Made the full round trip, got " + addativeSystemTimeOffset + " offset"); final Action1 calRecordListener = new Action1() { @Override public void call(CalRecord[] calRecords) { Log.d(TAG, "Made the full round trip, got " + calRecords.length + " Cal Records"); Calibration.create(calRecords, addativeSystemTimeOffset, getApplicationContext()); final Action1 sensorRecordListener = new Action1() { @Override public void call(SensorRecord[] sensorRecords) { Log.d(TAG, "Made the full round trip, got " + sensorRecords.length + " Sensor Records"); BgReading.create(sensorRecords, addativeSystemTimeOffset, getApplicationContext()); final Action1 evgRecordListener = new Action1() { @Override public void call(EGVRecord[] egvRecords) { Log.d(TAG, "Made the full round trip, got " + egvRecords.length + " EVG Records"); BgReading.create(egvRecords, addativeSystemTimeOffset, getApplicationContext()); } }; readData.getRecentEGVs(evgRecordListener); } }; readData.getRecentSensorRecords(sensorRecordListener); } }; readData.getRecentCalRecords(calRecordListener); } }; readData.readSystemTime(systemTimeListener); } public void bond(BluetoothGatt gatt) { reconnecting = true; attemptConnection(); } public boolean connect(final String address) { details.append("\nConnecting to device"); Log.w(TAG, "CONNECTING TO DEVICE"); if (mBluetoothAdapter == null || address == null) { details.append("\nBT adapter is null"); Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); return false; } if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) && mBluetoothGatt != null) { details.append("\nTrying to use an existing mBluetoothGatt for connection."); if (mBluetoothGatt.connect()) { mConnectionState = STATE_CONNECTING; return true; } else { return false; } } else { device = mBluetoothAdapter.getRemoteDevice(address); device.setPin("000000".getBytes()); if (device == null) { Log.w(TAG, "Device not found. Unable to connect."); details.append("\nDevice not found. Unable to connect."); return false; } mBluetoothGatt = device.connectGatt(getApplicationContext(), true, mGattCallback); Log.w(TAG, "Trying to create a new connection."); details.append("\nTrying to create a new connection to device"); mConnectionState = STATE_CONNECTING; return true; } } public void authenticateConnection(BluetoothGatt bluetoothGatt) { Log.w(TAG, "Trying to auth"); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); String receiverSn = prefs.getString("share_key", "SM00000000").toUpperCase(); if(bluetoothGatt != null) { mBluetoothGatt = bluetoothGatt; mShareService = mBluetoothGatt.getService(DexShareAttributes.CradleService); if (mShareService != null) { mAuthenticationCharacteristic = mShareService.getCharacteristic(DexShareAttributes.AuthenticationCode); if(mAuthenticationCharacteristic != null) { Log.w(TAG, "Auth Characteristic found: " + mAuthenticationCharacteristic.toString()); mAuthenticationCharacteristic.setValue((receiverSn + "000000").getBytes(StandardCharsets.US_ASCII)); currentGattTask = GATT_SETUP; step = 1; bluetoothGatt.writeCharacteristic(mAuthenticationCharacteristic); } else { Log.w(TAG, "Authentication Characteristic IS NULL"); } } else { Log.w(TAG, "CRADLE SERVICE IS NULL"); } } } public void assignCharacteristics() { mSendDataCharacteristic = mShareService.getCharacteristic(DexShareAttributes.ShareMessageReceiver); mReceiveDataCharacteristic = mShareService.getCharacteristic(DexShareAttributes.ShareMessageResponse); mHeartBeatCharacteristic = mShareService.getCharacteristic(DexShareAttributes.HeartBeat); mCommandCharacteristic = mShareService.getCharacteristic(DexShareAttributes.Command); mResponseCharacteristic = mShareService.getCharacteristic(DexShareAttributes.Response); } public void setListeners(int listener_number) { Log.w(TAG, "Setting Listener: #" + listener_number); if(listener_number == 1) { step = 3; setCharacteristicIndication(mReceiveDataCharacteristic); } else if(listener_number == 3) { setCharacteristicIndication(mResponseCharacteristic); step = 5; } } public void disconnect() { if (mBluetoothAdapter == null || mBluetoothGatt == null) { Log.w(TAG, "BluetoothAdapter not initialized"); return; } mBluetoothGatt.disconnect(); } public void close() { disconnect(); if (mBluetoothGatt == null) { return; } mBluetoothGatt.close(); mBluetoothGatt = null; mConnectionState = STATE_DISCONNECTED; Log.w(TAG, "bt Disconnected"); } public void readCharacteristic(BluetoothGattCharacteristic characteristic) { if (mBluetoothAdapter == null || mBluetoothGatt == null) { Log.w(TAG, "BluetoothAdapter not initialized"); return; } mBluetoothGatt.readCharacteristic(characteristic); } public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic){ setCharacteristicNotification(characteristic, true);} public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) { Log.w(TAG, "Characteristic setting notification"); mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); Log.w(TAG, "UUID FOUND: " + characteristic.getUuid()); BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(HM10Attributes.CLIENT_CHARACTERISTIC_CONFIG)); Log.w(TAG, "Descriptor found: " + descriptor.getUuid()); descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); mBluetoothGatt.writeDescriptor(descriptor); } public void setCharacteristicIndication(BluetoothGattCharacteristic characteristic){ setCharacteristicIndication(characteristic, true);} public void setCharacteristicIndication(BluetoothGattCharacteristic characteristic, boolean enabled) { Log.w(TAG, "Characteristic setting notification"); mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); Log.w(TAG, "UUID FOUND: " + characteristic.getUuid()); BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(HM10Attributes.CLIENT_CHARACTERISTIC_CONFIG)); Log.w(TAG, "Descriptor found: " + descriptor.getUuid()); descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE); mBluetoothGatt.writeDescriptor(descriptor); } private final BroadcastReceiver mPairReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) { final int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR); final int prevState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, BluetoothDevice.ERROR); if (state == BluetoothDevice.BOND_BONDED) { Log.d(TAG, "CALLBACK RECIEVED Bonded"); currentGattTask = GATT_SETUP; mBluetoothGatt.discoverServices(); } else if (state == BluetoothDevice.BOND_NONE){ Log.d(TAG, "CALLBACK RECIEVED: Not Bonded"); Toast.makeText(getApplicationContext(), "unBonded", Toast.LENGTH_LONG).show(); } else if (state == BluetoothDevice.BOND_BONDING) { Log.d(TAG, "CALLBACK RECIEVED: Trying to bond"); Toast.makeText(getApplicationContext(), "trying to bond", Toast.LENGTH_LONG).show(); } } } }; public void writeCommand(List packets, int aRecordType, Action1 dataResponseListener) { mDataResponseListener = dataResponseListener; successfulWrites = 0; writePackets = packets; recordType = aRecordType; step = 0; currentGattTask = GATT_WRITING_COMMANDS; gattWritingStep(); } private void nextGattStep() { Log.d(TAG, "Next Gatt Step"); step++; switch (currentGattTask) { case GATT_NOTHING: Log.d(TAG, "Next NOTHING: " + step); break; case GATT_SETUP: Log.d(TAG, "Next GATT SETUP: " + step); gattSetupStep(); break; case GATT_WRITING_COMMANDS: Log.d(TAG, "Next GATT WRITING: " + step); gattWritingStep(); break; } } public void clearGattTask() { currentGattTask = GATT_NOTHING; step = 0; } private void gattSetupStep() { step = 1; assignCharacteristics(); setListeners(1); } private void gattWritingStep() { Log.d(TAG, "Writing command to the Gatt, step: " + step); int index = step; if (index <= (writePackets.size() - 1)) { Log.d(TAG, "Writing: " + writePackets.get(index) + " index: " + index); mSendDataCharacteristic.setValue(writePackets.get(index)); mBluetoothGatt.writeCharacteristic(mSendDataCharacteristic); } else { clearGattTask(); } } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/StartNewSensor.java ================================================ package com.eveningoutpost.dexdrip; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.v4.widget.DrawerLayout; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.DatePicker; import android.widget.TimePicker; import android.widget.Toast; import com.eveningoutpost.dexdrip.UtilityModels.CollectionServiceStarter; import java.util.Calendar; import java.util.List; public class StartNewSensor extends Activity implements NavigationDrawerFragment.NavigationDrawerCallbacks { private String menu_name = "Start Sensor"; private NavigationDrawerFragment mNavigationDrawerFragment; public Button button; public DatePicker dp; public TimePicker tp; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if(Sensor.isActive() == false) { setContentView(R.layout.activity_start_new_sensor); button = (Button)findViewById(R.id.startNewSensor); dp = (DatePicker)findViewById(R.id.datePicker); tp = (TimePicker)findViewById(R.id.timePicker); addListenerOnButton(); } else { Intent intent = new Intent(this, StopSensor.class); startActivity(intent); finish(); } } @Override protected void onResume(){ super.onResume(); NavigationDrawerFragment mNavigationDrawerFragment; mNavigationDrawerFragment = (NavigationDrawerFragment) getFragmentManager().findFragmentById(R.id.navigation_drawer); mNavigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout), menu_name, this); } @Override public void onNavigationDrawerItemSelected(int position) { NavDrawerBuilder navDrawerBuilder = new NavDrawerBuilder(getApplicationContext()); List menu_option_list = navDrawerBuilder.nav_drawer_options; int menu_position = menu_option_list.indexOf(menu_name); if (position != menu_position) { List intent_list = navDrawerBuilder.nav_drawer_intents; Intent[] intent_array = intent_list.toArray(new Intent[intent_list.size()]); startActivity(intent_array[position]); finish(); } } public void addListenerOnButton() { button = (Button)findViewById(R.id.startNewSensor); button.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { Calendar calendar = Calendar.getInstance(); calendar.set(dp.getYear(), dp.getMonth(), dp.getDayOfMonth(), tp.getCurrentHour(), tp.getCurrentMinute(), 0); long startTime = calendar.getTime().getTime(); Sensor sensor = Sensor.create(startTime); Log.w("NEW SENSOR", "Sensor started at " + startTime); Toast.makeText(getApplicationContext(), "NEW SENSOR STARTED", Toast.LENGTH_LONG).show(); Intent intent = new Intent(getApplicationContext(), Home.class); CollectionServiceStarter.newStart(getApplicationContext()); startActivity(intent); finish(); } }); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/StopSensor.java ================================================ package com.eveningoutpost.dexdrip; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.v4.widget.DrawerLayout; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.Toast; import java.util.Date; public class StopSensor extends Activity implements NavigationDrawerFragment.NavigationDrawerCallbacks { private String menu_name = "Stop Sensor"; private NavigationDrawerFragment mNavigationDrawerFragment; public Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if(Sensor.isActive() == false) { Intent intent = new Intent(this, StartNewSensor.class); startActivity(intent); finish(); } else { setContentView(R.layout.activity_stop_sensor); button = (Button)findViewById(R.id.stop_sensor); addListenerOnButton(); } } @Override protected void onResume(){ super.onResume(); mNavigationDrawerFragment = (NavigationDrawerFragment) getFragmentManager().findFragmentById(R.id.navigation_drawer); mNavigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout), menu_name, this); } @Override public void onNavigationDrawerItemSelected(int position) { mNavigationDrawerFragment.swapContext(position); } public void addListenerOnButton() { button = (Button)findViewById(R.id.stop_sensor); button.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { Sensor sensor = Sensor.currentSensor(); sensor.stopped_at = new Date().getTime(); Log.w("NEW SENSOR", "Sensor stopped at " + sensor.stopped_at); sensor.save(); Toast.makeText(getApplicationContext(), "Sensor stopped", Toast.LENGTH_LONG).show(); Intent intent = new Intent(getApplicationContext(), Home.class); startActivity(intent); finish(); } }); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/SystemStatus.java ================================================ package com.eveningoutpost.dexdrip; import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v4.widget.DrawerLayout; import android.text.TextUtils; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.ImageButton; import android.widget.TextView; import com.eveningoutpost.dexdrip.Models.ActiveBluetoothDevice; import com.eveningoutpost.dexdrip.Models.Calibration; import com.eveningoutpost.dexdrip.UtilityModels.CollectionServiceStarter; import java.lang.reflect.Method; public class SystemStatus extends Activity implements NavigationDrawerFragment.NavigationDrawerCallbacks { private String menu_name = "System Status"; private NavigationDrawerFragment mNavigationDrawerFragment; public TextView collection_method; public TextView current_device; public TextView connection_status; public TextView notes; public Button restart_collection_service; public Button forget_device; public ImageButton refresh; public SharedPreferences prefs; public BluetoothManager mBluetoothManager; public ActiveBluetoothDevice activeBluetoothDevice; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_system_status); prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); mNavigationDrawerFragment = (NavigationDrawerFragment) getFragmentManager().findFragmentById(R.id.navigation_drawer); mNavigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout), menu_name, this); collection_method = (TextView)findViewById(R.id.collection_method); connection_status = (TextView)findViewById(R.id.connection_status); current_device = (TextView)findViewById(R.id.remembered_device); notes = (TextView)findViewById(R.id.other_notes); restart_collection_service = (Button)findViewById(R.id.restart_collection_service); forget_device = (Button)findViewById(R.id.forget_device); refresh = (ImageButton)findViewById(R.id.refresh_current_values); set_current_values(); restartButtonListener(); forgetDeviceListener(); refreshButtonListener(); } @Override public void onNavigationDrawerItemSelected(int position) { mNavigationDrawerFragment.swapContext(position); } private void set_current_values() { activeBluetoothDevice = ActiveBluetoothDevice.first(); mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); setCollectionMethod(); setCurrentDevice(); setConnectionStatus(); setNotes(); } public void setCollectionMethod() { collection_method.setText(prefs.getString("dex_collection_method", "BluetoothWixel")); } public void setCurrentDevice() { if(activeBluetoothDevice != null) { current_device.setText(activeBluetoothDevice.name); } else { current_device.setText("None Set"); } } public void setConnectionStatus() { boolean connected = false; if (mBluetoothManager != null && activeBluetoothDevice != null) { for (BluetoothDevice bluetoothDevice : mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT)) { if (bluetoothDevice.getAddress().compareTo(activeBluetoothDevice.address) == 0) { connected = true; } } } if(connected) { connection_status.setText("Connected"); } else { connection_status.setText("Not Connected"); } } public void setNotes() { if(mBluetoothManager == null) { notes.append("\n- This device does not seem to support bluetooth"); } else { if(!mBluetoothManager.getAdapter().isEnabled()) { notes.append("\n- Bluetooth seems to be turned off"); } else { if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR2){ notes.append("\n- The android version of this device is not compatible with Bluetooth Low Energy"); } } } } public void restartButtonListener() { restart_collection_service.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { CollectionServiceStarter.restartCollectionService(getApplicationContext()); set_current_values(); } }); } public void forgetDeviceListener() { forget_device.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { if(mBluetoothManager != null && ActiveBluetoothDevice.first() != null) { BluetoothAdapter bluetoothAdapter = mBluetoothManager.getAdapter(); if(bluetoothAdapter != null) { for( BluetoothDevice bluetoothDevice : bluetoothAdapter.getBondedDevices()) { if(bluetoothDevice.getAddress().compareTo(ActiveBluetoothDevice.first().address) == 0) { try { Method m = bluetoothDevice.getClass().getMethod("removeBond", (Class[]) null); m.invoke(bluetoothDevice, (Object[]) null); notes.append("\n- Bluetooth unbonded, if using share tell it to forget your device."); notes.append("\n- Scan for devices again to set connection back up!"); } catch (Exception e) { Log.e("SystemStatus", e.getMessage()); } } } ActiveBluetoothDevice.forget(); bluetoothAdapter.disable(); bluetoothAdapter.enable(); try { wait(1000); } catch(Exception e) { Log.e("SystemStatus", "Error stalling"); } } } CollectionServiceStarter.restartCollectionService(getApplicationContext()); set_current_values(); } }); } public void refreshButtonListener() { refresh.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { set_current_values(); } }); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Tables/BgReadingTable.java ================================================ package com.eveningoutpost.dexdrip.Tables; import android.app.ListActivity; import android.content.Context; import android.database.Cursor; import android.os.Bundle; import android.support.v4.widget.DrawerLayout; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ListAdapter; import android.widget.TextView; import com.eveningoutpost.dexdrip.Models.BgReading; import com.eveningoutpost.dexdrip.NavigationDrawerFragment; import com.eveningoutpost.dexdrip.R; import java.util.ArrayList; import java.util.Date; import java.util.List; public class BgReadingTable extends ListActivity implements NavigationDrawerFragment.NavigationDrawerCallbacks { private String menu_name = "BG Data Table"; private NavigationDrawerFragment mNavigationDrawerFragment; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.raw_data_list); } @Override protected void onResume() { super.onResume(); mNavigationDrawerFragment = (NavigationDrawerFragment) getFragmentManager().findFragmentById(R.id.navigation_drawer); mNavigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout), menu_name, this); getData(); } @Override public void onNavigationDrawerItemSelected(int position) { mNavigationDrawerFragment.swapContext(position); } private void getData() { final List latest = BgReading.latest(50); ListAdapter adapter = new BgReadingAdapter(this, latest); this.setListAdapter(adapter); } public static class BgReadingCursorAdapterViewHolder { TextView raw_data_id; TextView raw_data_value; TextView raw_data_slope; TextView raw_data_timestamp; public BgReadingCursorAdapterViewHolder(View root) { raw_data_id = (TextView) root.findViewById(R.id.raw_data_id); raw_data_value = (TextView) root.findViewById(R.id.raw_data_value); raw_data_slope = (TextView) root.findViewById(R.id.raw_data_slope); raw_data_timestamp = (TextView) root.findViewById(R.id.raw_data_timestamp); } } public static class BgReadingAdapter extends BaseAdapter { private final Context context; private final List readings; public BgReadingAdapter(Context context, List readings) { this.context = context; if(readings == null) readings = new ArrayList<>(); this.readings = readings; } public View newView(Context context, ViewGroup parent) { final View view = LayoutInflater.from(context).inflate(R.layout.raw_data_list_item, parent, false); final BgReadingCursorAdapterViewHolder holder = new BgReadingCursorAdapterViewHolder(view); view.setTag(holder); return view; } public void bindView(View view, Context context, BgReading bgReading) { final BgReadingCursorAdapterViewHolder tag = (BgReadingCursorAdapterViewHolder) view.getTag(); tag.raw_data_id.setText(Double.toString(bgReading.calculated_value)); tag.raw_data_value.setText(Double.toString(bgReading.age_adjusted_raw_value)); tag.raw_data_slope.setText(Double.toString(bgReading.raw_data)); tag.raw_data_timestamp.setText(new Date(bgReading.timestamp).toString()); } @Override public int getCount() { return readings.size(); } @Override public BgReading getItem(int position) { return readings.get(position); } @Override public long getItemId(int position) { return getItem(position).getId(); } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) convertView = newView(context, parent); bindView(convertView, context, getItem(position)); return convertView; } } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Tables/CalibrationDataTable.java ================================================ package com.eveningoutpost.dexdrip.Tables; import android.app.ListActivity; import android.content.Context; import android.database.Cursor; import android.os.Bundle; import android.support.v4.widget.DrawerLayout; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; import com.activeandroid.Cache; import com.eveningoutpost.dexdrip.Models.Calibration; import com.eveningoutpost.dexdrip.NavigationDrawerFragment; import com.eveningoutpost.dexdrip.R; import java.util.ArrayList; import java.util.List; public class CalibrationDataTable extends ListActivity implements NavigationDrawerFragment.NavigationDrawerCallbacks { private String menu_name = "Calibration Data Table"; private NavigationDrawerFragment mNavigationDrawerFragment; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.raw_data_list); } @Override protected void onResume(){ super.onResume(); mNavigationDrawerFragment = (NavigationDrawerFragment) getFragmentManager().findFragmentById(R.id.navigation_drawer); mNavigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout), menu_name, this); getData(); } @Override public void onNavigationDrawerItemSelected(int position) { mNavigationDrawerFragment.swapContext(position); } private void getData() { final List latest = Calibration.latest(50); CalibrationDataCursorAdapter adapter = new CalibrationDataCursorAdapter(this, latest); this.setListAdapter(adapter); } public static class CalibrationDataCursorAdapterViewHolder { TextView raw_data_id; TextView raw_data_value; TextView raw_data_slope; TextView raw_data_timestamp; public CalibrationDataCursorAdapterViewHolder(View root) { raw_data_id = (TextView) root.findViewById(R.id.raw_data_id); raw_data_value = (TextView) root.findViewById(R.id.raw_data_value); raw_data_slope = (TextView) root.findViewById(R.id.raw_data_slope); raw_data_timestamp = (TextView) root.findViewById(R.id.raw_data_timestamp); } } public static class CalibrationDataCursorAdapter extends BaseAdapter { private final Context context; private final List calibrations; public CalibrationDataCursorAdapter(Context context, List calibrations) { this.context = context; if(calibrations == null) calibrations = new ArrayList<>(); this.calibrations = calibrations; } public View newView(Context context, ViewGroup parent) { final View view = LayoutInflater.from(context).inflate(R.layout.raw_data_list_item, parent, false); final CalibrationDataCursorAdapterViewHolder holder = new CalibrationDataCursorAdapterViewHolder(view); view.setTag(holder); return view; } public void bindView(View view, Context context, Calibration calibration) { final CalibrationDataCursorAdapterViewHolder tag = (CalibrationDataCursorAdapterViewHolder) view.getTag(); tag.raw_data_id.setText(Double.toString(calibration.bg)); tag.raw_data_value.setText(Double.toString(calibration.estimate_raw_at_time_of_calibration)); tag.raw_data_slope.setText(Double.toString(calibration.slope)); tag.raw_data_timestamp.setText(Double.toString(calibration.intercept)); } @Override public int getCount() { return calibrations.size(); } @Override public Calibration getItem(int position) { return calibrations.get(position); } @Override public long getItemId(int position) { return getItem(position).getId(); } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) convertView = newView(context, parent); bindView(convertView, context, getItem(position)); return convertView; } } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/Tables/SensorDataTable.java ================================================ package com.eveningoutpost.dexdrip.Tables; import android.app.ListActivity; import android.database.Cursor; import android.os.Bundle; import android.support.v4.widget.DrawerLayout; import android.view.View; import android.widget.SimpleCursorAdapter; import com.activeandroid.Cache; import com.eveningoutpost.dexdrip.NavigationDrawerFragment; import com.eveningoutpost.dexdrip.R; import java.util.ArrayList; public class SensorDataTable extends ListActivity implements NavigationDrawerFragment.NavigationDrawerCallbacks { private String menu_name = "Sensor Data Table"; private NavigationDrawerFragment mNavigationDrawerFragment; private ArrayList results = new ArrayList(); private View mRootView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.raw_data_list); } @Override protected void onResume(){ super.onResume(); mNavigationDrawerFragment = (NavigationDrawerFragment) getFragmentManager().findFragmentById(R.id.navigation_drawer); mNavigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout), menu_name, this); getData(); } @Override public void onNavigationDrawerItemSelected(int position) { mNavigationDrawerFragment.swapContext(position); } private void getData() { Cursor cursor = Cache.openDatabase().rawQuery("Select * from Sensors order by _ID desc", null); SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.raw_data_list_item, cursor, new String[] { "started_at", "latest_battery_level", "uuid", "uuid" }, new int[] { R.id.raw_data_id, R.id.raw_data_value , R.id.raw_data_slope, R.id.raw_data_timestamp }); this.setListAdapter(adapter); // ListView listView = (ListView) findViewById(R.id.list); // listView.setAdapter(adapter); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/UsbConnectedActivity.java ================================================ package com.eveningoutpost.dexdrip; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.SyncingService; public class UsbConnectedActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if(SyncingService.isG4Connected(getApplicationContext())){ Intent checkInIntent = new Intent(getApplicationContext(), CalibrationCheckInActivity.class); startActivity(checkInIntent); finish(); } else { //TODO: Put check for usb wixel in here as an elseif Intent homeIntent = new Intent(getApplicationContext(), Home.class); startActivity(homeIntent); finish(); } setContentView(R.layout.activity_usb_connected); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/UtilityModels/BgGraphBuilder.java ================================================ package com.eveningoutpost.dexdrip.UtilityModels; import android.content.Context; import android.content.SharedPreferences; import android.content.res.Configuration; import android.graphics.Color; import android.preference.PreferenceManager; import android.text.format.DateFormat; import com.eveningoutpost.dexdrip.Models.BgReading; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import java.util.TimeZone; import lecho.lib.hellocharts.model.Axis; import lecho.lib.hellocharts.model.AxisValue; import lecho.lib.hellocharts.model.Line; import lecho.lib.hellocharts.model.LineChartData; import lecho.lib.hellocharts.model.PointValue; import lecho.lib.hellocharts.model.Viewport; import lecho.lib.hellocharts.util.Utils; import lecho.lib.hellocharts.view.Chart; /** * Created by stephenblack on 11/15/14. */ public class BgGraphBuilder { public int fuzzer = (1000 * 30 * 5); public double end_time = (new Date().getTime() + (60000 * 10)) / fuzzer; public double start_time = end_time - ((60000 * 60 * 24)) / fuzzer; public Context context; public SharedPreferences prefs; public double highMark; public double lowMark; public double defaultMinY; public double defaultMaxY; public boolean doMgdl; final int pointSize; final int axisTextSize; final int previewAxisTextSize; final int hoursPreviewStep; private double endHour; private final int numValues =(60/5)*24; private final List bgReadings = BgReading.latestForGraph( numValues, (start_time * fuzzer)); private List inRangeValues = new ArrayList(); private List highValues = new ArrayList(); private List lowValues = new ArrayList(); private List rawInterpretedValues = new ArrayList(); public Viewport viewport; public BgGraphBuilder(Context context){ this.context = context; this.prefs = PreferenceManager.getDefaultSharedPreferences(context); this.highMark = Double.parseDouble(prefs.getString("highValue", "170")); this.lowMark = Double.parseDouble(prefs.getString("lowValue", "70")); this.doMgdl = (prefs.getString("units", "mgdl").compareTo("mgdl") == 0); defaultMinY = unitized(40); defaultMaxY = unitized(250); pointSize = isXLargeTablet() ? 5 : 3; axisTextSize = isXLargeTablet() ? 20 : Axis.DEFAULT_TEXT_SIZE_SP; previewAxisTextSize = isXLargeTablet() ? 12 : 5; hoursPreviewStep = isXLargeTablet() ? 2 : 1; } public LineChartData lineData() { LineChartData lineData = new LineChartData(defaultLines()); lineData.setAxisYLeft(yAxis()); lineData.setAxisXBottom(xAxis()); return lineData; } public LineChartData previewLineData() { LineChartData previewLineData = new LineChartData(lineData()); previewLineData.setAxisYLeft(yAxis()); previewLineData.setAxisXBottom(previewXAxis()); previewLineData.getLines().get(4).setPointRadius(2); previewLineData.getLines().get(5).setPointRadius(2); previewLineData.getLines().get(6).setPointRadius(2); return previewLineData; } public List defaultLines() { addBgReadingValues(); List lines = new ArrayList(); lines.add(minShowLine()); lines.add(maxShowLine()); lines.add(highLine()); lines.add(lowLine()); lines.add(inRangeValuesLine()); lines.add(lowValuesLine()); lines.add(highValuesLine()); lines.add(rawInterpretedLine()); return lines; } public Line highValuesLine() { Line highValuesLine = new Line(highValues); highValuesLine.setColor(Utils.COLOR_ORANGE); highValuesLine.setHasLines(false); highValuesLine.setPointRadius(pointSize); highValuesLine.setHasPoints(true); return highValuesLine; } public Line lowValuesLine() { Line lowValuesLine = new Line(lowValues); lowValuesLine.setColor(Color.parseColor("#C30909")); lowValuesLine.setHasLines(false); lowValuesLine.setPointRadius(pointSize); lowValuesLine.setHasPoints(true); return lowValuesLine; } public Line inRangeValuesLine() { Line inRangeValuesLine = new Line(inRangeValues); inRangeValuesLine.setColor(Utils.COLOR_BLUE); inRangeValuesLine.setHasLines(false); inRangeValuesLine.setPointRadius(pointSize); inRangeValuesLine.setHasPoints(true); return inRangeValuesLine; } public Line rawInterpretedLine() { Line line = new Line(rawInterpretedValues); line.setHasLines(false); line.setPointRadius(1); line.setHasPoints(true); return line; } private void addBgReadingValues() { for (BgReading bgReading : bgReadings) { if (bgReading.raw_calculated != 0 && prefs.getBoolean("interpret_raw", false)) { rawInterpretedValues.add(new PointValue((float) (bgReading.timestamp/fuzzer), (float) unitized(bgReading.raw_calculated))); } else if (bgReading.calculated_value >= 400) { highValues.add(new PointValue((float) (bgReading.timestamp/fuzzer), (float) unitized(400))); } else if (unitized(bgReading.calculated_value) >= highMark) { highValues.add(new PointValue((float) (bgReading.timestamp/fuzzer), (float) unitized(bgReading.calculated_value))); } else if (unitized(bgReading.calculated_value) >= lowMark) { inRangeValues.add(new PointValue((float) (bgReading.timestamp/fuzzer), (float) unitized(bgReading.calculated_value))); } else if (bgReading.calculated_value >= 40) { lowValues.add(new PointValue((float)(bgReading.timestamp/fuzzer), (float) unitized(bgReading.calculated_value))); } else if (bgReading.calculated_value > 13) { lowValues.add(new PointValue((float)(bgReading.timestamp/fuzzer), (float) unitized(40))); } } } public Line highLine() { List highLineValues = new ArrayList(); highLineValues.add(new PointValue((float)start_time, (float)highMark)); highLineValues.add(new PointValue((float)end_time, (float)highMark)); Line highLine = new Line(highLineValues); highLine.setHasPoints(false); highLine.setStrokeWidth(1); highLine.setColor(Utils.COLOR_ORANGE); return highLine; } public Line lowLine() { List lowLineValues = new ArrayList(); lowLineValues.add(new PointValue((float)start_time, (float)lowMark)); lowLineValues.add(new PointValue((float)end_time, (float)lowMark)); Line lowLine = new Line(lowLineValues); lowLine.setHasPoints(false); lowLine.setAreaTransparency(50); lowLine.setColor(Color.parseColor("#C30909")); lowLine.setStrokeWidth(1); lowLine.setFilled(true); return lowLine; } public Line maxShowLine() { List maxShowValues = new ArrayList(); maxShowValues.add(new PointValue((float)start_time, (float)defaultMaxY)); maxShowValues.add(new PointValue((float)end_time, (float)defaultMaxY)); Line maxShowLine = new Line(maxShowValues); maxShowLine.setHasLines(false); maxShowLine.setHasPoints(false); return maxShowLine; } public Line minShowLine() { List minShowValues = new ArrayList(); minShowValues.add(new PointValue((float)start_time, (float)defaultMinY)); minShowValues.add(new PointValue((float)end_time, (float)defaultMinY)); Line minShowLine = new Line(minShowValues); minShowLine.setHasPoints(false); minShowLine.setHasLines(false); return minShowLine; } /////////AXIS RELATED////////////// public Axis yAxis() { Axis yAxis = new Axis(); yAxis.setAutoGenerated(false); List axisValues = new ArrayList(); for(int j = 1; j <= 12; j += 1) { if (doMgdl) { axisValues.add(new AxisValue(j * 50)); } else { axisValues.add(new AxisValue(j*2)); } } yAxis.setValues(axisValues); yAxis.setHasLines(true); yAxis.setMaxLabelChars(5); yAxis.setInside(true); yAxis.setTextSize(axisTextSize); return yAxis; } public Axis xAxis() { Axis xAxis = new Axis(); xAxis.setAutoGenerated(false); List xAxisValues = new ArrayList(); GregorianCalendar now = new GregorianCalendar(); GregorianCalendar today = new GregorianCalendar(now.get(Calendar.YEAR), now.get(Calendar.MONTH), now.get(Calendar.DAY_OF_MONTH)); final java.text.DateFormat timeFormat = hourFormat(); timeFormat.setTimeZone(TimeZone.getDefault()); double start_hour_block = today.getTime().getTime(); double timeNow = new Date().getTime(); for(int l=0; l<=24; l++) { if ((start_hour_block + (60000 * 60 * (l))) < timeNow) { if((start_hour_block + (60000 * 60 * (l + 1))) >= timeNow) { endHour = start_hour_block + (60000 * 60 * (l)); l=25; } } } for(int l=0; l<=24; l++) { double timestamp = (endHour - (60000 * 60 * l)); xAxisValues.add(new AxisValue((long)(timestamp/fuzzer), (timeFormat.format(timestamp)).toCharArray())); } xAxis.setValues(xAxisValues); xAxis.setHasLines(true); xAxis.setTextSize(axisTextSize); return xAxis; } private SimpleDateFormat hourFormat() { return new SimpleDateFormat(DateFormat.is24HourFormat(context) ? "HH" : "h a"); } private boolean isXLargeTablet() { return (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE; } public Axis previewXAxis(){ List previewXaxisValues = new ArrayList(); final java.text.DateFormat timeFormat = hourFormat(); timeFormat.setTimeZone(TimeZone.getDefault()); for(int l=0; l<=24; l+=hoursPreviewStep) { double timestamp = (endHour - (60000 * 60 * l)); previewXaxisValues.add(new AxisValue((long)(timestamp/fuzzer), (timeFormat.format(timestamp)).toCharArray())); } Axis previewXaxis = new Axis(); previewXaxis.setValues(previewXaxisValues); previewXaxis.setHasLines(true); previewXaxis.setTextSize(previewAxisTextSize); return previewXaxis; } /////////VIEWPORT RELATED////////////// public Viewport advanceViewport(Chart chart, Chart previewChart) { viewport = new Viewport(previewChart.getMaximumViewport()); viewport.inset((float)((86400000 / 2.5)/fuzzer), 0); double distance_to_move = ((new Date().getTime())/fuzzer) - viewport.left - (((viewport.right - viewport.left) /2)); viewport.offset((float) distance_to_move, 0); return viewport; } public double unitized(double value) { if(doMgdl) { return value; } else { return mmolConvert(value); } } public String unitized_string(double value) { DecimalFormat df = new DecimalFormat("#"); if (value >= 400) { return "HIGH"; } else if (value >= 40) { if(doMgdl) { df.setMaximumFractionDigits(0); return df.format(value); } else { df.setMaximumFractionDigits(1); return df.format(mmolConvert(value)); } } else if (value > 12) { return "LOW"; } else { switch((int)value) { case 0: return "??0"; case 1: return "?SN"; case 2: return "??2"; case 3: return "?NA"; case 5: return "?NC"; case 6: return "?CD"; case 9: return "?AD"; case 12: return "?RF"; default: return "???"; } } } public String unitizedDeltaString(double value) { DecimalFormat df = new DecimalFormat("#"); df.setMaximumFractionDigits(1); String delta_sign = ""; if (value > 0.1) { delta_sign = "+"; } if(doMgdl) { return delta_sign + df.format(unitized(value)) + " mg/dl"; } else { return delta_sign + df.format(unitized(value)) + " mmol"; } } public double mmolConvert(double mgdl) { return mgdl * Constants.MGDL_TO_MMOLL; } public String unit() { if(doMgdl){ return "mg/dl"; } else { return "mmol"; } } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/UtilityModels/BgSendQueue.java ================================================ package com.eveningoutpost.dexdrip.UtilityModels; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.os.BatteryManager; import android.os.Bundle; import android.os.PowerManager; import android.preference.PreferenceManager; import android.provider.BaseColumns; import android.util.Log; import com.activeandroid.Model; import com.activeandroid.annotation.Column; import com.activeandroid.annotation.Table; import com.activeandroid.query.Select; import com.eveningoutpost.dexdrip.Models.BgReading; import com.eveningoutpost.dexdrip.ShareModels.ShareRest; import com.eveningoutpost.dexdrip.widgetUpdateService; import java.util.List; /** * Created by stephenblack on 11/7/14. */ @Table(name = "BgSendQueue", id = BaseColumns._ID) public class BgSendQueue extends Model { @Column(name = "bgReading", index = true) public BgReading bgReading; @Column(name = "success", index = true) public boolean success; @Column(name = "mongo_success", index = true) public boolean mongo_success; @Column(name = "operation_type") public String operation_type; public static BgSendQueue nextBgJob() { return new Select() .from(BgSendQueue.class) .where("success = ?", false) .orderBy("_ID desc") .limit(1) .executeSingle(); } public static List queue() { return new Select() .from(BgSendQueue.class) .where("success = ?", false) .orderBy("_ID asc") .limit(20) .execute(); } public static List mongoQueue() { return new Select() .from(BgSendQueue.class) .where("mongo_success = ?", false) .where("operation_type = ?", "create") .orderBy("_ID asc") .limit(30) .execute(); } public static void addToQueue(BgReading bgReading, String operation_type, Context context) { PowerManager powerManager = (PowerManager) context.getSystemService(context.POWER_SERVICE); PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "sendQueue"); wakeLock.acquire(); BgSendQueue bgSendQueue = new BgSendQueue(); bgSendQueue.operation_type = operation_type; bgSendQueue.bgReading = bgReading; bgSendQueue.success = false; bgSendQueue.mongo_success = false; bgSendQueue.save(); Log.d("BGQueue", "New value added to queue!"); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); Intent updateIntent = new Intent(Intents.ACTION_NEW_BG_ESTIMATE_NO_DATA); context.sendBroadcast(updateIntent); context.startService(new Intent(context, widgetUpdateService.class)); if (prefs.getBoolean("cloud_storage_mongodb_enable", false) || prefs.getBoolean("cloud_storage_api_enable", false)) { Log.w("SENSOR QUEUE:", String.valueOf(bgSendQueue.mongo_success)); if (operation_type.compareTo("create") == 0) { MongoSendTask task = new MongoSendTask(context, bgSendQueue); task.execute(); } } if(prefs.getBoolean("broadcast_data_through_intents", false)) { Log.i("SENSOR QUEUE:", "Broadcast data"); final Bundle bundle = new Bundle(); bundle.putDouble(Intents.EXTRA_BG_ESTIMATE, bgReading.calculated_value); bundle.putDouble(Intents.EXTRA_BG_SLOPE, bgReading.calculated_value_slope); if(bgReading.hide_slope) { bundle.putString(Intents.EXTRA_BG_SLOPE_NAME, "9"); } else { bundle.putString(Intents.EXTRA_BG_SLOPE_NAME, bgReading.slopeName()); } bundle.putInt(Intents.EXTRA_SENSOR_BATTERY, getBatteryLevel(context)); bundle.putLong(Intents.EXTRA_TIMESTAMP, bgReading.timestamp); Intent intent = new Intent(Intents.ACTION_NEW_BG_ESTIMATE); intent.putExtras(bundle); context.sendBroadcast(intent, Intents.RECEIVER_PERMISSION); } if(prefs.getBoolean("broadcast_to_pebble", false)) { PebbleSync pebbleSync = new PebbleSync(); pebbleSync.sendData(context, bgReading); } if(prefs.getBoolean("share_upload", false)) { ShareRest shareRest = new ShareRest(context); Log.w("ShareRest", "About to call ShareRest!!"); shareRest.sendBgData(bgReading); } wakeLock.release(); } public void markMongoSuccess() { mongo_success = true; save(); } public static int getBatteryLevel(Context context) { Intent batteryIntent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); if(level == -1 || scale == -1) { return 50; } return (int)(((float)level / (float)scale) * 100.0f); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/UtilityModels/CalibrationSendQueue.java ================================================ package com.eveningoutpost.dexdrip.UtilityModels; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.provider.BaseColumns; import com.activeandroid.Model; import com.activeandroid.annotation.Column; import com.activeandroid.annotation.Table; import com.activeandroid.query.Select; import com.eveningoutpost.dexdrip.Models.Calibration; import java.util.List; /** * Created by stephenblack on 11/7/14. */ @Table(name = "CalibrationSendQueue", id = BaseColumns._ID) public class CalibrationSendQueue extends Model { @Column(name = "calibration", index = true) public Calibration calibration; @Column(name = "success", index = true) public boolean success; @Column(name = "mongo_success", index = true) public boolean mongo_success; public static CalibrationSendQueue nextCalibrationJob() { CalibrationSendQueue job = new Select() .from(CalibrationSendQueue.class) .where("success = ?", false) .orderBy("_ID desc") .limit(1) .executeSingle(); return job; } public static List queue() { return new Select() .from(CalibrationSendQueue.class) .where("success = ?", false) .orderBy("_ID asc") .execute(); } public static List mongoQueue() { return new Select() .from(CalibrationSendQueue.class) .where("mongo_success = ?", false) .orderBy("_ID asc") .limit(30) .execute(); } public static void addToQueue(Calibration calibration, Context context) { CalibrationSendQueue calibrationSendQueue = new CalibrationSendQueue(); calibrationSendQueue.calibration = calibration; calibrationSendQueue.success = false; calibrationSendQueue.mongo_success = false; calibrationSendQueue.save(); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); if (prefs.getBoolean("cloud_storage_mongodb_enable", false) || prefs.getBoolean("cloud_storage_api_enable", false)) { MongoSendTask task = new MongoSendTask(context, calibrationSendQueue); task.execute(); } } public void markMongoSuccess() { mongo_success = true; save(); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/UtilityModels/CollectionServiceStarter.java ================================================ package com.eveningoutpost.dexdrip.UtilityModels; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.util.Log; import com.eveningoutpost.dexdrip.Services.DexCollectionService; import com.eveningoutpost.dexdrip.Services.DexShareCollectionService; import com.eveningoutpost.dexdrip.Services.WixelReader; /** * Created by stephenblack on 12/22/14. */ public class CollectionServiceStarter { private Context mContext; public static boolean isBTWixel(Context context) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); String collection_method = prefs.getString("dex_collection_method", "BluetoothWixel"); if(collection_method.compareTo("BluetoothWixel") == 0) { return true; } return false; } public static boolean isBTShare(Context context) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); String collection_method = prefs.getString("dex_collection_method", "BluetoothWixel"); if(collection_method.compareTo("DexcomShare") == 0) { return true; } return false; } public static boolean isWifiWixel(Context context) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); String collection_method = prefs.getString("dex_collection_method", "BluetoothWixel"); if(collection_method.compareTo("WifiWixel") == 0) { return true; } return false; } public static void newStart(Context context) { CollectionServiceStarter collectionServiceStarter = new CollectionServiceStarter(context); collectionServiceStarter.start(context); } public void start(Context context) { mContext = context; SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); String collection_method = prefs.getString("dex_collection_method", "BluetoothWixel"); if(isBTWixel(context)) { Log.d("DexDrip", "Starting bt wixel collector"); stopWifWixelThread(); stopBtShareService(); startBtWixelService(); } else if(isWifiWixel(context)){ Log.d("DexDrip", "Starting wifi wixel collector"); stopBtWixelService(); stopBtShareService(); startWifWixelThread(); } else if(isBTShare(context)) { Log.d("DexDrip", "Starting bt share collector"); stopBtWixelService(); stopWifWixelThread(); startBtShareService(); } Log.d("ColServiceStarter", collection_method); } public CollectionServiceStarter(Context context) { mContext = context; } public static void restartCollectionService(Context context) { CollectionServiceStarter collectionServiceStarter = new CollectionServiceStarter(context); collectionServiceStarter.stopBtShareService(); collectionServiceStarter.stopBtWixelService(); collectionServiceStarter.stopWifWixelThread(); collectionServiceStarter.start(context); } private void startBtWixelService() { Log.d("ColServiceStarter", "starting bt wixel service"); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) { mContext.startService(new Intent(mContext, DexCollectionService.class)); } } private void stopBtWixelService() { Log.d("ColServiceStarter", "stopping bt wixel service"); mContext.stopService(new Intent(mContext, DexCollectionService.class)); } private void startBtShareService() { Log.d("ColServiceStarter", "starting bt share service"); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) { mContext.startService(new Intent(mContext, DexShareCollectionService.class)); } } private void stopBtShareService() { Log.d("ColServiceStarter", "stopping bt share service"); mContext.stopService(new Intent(mContext, DexShareCollectionService.class)); } private void startWifWixelThread() { WixelReader.sStart(mContext); } private void stopWifWixelThread() { WixelReader.sStop(); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/UtilityModels/Constants.java ================================================ package com.eveningoutpost.dexdrip.UtilityModels; /** * Various constants */ public class Constants { public static final double MMOLL_TO_MGDL = 18.0182; public static final double MGDL_TO_MMOLL = 1 / MMOLL_TO_MGDL; } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/UtilityModels/DexShareAttributes.java ================================================ package com.eveningoutpost.dexdrip.UtilityModels; import java.math.BigInteger; import java.util.UUID; /** * Created by stephenblack on 2/4/15. */ public class DexShareAttributes { //Share Service String public static final UUID CradleService= UUID.fromString("F0ABA0B1-EBFA-F96F-28DA-076C35A521DB"); //Share Characteristic Strings public static final UUID AuthenticationCode = UUID.fromString("F0ABACAC-EBFA-F96F-28DA-076C35A521DB"); public static final UUID ShareMessageReceiver= UUID.fromString("F0ABB20A-EBFA-F96F-28DA-076C35A521DB"); // Max 20 Bytes - Writable public static final UUID ShareMessageResponse= UUID.fromString("F0ABB20B-EBFA-F96F-28DA-076C35A521DB"); // Max 20 Bytes public static final UUID Command= UUID.fromString("F0ABB0CC-EBFA-F96F-28DA-076C35A521DB"); public static final UUID Response= UUID.fromString("F0ABB0CD-EBFA-F96F-28DA-076C35A521DB"); // Writable? public static final UUID HeartBeat= UUID.fromString("F0AB2B18-EBFA-F96F-28DA-076C35A521DB"); //Possible new uuids???? 60bfxxxx-60b0-4d4f-0000-000160c48d70 public static final UUID CradleService2= UUID.fromString("F0ACA0B1-EBFA-F96F-28DA-076C35A521DB"); public static final UUID AuthenticationCode2 = UUID.fromString("F0ACACAC-EBFA-F96F-28DA-076C35A521DB"); // read, write public static final UUID ShareMessageReceiver2= UUID.fromString("F0ACB20A-EBFA-F96F-28DA-076C35A521DB"); // read, write public static final UUID ShareMessageResponse2= UUID.fromString("F0ACB20B-EBFA-F96F-28DA-076C35A521DB"); // indicate, read public static final UUID Command2= UUID.fromString("F0ACB0CC-EBFA-F96F-28DA-076C35A521DB"); // read, write public static final UUID Response2= UUID.fromString("F0ACB0CD-EBFA-F96F-28DA-076C35A521DB"); // indicate, read, write public static final UUID HeartBeat2= UUID.fromString("F0AC2B18-EBFA-F96F-28DA-076C35A521DB"); // notify, read //Device Info public static final UUID DeviceService= UUID.fromString("00001804-0000-1000-8000-00805f9b34fb"); public static final UUID PowerLevel= UUID.fromString("00002a07-0000-1000-8000-00805f9b34fb"); } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/UtilityModels/ForegroundServiceStarter.java ================================================ package com.eveningoutpost.dexdrip.UtilityModels; import android.app.Notification; import android.app.PendingIntent; import android.app.Service; import android.app.TaskStackBuilder; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import android.util.Log; import com.eveningoutpost.dexdrip.Home; import com.eveningoutpost.dexdrip.R; import com.eveningoutpost.dexdrip.Services.DexShareCollectionService; /** * Created by stephenblack on 12/25/14. */ public class ForegroundServiceStarter { private Service mService; private Context mContext; private boolean run_service_in_foreground = false; private int FOREGROUND_ID = 8811; public ForegroundServiceStarter(Context context, Service service) { mContext = context; mService = service; SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); run_service_in_foreground = prefs.getBoolean("run_service_in_foreground", false); } private Notification notification() { Intent intent = new Intent(mContext, Home.class); TaskStackBuilder stackBuilder = TaskStackBuilder.create(mContext); stackBuilder.addParentStack(Home.class); stackBuilder.addNextIntent(intent); PendingIntent resultPendingIntent = stackBuilder.getPendingIntent( 0, PendingIntent.FLAG_UPDATE_CURRENT ); NotificationCompat.Builder b=new NotificationCompat.Builder(mService); b.setOngoing(true); b.setCategory(Notification.CATEGORY_SERVICE); // Hide this notification "below the fold" on L+ b.setPriority(Notification.PRIORITY_MIN); // Don't show this notification on the lock screen on L+ b.setVisibility(Notification.VISIBILITY_SECRET); b.setContentTitle("xDrip is Running") .setContentText("xDrip Data collection service is running.") .setSmallIcon(R.drawable.ic_action_communication_invert_colors_on); b.setContentIntent(resultPendingIntent); return(b.build()); } public void start() { if (run_service_in_foreground) { Log.e("FOREGROUND", "should be moving to foreground"); mService.startForeground(FOREGROUND_ID, notification()); } } public void stop() { if (run_service_in_foreground) { Log.e("FOREGROUND", "should be moving out of foreground"); mService.stopForeground(true); } } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/UtilityModels/HM10Attributes.java ================================================ package com.eveningoutpost.dexdrip.UtilityModels; /** * Created by stephenblack on 10/26/14. */ public class HM10Attributes { public static String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb"; public static String HM_10_SERVICE = "0000ffe0-0000-1000-8000-00805f9b34fb"; public static String HM_RX_TX = "0000ffe1-0000-1000-8000-00805f9b34fb"; } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/UtilityModels/Intents.java ================================================ package com.eveningoutpost.dexdrip.UtilityModels; /** * For integration. */ public interface Intents { String RECEIVER_PERMISSION = "com.eveningoutpost.dexdrip.permissions.RECEIVE_BG_ESTIMATE"; String ACTION_NEW_BG_ESTIMATE = "com.eveningoutpost.dexdrip.BgEstimate"; String EXTRA_BG_ESTIMATE = "com.eveningoutpost.dexdrip.Extras.BgEstimate"; String EXTRA_BG_SLOPE = "com.eveningoutpost.dexdrip.Extras.BgSlope"; String EXTRA_BG_SLOPE_NAME = "com.eveningoutpost.dexdrip.Extras.BgSlopeName"; String EXTRA_SENSOR_BATTERY = "com.eveningoutpost.dexdrip.Extras.SensorBattery"; String EXTRA_TIMESTAMP = "com.eveningoutpost.dexdrip.Extras.Time"; String ACTION_NEW_BG_ESTIMATE_NO_DATA = "com.eveningoutpost.dexdrip.BgEstimateNoData"; } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/UtilityModels/MongoSendTask.java ================================================ package com.eveningoutpost.dexdrip.UtilityModels; import android.content.Context; import android.os.AsyncTask; import android.util.Log; import com.eveningoutpost.dexdrip.Models.BgReading; import com.eveningoutpost.dexdrip.Models.Calibration; import com.eveningoutpost.dexdrip.Services.SyncService; import java.util.ArrayList; import java.util.List; /** * Created by stephenblack on 12/19/14. */ public class MongoSendTask extends AsyncTask { private Context context; public List bgsQueue = new ArrayList(); public List calibrationsQueue = new ArrayList(); private Exception exception; public MongoSendTask(Context pContext, BgSendQueue bgSendQueue) { bgsQueue.add(bgSendQueue); context = pContext; } public MongoSendTask(Context pContext, CalibrationSendQueue calibrationSendQueue) { calibrationsQueue.add(calibrationSendQueue); context = pContext; } public MongoSendTask(Context pContext) { calibrationsQueue = CalibrationSendQueue.mongoQueue(); bgsQueue = BgSendQueue.mongoQueue(); context = pContext; } public SyncService doInBackground(String... urls) { try { List bgReadings = new ArrayList(); List calibrations = new ArrayList(); for (CalibrationSendQueue job : calibrationsQueue) { calibrations.add(job.calibration); } for (BgSendQueue job : bgsQueue) { bgReadings.add(job.bgReading); } if(bgReadings.size() + calibrations.size() > 0) { NightscoutUploader uploader = new NightscoutUploader(context); boolean uploadStatus = uploader.upload(bgReadings, calibrations, calibrations); if (uploadStatus) { for (CalibrationSendQueue calibration : calibrationsQueue) { calibration.markMongoSuccess(); } for (BgSendQueue bgReading : bgsQueue) { bgReading.markMongoSuccess(); } } } } catch (Exception e) { this.exception = e; return null; } return new SyncService(); } // protected void onPostExecute(RSSFeed feed) { // // TODO: check this.exception // // TODO: do something with the feed // } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/UtilityModels/NightscoutUploader.java ================================================ package com.eveningoutpost.dexdrip.UtilityModels; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.os.BatteryManager; import android.preference.PreferenceManager; import android.util.Log; import com.eveningoutpost.dexdrip.Models.BgReading; import com.eveningoutpost.dexdrip.Models.Calibration; import com.mongodb.BasicDBObject; import com.mongodb.DB; import com.mongodb.DBCollection; import com.mongodb.MongoClient; import com.mongodb.MongoClientURI; import com.mongodb.WriteConcern; import org.apache.http.Header; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.BasicResponseHandler; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicHeader; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; import org.json.JSONObject; import java.security.MessageDigest; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.TimeZone; /** * THIS CLASS WAS BUILT BY THE NIGHTSCOUT GROUP FOR THEIR NIGHTSCOUT ANDROID UPLOADER * https://github.com/nightscout/android-uploader/ * I have modified this class to make it fit my needs * Modifications include field remappings and lists instead of arrays * A DTO would probably be a better future implementation * -Stephen Black */ public class NightscoutUploader { private static final String TAG = NightscoutUploader.class.getSimpleName(); private static final int SOCKET_TIMEOUT = 60000; private static final int CONNECTION_TIMEOUT = 30000; private Context mContext; private Boolean enableRESTUpload; private Boolean enableMongoUpload; private SharedPreferences prefs; public NightscoutUploader(Context context) { mContext = context; prefs = PreferenceManager.getDefaultSharedPreferences(mContext); enableRESTUpload = prefs.getBoolean("cloud_storage_api_enable", false); enableMongoUpload = prefs.getBoolean("cloud_storage_mongodb_enable", false); } public boolean upload(BgReading glucoseDataSet, Calibration meterRecord, Calibration calRecord) { List glucoseDataSets = new ArrayList(); glucoseDataSets.add(glucoseDataSet); List meterRecords = new ArrayList(); meterRecords.add(meterRecord); List calRecords = new ArrayList(); calRecords.add(calRecord); return upload(glucoseDataSets, meterRecords, calRecords); } public boolean upload(List glucoseDataSets, List meterRecords, List calRecords) { boolean mongoStatus = false; boolean apiStatus = false; if (enableRESTUpload) { long start = System.currentTimeMillis(); Log.i(TAG, String.format("Starting upload of %s record using a REST API", glucoseDataSets.size())); apiStatus = doRESTUpload(prefs, glucoseDataSets, meterRecords, calRecords); Log.i(TAG, String.format("Finished upload of %s record using a REST API in %s ms", glucoseDataSets.size(), System.currentTimeMillis() - start)); } if (enableMongoUpload) { double start = new Date().getTime(); mongoStatus = doMongoUpload(prefs, glucoseDataSets, meterRecords, calRecords); Log.i(TAG, String.format("Finished upload of %s record using a Mongo in %s ms", glucoseDataSets.size() + meterRecords.size(), System.currentTimeMillis() - start)); } return apiStatus || mongoStatus; } private boolean doRESTUpload(SharedPreferences prefs, List glucoseDataSets, List meterRecords, List calRecords) { String baseURLSettings = prefs.getString("cloud_storage_api_base", ""); ArrayList baseURIs = new ArrayList(); try { for (String baseURLSetting : baseURLSettings.split(" ")) { String baseURL = baseURLSetting.trim(); if (baseURL.isEmpty()) continue; baseURIs.add(baseURL + (baseURL.endsWith("/") ? "" : "/")); } } catch (Exception e) { Log.e(TAG, "Unable to process API Base URL"); return false; } for (String baseURI : baseURIs) { try { doRESTUploadTo(baseURI, glucoseDataSets, meterRecords, calRecords); } catch (Exception e) { Log.e(TAG, "Unable to do REST API Upload"); return false; } } return true; } private void doRESTUploadTo(String baseURI, List glucoseDataSets, List meterRecords, List calRecords) { try { int apiVersion = 0; if (baseURI.endsWith("/v1/")) apiVersion = 1; String baseURL = null; String secret = null; String[] uriParts = baseURI.split("@"); if (uriParts.length == 1 && apiVersion == 0) { baseURL = uriParts[0]; } else if (uriParts.length == 1 && apiVersion > 0) { throw new Exception("Starting with API v1, a pass phase is required"); } else if (uriParts.length == 2 && apiVersion > 0) { secret = uriParts[0]; baseURL = uriParts[1]; } else { throw new Exception("Unexpected baseURI"); } String postURL = baseURL + "entries"; Log.i(TAG, "postURL: " + postURL); HttpParams params = new BasicHttpParams(); HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT); HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT); DefaultHttpClient httpclient = new DefaultHttpClient(params); HttpPost post = new HttpPost(postURL); Header apiSecretHeader = null; if (apiVersion > 0) { if (secret == null || secret.isEmpty()) { throw new Exception("Starting with API v1, a pass phase is required"); } else { MessageDigest digest = MessageDigest.getInstance("SHA-1"); byte[] bytes = secret.getBytes("UTF-8"); digest.update(bytes, 0, bytes.length); bytes = digest.digest(); StringBuilder sb = new StringBuilder(bytes.length * 2); for (byte b: bytes) { sb.append(String.format("%02x", b & 0xff)); } String token = sb.toString(); apiSecretHeader = new BasicHeader("api-secret", token); } } if (apiSecretHeader != null) { post.setHeader(apiSecretHeader); } for (BgReading record : glucoseDataSets) { JSONObject json = new JSONObject(); try { if (apiVersion >= 1) populateV1APIBGEntry(json, record); else populateLegacyAPIEntry(json, record); } catch (Exception e) { Log.w(TAG, "Unable to populate entry"); continue; } String jsonString = json.toString(); Log.i(TAG, "SGV JSON: " + jsonString); try { StringEntity se = new StringEntity(jsonString); post.setEntity(se); post.setHeader("Accept", "application/json"); post.setHeader("Content-type", "application/json"); ResponseHandler responseHandler = new BasicResponseHandler(); httpclient.execute(post, responseHandler); } catch (Exception e) { Log.w(TAG, "Unable to populate entry"); } } if (apiVersion >= 1) { for (Calibration record : meterRecords) { JSONObject json = new JSONObject(); try { populateV1APIMeterReadingEntry(json, record); } catch (Exception e) { Log.w(TAG, "Unable to populate entry"); continue; } String jsonString = json.toString(); Log.i(TAG, "MBG JSON: " + jsonString); try { StringEntity se = new StringEntity(jsonString); post.setEntity(se); post.setHeader("Accept", "application/json"); post.setHeader("Content-type", "application/json"); ResponseHandler responseHandler = new BasicResponseHandler(); httpclient.execute(post, responseHandler); } catch (Exception e) { Log.w(TAG, "Unable to post data"); } } } if (apiVersion >= 1) { for (Calibration calRecord : calRecords) { JSONObject json = new JSONObject(); try { populateV1APICalibrationEntry(json, calRecord); } catch (Exception e) { Log.w(TAG, "Unable to populate entry"); continue; } String jsonString = json.toString(); Log.i(TAG, "CAL JSON: " + jsonString); try { StringEntity se = new StringEntity(jsonString); post.setEntity(se); post.setHeader("Accept", "application/json"); post.setHeader("Content-type", "application/json"); ResponseHandler responseHandler = new BasicResponseHandler(); httpclient.execute(post, responseHandler); } catch (Exception e) { Log.w(TAG, "Unable to post data"); } } } // TODO: this is a quick port from the original code and needs to be checked before release postDeviceStatus(baseURL, apiSecretHeader, httpclient); } catch (Exception e) { Log.w(TAG, "Unable to post data"); } } private void populateV1APIBGEntry(JSONObject json, BgReading record) throws Exception { SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss a"); format.setTimeZone(TimeZone.getDefault()); json.put("device", "xDrip-"+prefs.getString("dex_collection_method", "BluetoothWixel")); json.put("date", record.timestamp); json.put("dateString", format.format(record.timestamp)); json.put("sgv", (int)record.calculated_value); json.put("direction", record.slopeName()); json.put("type", "sgv"); json.put("filtered", record.filtered_data * 1000); json.put("unfiltered", record.age_adjusted_raw_value * 1000); json.put("rssi", 100); json.put("noise", Integer.valueOf(record.noiseValue())); } private void populateLegacyAPIEntry(JSONObject json, BgReading record) throws Exception { SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss a"); format.setTimeZone(TimeZone.getDefault()); json.put("device", "xDrip-"+prefs.getString("dex_collection_method", "BluetoothWixel")); json.put("date", record.timestamp); json.put("dateString", format.format(record.timestamp)); json.put("sgv", (int)record.calculated_value); json.put("direction", record.slopeName()); } private void populateV1APIMeterReadingEntry(JSONObject json, Calibration record) throws Exception { SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss a"); format.setTimeZone(TimeZone.getDefault()); json.put("device", "xDrip-"+prefs.getString("dex_collection_method", "BluetoothWixel")); json.put("type", "mbg"); json.put("date", record.timestamp); json.put("dateString", format.format(record.timestamp)); json.put("mbg", record.bg); } private void populateV1APICalibrationEntry(JSONObject json, Calibration record) throws Exception { SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss a"); format.setTimeZone(TimeZone.getDefault()); json.put("device", "xDrip-" + prefs.getString("dex_collection_method", "BluetoothWixel")); json.put("type", "cal"); json.put("date", record.timestamp); json.put("dateString", format.format(record.timestamp)); if(record.check_in) { json.put("slope", (long) (record.first_slope)); json.put("intercept", (long) ((record.first_intercept))); json.put("scale", record.first_scale); } else { json.put("slope", (long) (record.slope * 1000)); json.put("intercept", (long) ((record.intercept * -1000) / (record.slope * 1000))); json.put("scale", 1); } } // TODO: this is a quick port from original code and needs to be refactored before release private void postDeviceStatus(String baseURL, Header apiSecretHeader, DefaultHttpClient httpclient) throws Exception { String devicestatusURL = baseURL + "devicestatus"; Log.i(TAG, "devicestatusURL: " + devicestatusURL); JSONObject json = new JSONObject(); json.put("uploaderBattery", getBatteryLevel()); String jsonString = json.toString(); HttpPost post = new HttpPost(devicestatusURL); if (apiSecretHeader != null) { post.setHeader(apiSecretHeader); } StringEntity se = new StringEntity(jsonString); post.setEntity(se); post.setHeader("Accept", "application/json"); post.setHeader("Content-type", "application/json"); ResponseHandler responseHandler = new BasicResponseHandler(); httpclient.execute(post, responseHandler); } private boolean doMongoUpload(SharedPreferences prefs, List glucoseDataSets, List meterRecords, List calRecords) { SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss a"); format.setTimeZone(TimeZone.getDefault()); String dbURI = prefs.getString("cloud_storage_mongodb_uri", null); String collectionName = prefs.getString("cloud_storage_mongodb_collection", null); String dsCollectionName = prefs.getString("cloud_storage_mongodb_device_status_collection", "devicestatus"); if (dbURI != null && collectionName != null) { try { // connect to db MongoClientURI uri = new MongoClientURI(dbURI.trim()); MongoClient client = new MongoClient(uri); // get db DB db = client.getDB(uri.getDatabase()); // get collection DBCollection dexcomData = db.getCollection(collectionName.trim()); Log.i(TAG, "The number of EGV records being sent to MongoDB is " + glucoseDataSets.size()); for (BgReading record : glucoseDataSets) { // make db object BasicDBObject testData = new BasicDBObject(); testData.put("device", "xDrip-"+prefs.getString("dex_collection_method", "BluetoothWixel")); testData.put("date", record.timestamp); testData.put("dateString", format.format(record.timestamp)); testData.put("sgv", Math.round(record.calculated_value)); testData.put("direction", record.slopeName()); testData.put("type", "sgv"); testData.put("filtered", record.filtered_data * 1000); testData.put("unfiltered", record.age_adjusted_raw_value * 1000 ); testData.put("rssi", 100); testData.put("noise", Integer.valueOf(record.noiseValue())); dexcomData.update(testData, testData, true, false, WriteConcern.UNACKNOWLEDGED); } Log.i(TAG, "The number of MBG records being sent to MongoDB is " + meterRecords.size()); for (Calibration meterRecord : meterRecords) { // make db object BasicDBObject testData = new BasicDBObject(); testData.put("device", "xDrip-"+prefs.getString("dex_collection_method", "BluetoothWixel")); testData.put("type", "mbg"); testData.put("date", meterRecord.timestamp); testData.put("dateString", format.format(meterRecord.timestamp)); testData.put("mbg", meterRecord.bg); dexcomData.update(testData, testData, true, false, WriteConcern.UNACKNOWLEDGED); } for (Calibration calRecord : calRecords) { // make db object BasicDBObject testData = new BasicDBObject(); testData.put("device", "xDrip-"+prefs.getString("dex_collection_method", "BluetoothWixel")); testData.put("date", calRecord.timestamp); testData.put("dateString", format.format(calRecord.timestamp)); if(calRecord.check_in) { testData.put("slope", (long) (calRecord.first_slope)); testData.put("intercept", (long) ((calRecord.first_intercept))); testData.put("scale", calRecord.first_scale); } else { testData.put("slope", (long) (calRecord.slope * 1000)); testData.put("intercept", (long) ((calRecord.intercept * -1000) / (calRecord.slope * 1000))); testData.put("scale", 1); } testData.put("type", "cal"); dexcomData.update(testData, testData, true, false, WriteConcern.UNACKNOWLEDGED); } // TODO: quick port from original code, revisit before release DBCollection dsCollection = db.getCollection(dsCollectionName); BasicDBObject devicestatus = new BasicDBObject(); devicestatus.put("uploaderBattery", getBatteryLevel()); devicestatus.put("created_at", new Date()); dsCollection.insert(devicestatus, WriteConcern.UNACKNOWLEDGED); client.close(); return true; } catch (Exception e) { Log.e(TAG, "Unable to upload data to mongo"); } } return false; } public int getBatteryLevel() { Intent batteryIntent = mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); if(level == -1 || scale == -1) { return 50; } return (int)(((float)level / (float)scale) * 100.0f); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/UtilityModels/Notifications.java ================================================ package com.eveningoutpost.dexdrip.UtilityModels; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.MediaPlayer; import android.net.Uri; import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import android.util.Log; import com.eveningoutpost.dexdrip.AddCalibration; import com.eveningoutpost.dexdrip.DoubleCalibrationActivity; import com.eveningoutpost.dexdrip.Home; import com.eveningoutpost.dexdrip.Models.BgReading; import com.eveningoutpost.dexdrip.Models.Calibration; import com.eveningoutpost.dexdrip.Models.CalibrationRequest; import com.eveningoutpost.dexdrip.Models.UserNotification; import com.eveningoutpost.dexdrip.R; import com.eveningoutpost.dexdrip.Sensor; import java.text.DecimalFormat; import java.util.Date; import java.util.List; /** * Created by stephenblack on 11/28/14. */ public class Notifications { public static final long[] vibratePattern = {0,1000,300,1000,300,1000}; public static boolean bg_notifications; public static boolean bg_vibrate; public static boolean bg_lights; public static boolean bg_sound; public static boolean bg_sound_in_silent; public static int bg_snooze; public static String bg_notification_sound; public static boolean calibration_notifications; public static boolean calibration_vibrate; public static boolean calibration_lights; public static boolean calibration_sound; public static int calibration_snooze; public static String calibration_notification_sound; public static Context mContext; public static int currentVolume; public static AudioManager manager; public static final int BgNotificationId = 001; public static final int calibrationNotificationId = 002; public static final int doubleCalibrationNotificationId = 003; public static final int extraCalibrationNotificationId = 004; public static final int exportCompleteNotificationId = 005; public static void setNotificationSettings(Context context) { mContext = context; SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); bg_notifications = prefs.getBoolean("bg_notifications", true); bg_vibrate = prefs.getBoolean("bg_vibrate", true); bg_lights = prefs.getBoolean("bg_lights", true); bg_sound = prefs.getBoolean("bg_play_sound", true); bg_snooze = Integer.parseInt(prefs.getString("bg_snooze", "20")); bg_notification_sound = prefs.getString("bg_notification_sound", "content://settings/system/notification_sound"); bg_sound_in_silent = prefs.getBoolean("bg_sound_in_silent", false); calibration_notifications = prefs.getBoolean("calibration_notifications", true); calibration_vibrate = prefs.getBoolean("calibration_vibrate", true); calibration_lights = prefs.getBoolean("calibration_lights", true); calibration_sound = prefs.getBoolean("calibration_play_sound", true); calibration_snooze = Integer.parseInt(prefs.getString("calibration_snooze", "20")); calibration_notification_sound = prefs.getString("calibration_notification_sound", "content://settings/system/notification_sound"); } public static void notificationSetter(Context context) { setNotificationSettings(context); BgGraphBuilder bgGraphBuilder = new BgGraphBuilder(context); double high = bgGraphBuilder.highMark; double low = bgGraphBuilder.lowMark; Sensor sensor = Sensor.currentSensor(); List bgReadings = BgReading.latest(3); List calibrations = Calibration.allForSensorInLastFourDays(); if(bgReadings.size() < 3) { return; } if(calibrations.size() < 2) { return; } BgReading bgReading = bgReadings.get(0); if (bg_notifications && sensor != null) { if (bgGraphBuilder.unitized(bgReading.calculated_value) >= high || bgGraphBuilder.unitized(bgReading.calculated_value) <= low) { if(bgReading.calculated_value > 14) { if (bgReading.hide_slope) { bgAlert(bgReading.displayValue(mContext), ""); } else { bgAlert(bgReading.displayValue(mContext), bgReading.slopeArrow()); } } } else { clearBgAlert(); } } else { clearAllBgNotifications(); } if (calibration_notifications) { if (bgReadings.size() >= 3) { if (calibrations.size() == 0 && (new Date().getTime() - bgReadings.get(2).timestamp <= (60000 * 30)) && sensor != null) { if ((sensor.started_at + (60000 * 60 * 2)) < new Date().getTime()) { doubleCalibrationRequest(); } else { clearDoubleCalibrationRequest(); } } else { clearDoubleCalibrationRequest(); } } else { clearDoubleCalibrationRequest(); } if (CalibrationRequest.shouldRequestCalibration(bgReading) && (new Date().getTime() - bgReadings.get(2).timestamp <= (60000 * 24))) { extraCalibrationRequest(); } else { clearExtraCalibrationRequest(); } if (calibrations.size() >= 1 && Math.abs((new Date().getTime() - calibrations.get(0).timestamp))/(1000*60*60) > 12) { Log.e("NOTIFICATIONS", "Calibration difference in hours: " + ((new Date().getTime() - calibrations.get(0).timestamp))/(1000*60*60)); calibrationRequest(); } else { clearCalibrationRequest(); } } else { clearAllCalibrationNotifications(); } } public static void soundAlert(String soundUri) { manager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); int maxVolume = manager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); currentVolume = manager.getStreamVolume(AudioManager.STREAM_MUSIC); manager.setStreamVolume(AudioManager.STREAM_MUSIC, maxVolume, 0); Uri notification = Uri.parse(bg_notification_sound); MediaPlayer player = MediaPlayer.create(mContext, notification); player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { manager.setStreamVolume(AudioManager.STREAM_MUSIC, currentVolume, 0); } }); player.start(); } public static void clearAllBgNotifications() { notificationDismiss(BgNotificationId); } public static void clearAllCalibrationNotifications() { notificationDismiss(calibrationNotificationId); notificationDismiss(extraCalibrationNotificationId); notificationDismiss(doubleCalibrationNotificationId); } public static void bgNotificationCreate(String title, String content, Intent intent, int notificationId) { NotificationCompat.Builder mBuilder = notificationBuilder(title, content, intent); if (bg_vibrate) { mBuilder.setVibrate(vibratePattern);} if (bg_lights) { mBuilder.setLights(0xff00ff00, 300, 1000);} if (bg_sound && !bg_sound_in_silent) { mBuilder.setSound(Uri.parse(bg_notification_sound), AudioAttributes.FLAG_AUDIBILITY_ENFORCED);} if (bg_sound && bg_sound_in_silent) { soundAlert(bg_notification_sound);} NotificationManager mNotifyMgr = (NotificationManager) mContext.getSystemService(mContext.NOTIFICATION_SERVICE); mNotifyMgr.cancel(notificationId); mNotifyMgr.notify(notificationId, mBuilder.build()); } public static void calibrationNotificationCreate(String title, String content, Intent intent, int notificationId) { NotificationCompat.Builder mBuilder = notificationBuilder(title, content, intent); if (calibration_vibrate) { mBuilder.setVibrate(vibratePattern);} if (calibration_lights) { mBuilder.setLights(0xff00ff00, 300, 1000);} if (calibration_sound) { mBuilder.setSound(Uri.parse(calibration_notification_sound), AudioAttributes.FLAG_AUDIBILITY_ENFORCED);} NotificationManager mNotifyMgr = (NotificationManager) mContext.getSystemService(mContext.NOTIFICATION_SERVICE); mNotifyMgr.cancel(notificationId); mNotifyMgr.notify(notificationId, mBuilder.build()); } public static void notificationUpdate(String title, String content, Intent intent, int notificationId) { NotificationCompat.Builder mBuilder = notificationBuilder(title, content, intent); NotificationManager mNotifyMgr = (NotificationManager) mContext.getSystemService(mContext.NOTIFICATION_SERVICE); mNotifyMgr.notify(notificationId, mBuilder.build()); } public static NotificationCompat.Builder notificationBuilder(String title, String content, Intent intent) { return new NotificationCompat.Builder(mContext) .setSmallIcon(R.drawable.ic_action_communication_invert_colors_on) .setContentTitle(title) .setContentText(content) .setContentIntent(notificationIntent(intent)); } public static PendingIntent notificationIntent(Intent intent){ return PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } public static void notificationDismiss(int notificationId) { NotificationManager mNotifyMgr = (NotificationManager) mContext.getSystemService(mContext.NOTIFICATION_SERVICE); mNotifyMgr.cancel(notificationId); } public static void bgAlert(String value, String slopeArrow) { UserNotification userNotification = UserNotification.lastBgAlert(); if ((userNotification == null) || (userNotification.timestamp <= ((new Date().getTime()) - (60000 * bg_snooze)))) { if (userNotification != null) { userNotification.delete(); } UserNotification newUserNotification = UserNotification.create(value + " " + slopeArrow, "bg_alert"); String title = value + " " + slopeArrow; String content = "BG LEVEL ALERT: " + value + " " + slopeArrow; Intent intent = new Intent(mContext, Home.class); bgNotificationCreate(title, content, intent, BgNotificationId); } else if ((userNotification != null) && (userNotification.timestamp >= ((new Date().getTime()) - (60000 * bg_snooze)))) { String title = value + " " + slopeArrow; String content = "BG LEVEL ALERT: " + value + " " + slopeArrow; Intent intent = new Intent(mContext, Home.class); notificationUpdate(title, content, intent, BgNotificationId); } } public static void calibrationRequest() { UserNotification userNotification = UserNotification.lastCalibrationAlert(); if ((userNotification == null) || (userNotification.timestamp <= ((new Date().getTime()) - (60000 * calibration_snooze)))) { if (userNotification != null) { userNotification.delete(); } UserNotification newUserNotification = UserNotification.create("12 hours since last Calibration", "calibration_alert"); String title = "Calibration Needed"; String content = "12 hours since last calibration"; Intent intent = new Intent(mContext, AddCalibration.class); calibrationNotificationCreate(title, content, intent, calibrationNotificationId); } } public static void doubleCalibrationRequest() { UserNotification userNotification = UserNotification.lastDoubleCalibrationAlert(); if ((userNotification == null) || (userNotification.timestamp <= ((new Date().getTime()) - (60000 * calibration_snooze)))) { if (userNotification != null) { userNotification.delete(); } UserNotification newUserNotification = UserNotification.create("Double Calibration", "double_calibration_alert"); String title = "Sensor is ready"; String content = "Sensor is ready, please enter a double calibration"; Intent intent = new Intent(mContext, DoubleCalibrationActivity.class); calibrationNotificationCreate(title, content, intent, calibrationNotificationId); } } public static void extraCalibrationRequest() { UserNotification userNotification = UserNotification.lastExtraCalibrationAlert(); if ((userNotification == null) || (userNotification.timestamp <= ((new Date().getTime()) - (60000 * calibration_snooze)))) { if (userNotification != null) { userNotification.delete(); } UserNotification newUserNotification = UserNotification.create("Extra Calibration Requested", "extra_calibration_alert"); String title = "Calibration Needed"; String content = "A calibration entered now will GREATLY increase performance"; Intent intent = new Intent(mContext, AddCalibration.class); calibrationNotificationCreate(title, content, intent, extraCalibrationNotificationId); } } public static void clearCalibrationRequest() { UserNotification userNotification = UserNotification.lastCalibrationAlert(); if (userNotification != null) { userNotification.delete(); notificationDismiss(calibrationNotificationId); } } public static void clearDoubleCalibrationRequest() { UserNotification userNotification = UserNotification.lastDoubleCalibrationAlert(); if (userNotification != null) { userNotification.delete(); notificationDismiss(doubleCalibrationNotificationId); } } public static void clearExtraCalibrationRequest() { UserNotification userNotification = UserNotification.lastExtraCalibrationAlert(); if (userNotification != null) { userNotification.delete(); notificationDismiss(extraCalibrationNotificationId); } } public static void clearBgAlert() { UserNotification userNotification = UserNotification.lastBgAlert(); if (userNotification != null) { userNotification.delete(); notificationDismiss(BgNotificationId); } } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/UtilityModels/PebbleSync.java ================================================ package com.eveningoutpost.dexdrip.UtilityModels; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.BatteryManager; import android.util.Log; import com.eveningoutpost.dexdrip.Models.BgReading; import com.getpebble.android.kit.PebbleKit; import com.getpebble.android.kit.util.PebbleDictionary; import java.util.Date; import java.util.UUID; /** * Created by THE NIGHTSCOUT PROJECT CONTRIBUTORS (and adapted to fit the needs of this project) */ public class PebbleSync { // CGM_ICON_KEY = 0x0, // TUPLE_CSTRING, MAX 2 BYTES (10) // CGM_BG_KEY = 0x1, // TUPLE_CSTRING, MAX 4 BYTES (253 OR 22.2) // CGM_TCGM_KEY = 0x2, // TUPLE_INT, 4 BYTES (CGM TIME) // CGM_TAPP_KEY = 0x3, // TUPLE_INT, 4 BYTES (APP / PHONE TIME) // CGM_DLTA_KEY = 0x4, // TUPLE_CSTRING, MAX 5 BYTES (BG DELTA, -100 or -10.0) // CGM_UBAT_KEY = 0x5, // TUPLE_CSTRING, MAX 3 BYTES (UPLOADER BATTERY, 100) // CGM_NAME_KEY = 0x6 // TUPLE_CSTRING, MAX 9 BYTES (xDrip) public static final UUID PEBBLEAPP_UUID = UUID.fromString("2c3f5ab3-7506-44e7-b8d0-2c63de32e1ec"); public static final int ICON_KEY = 0; public static final int BG_KEY = 1; public static final int RECORD_TIME_KEY = 2; public static final int PHONE_TIME_KEY = 3; public static final int BG_DELTA_KEY = 4; public static final int UPLOADER_BATTERY_KEY = 5; public static final int NAME_KEY = 6; private Context mContext; private BgGraphBuilder bgGraphBuilder; private BgReading mBgReading; public PebbleDictionary buildDictionary() { PebbleDictionary dictionary = new PebbleDictionary(); dictionary.addString(ICON_KEY, slopeOrdinal()); dictionary.addString(BG_KEY, bgReading()); dictionary.addUint32(RECORD_TIME_KEY, (int) (mBgReading.timestamp / 1000)); dictionary.addUint32(PHONE_TIME_KEY, (int) (new Date().getTime() / 1000)); dictionary.addString(BG_DELTA_KEY, bgDelta()); dictionary.addString(UPLOADER_BATTERY_KEY, phoneBattery()); dictionary.addString(NAME_KEY, "xDrip"); return dictionary; } public void sendData(Context context, BgReading bgReading){ mContext = context; bgGraphBuilder = new BgGraphBuilder(mContext); mBgReading = BgReading.last(); sendDownload(buildDictionary()); } public String bgReading() { return bgGraphBuilder.unitized_string(mBgReading.calculated_value); } public String bgDelta() { String deltaString = bgGraphBuilder.unitized_string((int)(mBgReading.calculated_value_slope * (5 * 60 * 1000))); if(mBgReading.calculated_value_slope > 0.1) { return ("+"+deltaString); } else if(mBgReading.calculated_value_slope > -0.1 && mBgReading.calculated_value_slope < 0.1) { return "0"; } else { return deltaString; } } public String phoneBattery() { return String.valueOf(getBatteryLevel()); } public String bgUnit() { return bgGraphBuilder.unit(); } public void sendDownload(PebbleDictionary dictionary) { if (PebbleKit.isWatchConnected(mContext)) { if (dictionary != null && mContext != null) { Log.d("PEBBLE PUSHER", "Sending data to pebble"); PebbleKit.sendDataToPebble(mContext, PEBBLEAPP_UUID, dictionary); } } } public int getBatteryLevel() { Intent batteryIntent = mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); if(level == -1 || scale == -1) { return 50; } return (int)(((float)level / (float)scale) * 100.0f); } public String slopeOrdinal(){ double slope_by_minute = mBgReading.calculated_value_slope * 60000; String arrow = "0"; if (slope_by_minute <= (-3.5)) { arrow = "7"; } else if (slope_by_minute <= (-2)) { arrow = "6"; } else if (slope_by_minute <= (-1)) { arrow = "5"; } else if (slope_by_minute <= (1)) { arrow = "4"; } else if (slope_by_minute <= (2)) { arrow = "3"; } else if (slope_by_minute <= (3.5)) { arrow = "2"; } else { arrow = "1"; } if(mBgReading.hide_slope) { arrow = "9"; } return arrow; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/UtilityModels/RedBearLabAttributes.java ================================================ package com.eveningoutpost.dexdrip.UtilityModels; import java.util.UUID; /** * Created by stephenblack on 2/21/15. */ public class RedBearLabAttributes { public static UUID CLIENT_CHARACTERISTIC_CONFIG = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); public static UUID REDBEARLAB_SERVICE = UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb"); public static UUID REDBEARLAB_TX = UUID.fromString("713d0002-503e-4c75-ba94-3148f18d941e"); } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/UtilityModels/RestCalls.java ================================================ package com.eveningoutpost.dexdrip.UtilityModels; import android.util.Log; import com.eveningoutpost.dexdrip.Interfaces.BgReadingInterface; import com.eveningoutpost.dexdrip.Interfaces.CalibrationInterface; import com.eveningoutpost.dexdrip.Interfaces.SensorInterface; import com.eveningoutpost.dexdrip.Models.BgReading; import com.eveningoutpost.dexdrip.Models.Calibration; import com.eveningoutpost.dexdrip.Sensor; import com.eveningoutpost.dexdrip.Models.User; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.internal.bind.DateTypeAdapter; import java.util.Date; import retrofit.Callback; import retrofit.RequestInterceptor; import retrofit.RestAdapter; import retrofit.RetrofitError; import retrofit.client.Response; import retrofit.converter.GsonConverter; /** * Created by stephenblack on 11/6/14. */ public class RestCalls { private static final String baseUrl = "http://10.0.2.2:3000"; public static Gson gson = new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() .registerTypeAdapter(Date.class, new DateTypeAdapter()) .create(); public static void sendBgReading(final BgSendQueue bgSendQueue) { User user = User.currentUser(); bgReadingInterface().createReading(user.uuid, bgSendQueue.bgReading, new Callback() { @Override public void success(Gson gsonResponse, Response response) { bgSendQueue.success = true; bgSendQueue.save(); BgReading bgReading = bgSendQueue.bgReading; bgReading.synced = true; bgReading.save(); } @Override public void failure(RetrofitError error) { Response response = error.getResponse(); Log.w("REST CALL ERROR:", "****************"); Log.w("REST CALL STATUS:", "" + response.getStatus()); Log.w("REST CALL REASON:", response.getReason()); } } ); } public static void updateBgReading(final BgSendQueue bgSendQueue) { User user = User.currentUser(); bgReadingInterface().updateReading(user.uuid, bgSendQueue.bgReading.uuid, bgSendQueue.bgReading, new Callback() { @Override public void success(Gson gsonResponse, Response response) { Log.w("REST CALL Update Success!:", "****************"); bgSendQueue.success = true; bgSendQueue.save(); } @Override public void failure(RetrofitError error) { Response response = error.getResponse(); Log.w("REST CALL ERROR:", "****************"); Log.w("REST CALL STATUS:", "" + response.getStatus()); Log.w("REST CALL REASON:", response.getReason()); } } ); } public static void sendCalibration(final CalibrationSendQueue calibrationSendQueue) { User user = User.currentUser(); calibrationInterface().createCalibration(user.uuid, calibrationSendQueue.calibration, new Callback() { @Override public void success(Gson gsonResponse, Response response) { calibrationSendQueue.success = true; calibrationSendQueue.save(); Calibration calibration = calibrationSendQueue.calibration; calibration.save(); } @Override public void failure(RetrofitError error) { Response response = error.getResponse(); Log.w("REST CALL ERROR:", "****************"); Log.w("REST CALL STATUS:", "" + response.getStatus()); Log.w("REST CALL REASON:", response.getReason()); } } ); } public static void sendSensor(final SensorSendQueue sensorSendQueue) { User user = User.currentUser(); sensorInterface().createSensor(user.uuid, sensorSendQueue.sensor, new Callback() { @Override public void success(Gson gsonResponse, Response response) { sensorSendQueue.success = true; sensorSendQueue.save(); Sensor sensor = sensorSendQueue.sensor; sensor.save(); } @Override public void failure(RetrofitError error) { Response response = error.getResponse(); Log.w("REST CALL ERROR:", "****************"); Log.w("REST CALL STATUS:", "" + response.getStatus()); Log.w("REST CALL REASON:", response.getReason()); } } ); } public static BgReadingInterface bgReadingInterface() { RestAdapter adapter = adapterBuilder().build(); BgReadingInterface bgReadingInterface = adapter.create(BgReadingInterface.class); return bgReadingInterface; } public static SensorInterface sensorInterface() { RestAdapter adapter = adapterBuilder().build(); SensorInterface sensorInterface = adapter.create(SensorInterface.class); return sensorInterface; } public static CalibrationInterface calibrationInterface() { RestAdapter adapter = adapterBuilder().build(); CalibrationInterface calibrationInterface = adapter.create(CalibrationInterface.class); return calibrationInterface; } public static RestAdapter.Builder adapterBuilder() { RestAdapter.Builder adapterBuilder = new RestAdapter.Builder(); adapterBuilder .setEndpoint(baseUrl) .setConverter(new GsonConverter(gson)) .setRequestInterceptor(requestInterceptor()); return adapterBuilder; } public static RequestInterceptor requestInterceptor(){ RequestInterceptor requestInterceptor = new RequestInterceptor() { User currentUser = User.currentUser(); @Override public void intercept(RequestFacade request) { request.addHeader("email", currentUser.email); request.addHeader("token", currentUser.token); } }; return requestInterceptor; } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/UtilityModels/SensorSendQueue.java ================================================ package com.eveningoutpost.dexdrip.UtilityModels; import android.provider.BaseColumns; import com.activeandroid.Model; import com.activeandroid.annotation.Column; import com.activeandroid.annotation.Table; import com.activeandroid.query.Select; import com.eveningoutpost.dexdrip.Sensor; import java.util.List; /** * Created by stephenblack on 11/7/14. */ @Table(name = "SensorSendQueue", id = BaseColumns._ID) public class SensorSendQueue extends Model { @Column(name = "Sensor", index = true) public Sensor sensor; @Column(name = "success", index = true) public boolean success; public static SensorSendQueue nextSensorJob() { SensorSendQueue job = new Select() .from(SensorSendQueue.class) .where("success =", false) .orderBy("_ID desc") .limit(1) .executeSingle(); return job; } public static List queue() { return new Select() .from(SensorSendQueue.class) .where("success = ?", false) .orderBy("_ID desc") .execute(); } public static void addToQueue(Sensor sensor) { SensorSendQueue sensorSendQueue = new SensorSendQueue(); sensorSendQueue.sensor = sensor; sensorSendQueue.success = false; sensorSendQueue.save(); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/utils/DatabaseUtil.java ================================================ package com.eveningoutpost.dexdrip.utils; import android.content.Context; import android.net.Uri; import android.os.Environment; import android.text.format.DateFormat; import com.activeandroid.Configuration; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.nio.channels.FileChannel; import static com.eveningoutpost.dexdrip.utils.FileUtils.*; /** * Save the SQL database to file. */ public class DatabaseUtil { public static String saveSql(Context context) { try { final String databaseName = new Configuration.Builder(context).create().getDatabaseName(); final String dir = getExternalDir(); makeSureDirectoryExists(dir); final StringBuilder sb = new StringBuilder(); sb.append(dir); sb.append("/export"); sb.append(DateFormat.format("yyyyMMdd-kkmmss", System.currentTimeMillis())); sb.append(".sqlite"); final String filename = sb.toString(); final File sd = Environment.getExternalStorageDirectory(); if (sd.canWrite()) { final File currentDB = context.getDatabasePath(databaseName); final File backupDB = new File(filename); if (currentDB.exists()) { final FileInputStream srcStream = new FileInputStream(currentDB); final FileChannel src = srcStream.getChannel(); final FileOutputStream destStream = new FileOutputStream(backupDB); final FileChannel dst = destStream.getChannel(); dst.transferFrom(src, 0, src.size()); src.close(); srcStream.close(); dst.close(); destStream.close(); } } return filename; } catch (final Exception e) { throw new RuntimeException(e); } } public static void loadSql(Context context, Uri uri) { try { final String databaseName = new Configuration.Builder(context).create().getDatabaseName(); final File currentDB = context.getDatabasePath(databaseName); final File replacement = new File(uri.getPath()); if (currentDB.canWrite()) { final FileInputStream srcStream = new FileInputStream(replacement); final FileChannel src = srcStream.getChannel(); final FileOutputStream destStream = new FileOutputStream(currentDB); final FileChannel dst = destStream.getChannel(); dst.transferFrom(src, 0, src.size()); src.close(); srcStream.close(); dst.close(); destStream.close(); } else { throw new RuntimeException("Couldn't write to " + currentDB); } } catch (final Exception e) { throw new RuntimeException(e); } } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/utils/FileUtils.java ================================================ package com.eveningoutpost.dexdrip.utils; import android.os.Environment; import java.io.File; public class FileUtils { public static boolean makeSureDirectoryExists( final String dir ) { final File file = new File( dir ); return file.exists() || file.mkdirs(); } public static String getExternalDir() { final StringBuilder sb = new StringBuilder(); sb.append( Environment.getExternalStorageDirectory().getAbsolutePath() ); sb.append( "/xdrip" ); final String dir = sb.toString(); return dir; } public static String combine( final String path1, final String path2 ) { final File file1 = new File( path1 ); final File file2 = new File( file1, path2 ); return file2.getPath(); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/utils/Preferences.java ================================================ package com.eveningoutpost.dexdrip.utils; import android.content.Context; import android.content.SharedPreferences; import android.content.res.Configuration; import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; import android.os.Bundle; import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceActivity; import android.preference.PreferenceCategory; import android.preference.PreferenceFragment; import android.preference.PreferenceGroup; import android.preference.PreferenceManager; import android.preference.PreferenceScreen; import android.preference.RingtonePreference; import android.text.TextUtils; import android.util.Log; import com.eveningoutpost.dexdrip.R; import com.eveningoutpost.dexdrip.UtilityModels.CollectionServiceStarter; import com.eveningoutpost.dexdrip.UtilityModels.ForegroundServiceStarter; import java.util.List; /** * A {@link PreferenceActivity} that presents a set of application settings. On * handset devices, settings are presented as a single list. On tablets, * settings are split by category, with category headers shown to the left of * the list of settings. *

* See * Android Design: Settings for design guidelines and the Settings * API Guide for more information on developing a Settings UI. */ public class Preferences extends PreferenceActivity { public static SharedPreferences prefs; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getFragmentManager().beginTransaction().replace(android.R.id.content, new AllPrefsFragment()).commit(); } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); // addPreferencesFromResource(R.xml.pref_general); } @Override protected boolean isValidFragment(String fragmentName) { if (AllPrefsFragment.class.getName().equals(fragmentName)){ return true; } return false; } @Override public boolean onIsMultiPane() { return isXLargeTablet(this); } private static boolean isXLargeTablet(Context context) { return (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE; } @Override public void onBuildHeaders(List

target) { loadHeadersFromResource(R.xml.pref_headers, target); } private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object value) { String stringValue = value.toString(); if (preference instanceof ListPreference) { ListPreference listPreference = (ListPreference) preference; int index = listPreference.findIndexOfValue(stringValue); preference.setSummary( index >= 0 ? listPreference.getEntries()[index] : null); } else if (preference instanceof RingtonePreference) { // For ringtone preferences, look up the correct display value // using RingtoneManager. if (TextUtils.isEmpty(stringValue)) { // Empty values correspond to 'silent' (no ringtone). preference.setSummary(R.string.pref_ringtone_silent); } else { Ringtone ringtone = RingtoneManager.getRingtone( preference.getContext(), Uri.parse(stringValue)); if (ringtone == null) { // Clear the summary if there was a lookup error. preference.setSummary(null); } else { // Set the summary to reflect the new ringtone display // name. String name = ringtone.getTitle(preference.getContext()); preference.setSummary(name); } } } else { // For all other preferences, set the summary to the value's // simple string representation. preference.setSummary(stringValue); } return true; } }; private static void bindPreferenceSummaryToValue(Preference preference) { preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener); sBindPreferenceSummaryToValueListener.onPreferenceChange(preference, PreferenceManager .getDefaultSharedPreferences(preference.getContext()) .getString(preference.getKey(), "")); } public static class AllPrefsFragment extends PreferenceFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.pref_license); addPreferencesFromResource(R.xml.pref_general); bindPreferenceSummaryToValue(findPreference("highValue")); bindPreferenceSummaryToValue(findPreference("lowValue")); bindPreferenceSummaryToValue(findPreference("units")); addPreferencesFromResource(R.xml.pref_notifications); bindPreferenceSummaryToValue(findPreference("bg_snooze")); addPreferencesFromResource(R.xml.pref_data_source); addPreferencesFromResource(R.xml.pref_data_sync); bindPreferenceSummaryToValue(findPreference("cloud_storage_mongodb_uri")); bindPreferenceSummaryToValue(findPreference("cloud_storage_mongodb_collection")); bindPreferenceSummaryToValue(findPreference("cloud_storage_mongodb_device_status_collection")); bindPreferenceSummaryToValue(findPreference("cloud_storage_api_base")); addPreferencesFromResource(R.xml.pref_advanced_settings); final Preference collectionMethod = findPreference("dex_collection_method"); final Preference runInForeground = findPreference("run_service_in_foreground"); final Preference wifiRecievers = findPreference("wifi_recievers_addresses"); final Preference predictiveBG = findPreference("predictive_bg"); final Preference interpretRaw = findPreference("interpret_raw"); final Preference shareKey = findPreference("share_key"); final PreferenceCategory collectionCategory = (PreferenceCategory) findPreference("collection_category"); final PreferenceCategory otherCategory = (PreferenceCategory) findPreference("other_category"); final PreferenceScreen calibrationAlertsScreen = (PreferenceScreen) findPreference("calibration_alerts_screen"); final PreferenceCategory alertsCategory = (PreferenceCategory) findPreference("alerts_category"); prefs = getPreferenceManager().getDefaultSharedPreferences(getActivity()); Log.d("PREF", prefs.getString("dex_collection_method", "BluetoothWixel")); if(prefs.getString("dex_collection_method", "BluetoothWixel").compareTo("DexcomShare") != 0) { collectionCategory.removePreference(shareKey); otherCategory.removePreference(interpretRaw); alertsCategory.addPreference(calibrationAlertsScreen); } else { otherCategory.removePreference(predictiveBG); alertsCategory.removePreference(calibrationAlertsScreen); prefs.edit().putBoolean("calibration_notifications", false).apply(); } if(prefs.getString("dex_collection_method", "BluetoothWixel").compareTo("BluetoothWixel") != 0 && prefs.getString("dex_collection_method", "BluetoothWixel").compareTo("DexcomShare") != 0) { collectionCategory.removePreference(runInForeground); } if(prefs.getString("dex_collection_method", "BluetoothWixel").compareTo("WifiWixel") != 0) { collectionCategory.removePreference(wifiRecievers); } bindPreferenceSummaryToValue(collectionMethod); bindPreferenceSummaryToValue(shareKey); bindPreferenceSummaryToValue(wifiRecievers); collectionMethod.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { if(((String) newValue).compareTo("DexcomShare") != 0) { // NOT USING SHARE collectionCategory.removePreference(shareKey); otherCategory.removePreference(interpretRaw); otherCategory.addPreference(predictiveBG); alertsCategory.addPreference(calibrationAlertsScreen); } else { collectionCategory.addPreference(shareKey); otherCategory.addPreference(interpretRaw); otherCategory.removePreference(predictiveBG); alertsCategory.removePreference(calibrationAlertsScreen); prefs.edit().putBoolean("calibration_notifications", false).apply(); } if(((String) newValue).compareTo("BluetoothWixel") != 0 && ((String) newValue).compareTo("DexcomShare") != 0) { collectionCategory.removePreference(runInForeground); } else { collectionCategory.addPreference(runInForeground); } if(((String) newValue).compareTo("WifiWixel") != 0) { collectionCategory.removePreference(wifiRecievers); } else { collectionCategory.addPreference(wifiRecievers); } String stringValue = newValue.toString(); if (preference instanceof ListPreference) { ListPreference listPreference = (ListPreference) preference; int index = listPreference.findIndexOfValue(stringValue); preference.setSummary( index >= 0 ? listPreference.getEntries()[index] : null); } else if (preference instanceof RingtonePreference) { if (TextUtils.isEmpty(stringValue)) { preference.setSummary(R.string.pref_ringtone_silent); } else { Ringtone ringtone = RingtoneManager.getRingtone( preference.getContext(), Uri.parse(stringValue)); if (ringtone == null) { preference.setSummary(null); } else { String name = ringtone.getTitle(preference.getContext()); preference.setSummary(name); } } } else { preference.setSummary(stringValue); } CollectionServiceStarter.restartCollectionService(preference.getContext()); return true; } }); } } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/utils/ShareNotification.java ================================================ package com.eveningoutpost.dexdrip.utils; import android.annotation.TargetApi; import android.app.Notification; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Build; import android.support.v4.app.NotificationCompat; /** * Helper class to show a share notificaiton. */ public class ShareNotification { public static void viewOrShare(String mime, Uri uri, NotificationCompat.Builder builder, Context context) { final Intent viewFileIntent = new Intent(Intent.ACTION_VIEW); viewFileIntent.setDataAndType(uri, mime); ResolveInfo matches = context.getPackageManager().resolveActivity(viewFileIntent, PackageManager.MATCH_DEFAULT_ONLY); if (matches != null) { final PendingIntent resultPendingIntent = PendingIntent.getActivity(context, 0, viewFileIntent, PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(resultPendingIntent); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { addShare(builder, mime, uri, context); } } else { final Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); shareIntent.putExtra(Intent.EXTRA_STREAM, uri); shareIntent.setType(mime); final PendingIntent sharePendingIntent = PendingIntent.getActivity(context, 0, shareIntent, PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(sharePendingIntent); } } @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private static void addShare(NotificationCompat.Builder notification, String mime, Uri uri, Context context) { final Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); shareIntent.putExtra(Intent.EXTRA_STREAM, uri); shareIntent.setType(mime); final PendingIntent sharePendingIntent = PendingIntent.getActivity(context, 0, shareIntent, PendingIntent.FLAG_UPDATE_CURRENT); notification.addAction(android.R.drawable.ic_menu_share, "Share", sharePendingIntent); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/widgetUpdateService.java ================================================ package com.eveningoutpost.dexdrip; import android.app.AlarmManager; import android.app.PendingIntent; import android.app.Service; import android.appwidget.AppWidgetManager; 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.os.IBinder; import android.preference.PreferenceManager; import android.util.Log; import com.eveningoutpost.dexdrip.ImportedLibraries.dexcom.ReadDataShare; import com.eveningoutpost.dexdrip.Models.BgReading; import com.eveningoutpost.dexdrip.Services.DexShareCollectionService; import com.eveningoutpost.dexdrip.UtilityModels.CollectionServiceStarter; import com.eveningoutpost.dexdrip.UtilityModels.Intents; import java.util.Calendar; import java.util.Date; public class widgetUpdateService extends Service { public String TAG = "widgetUpdateService"; BroadcastReceiver _broadcastReceiver; public widgetUpdateService() {} @Override public IBinder onBind(Intent intent) { throw new UnsupportedOperationException("Not yet implemented"); } @Override public void onCreate() { super.onCreate(); setFailoverTimer(); _broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context ctx, Intent intent) { if (intent.getAction().compareTo(Intent.ACTION_TIME_TICK) == 0) { updateCurrentBgInfo(); } } }; registerReceiver(_broadcastReceiver, new IntentFilter(Intent.ACTION_TIME_TICK)); } @Override public int onStartCommand(Intent intent, int flags, int startId) { setFailoverTimer(); updateCurrentBgInfo(); return START_STICKY; } @Override public void onDestroy() { super.onDestroy(); if (_broadcastReceiver != null) { unregisterReceiver(_broadcastReceiver); } } public void setFailoverTimer() { //Keep it alive! if(AppWidgetManager.getInstance(getApplication()).getAppWidgetIds(new ComponentName(getApplication(), xDripWidget.class)).length > 0) { long retry_in = (1000 * 60 * 5); Log.d(TAG, "Fallover Restarting in: " + (retry_in / (60 * 1000)) + " minutes"); Calendar calendar = Calendar.getInstance(); AlarmManager alarm = (AlarmManager) getSystemService(ALARM_SERVICE); alarm.set(alarm.RTC_WAKEUP, calendar.getTimeInMillis() + retry_in, PendingIntent.getService(this, 0, new Intent(this, widgetUpdateService.class), 0)); } } public void updateCurrentBgInfo() { Log.d(TAG, "Sending update flag to widget"); int ids[] = AppWidgetManager.getInstance(getApplication()).getAppWidgetIds(new ComponentName(getApplication(), xDripWidget.class)); Log.d(TAG, "Updating " + ids.length + " widgets"); Intent intent = new Intent(this,xDripWidget.class); intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS,ids); sendBroadcast(intent); } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/xDripWidget.java ================================================ package com.eveningoutpost.dexdrip; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.graphics.Paint; import android.preference.PreferenceManager; import android.util.Log; import android.widget.RemoteViews; import android.widget.TextView; import com.eveningoutpost.dexdrip.Models.BgReading; import com.eveningoutpost.dexdrip.Services.DexCollectionService; import com.eveningoutpost.dexdrip.UtilityModels.BgGraphBuilder; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * Implementation of App Widget functionality. */ public class xDripWidget extends AppWidgetProvider { public static RemoteViews views; public static Context mContext; public static String TAG = "xDripWidget"; @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { final int N = appWidgetIds.length; for (int i = 0; i < N; i++) { updateAppWidget(context, appWidgetManager, appWidgetIds[i]); } } @Override public void onEnabled(Context context) { Log.d(TAG, "Widget enabled"); context.startService(new Intent(context, widgetUpdateService.class)); } @Override public void onDisabled(Context context) { Log.d(TAG, "Widget disabled"); // Enter relevant functionality for when the last widget is disabled } static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) { mContext = context; views = new RemoteViews(context.getPackageName(), R.layout.x_drip_widget); Log.d(TAG, "Update widget signal received"); displayCurrentInfo(); appWidgetManager.updateAppWidget(appWidgetId, views); } public static void displayCurrentInfo() { BgGraphBuilder bgGraphBuilder = new BgGraphBuilder(mContext); BgReading lastBgreading = BgReading.lastNoSenssor(); if (lastBgreading != null) { double estimate = 0; if ((new Date().getTime()) - (60000 * 11) - lastBgreading.timestamp > 0) { estimate = lastBgreading.calculated_value; Log.d(TAG, "old value, estimate " + estimate); views.setTextViewText(R.id.widgetBg, bgGraphBuilder.unitized_string(estimate)); views.setTextViewText(R.id.widgetArrow, "--"); views.setInt(R.id.widgetBg, "setPaintFlags", Paint.STRIKE_THRU_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG); } else { estimate = lastBgreading.calculated_value; String stringEstimate = bgGraphBuilder.unitized_string(estimate); String slope_arrow = BgReading.slopeArrow((lastBgreading.calculated_value_slope * 60000)); if (lastBgreading.hide_slope) { slope_arrow = "--"; } Log.d(TAG, "newish value, estimate " + stringEstimate + slope_arrow); views.setTextViewText(R.id.widgetBg, stringEstimate); views.setTextViewText(R.id.widgetArrow, slope_arrow); views.setInt(R.id.widgetBg, "setPaintFlags", 0); } List bgReadingList = BgReading.latest(2); if(bgReadingList != null && bgReadingList.size() == 2) { views.setTextViewText(R.id.widgetDelta, bgGraphBuilder.unitizedDeltaString(lastBgreading.calculated_value - bgReadingList.get(1).calculated_value)); } else { views.setTextViewText(R.id.widgetDelta, "--"); } int timeAgo =(int) Math.floor((new Date().getTime() - lastBgreading.timestamp)/(1000*60)); if (timeAgo == 1) { views.setTextViewText(R.id.readingAge, timeAgo + " Minute ago"); } else { views.setTextViewText(R.id.readingAge, timeAgo + " Minutes ago"); } if (timeAgo > 15) { views.setTextColor(R.id.readingAge, Color.parseColor("#FFBB33")); } else { views.setTextColor(R.id.readingAge, Color.WHITE); } if (bgGraphBuilder.unitized(estimate) <= bgGraphBuilder.lowMark) { views.setTextColor(R.id.widgetBg, Color.parseColor("#C30909")); views.setTextColor(R.id.widgetDelta, Color.parseColor("#C30909")); views.setTextColor(R.id.widgetArrow, Color.parseColor("#C30909")); } else if (bgGraphBuilder.unitized(estimate) >= bgGraphBuilder.highMark) { views.setTextColor(R.id.widgetBg, Color.parseColor("#FFBB33")); views.setTextColor(R.id.widgetDelta, Color.parseColor("#FFBB33")); views.setTextColor(R.id.widgetArrow, Color.parseColor("#FFBB33")); } else { views.setTextColor(R.id.widgetBg, Color.WHITE); views.setTextColor(R.id.widgetDelta, Color.WHITE); views.setTextColor(R.id.widgetArrow, Color.WHITE); } } } } ================================================ FILE: app/src/main/java/com/eveningoutpost/dexdrip/xdrip.java ================================================ package com.eveningoutpost.dexdrip; import android.app.Application; import com.crashlytics.android.Crashlytics; import io.fabric.sdk.android.Fabric; import org.acra.ACRA; import org.acra.ReportField; import org.acra.ReportingInteractionMode; import org.acra.annotation.ReportsCrashes; import org.acra.sender.HttpSender; /** * Created by stephenblack on 3/21/15. */ @ReportsCrashes( formUri = "https://yoursolace.cloudant.com/acra-xdrip/_design/acra-storage/_update/report", reportType = HttpSender.Type.JSON, httpMethod = HttpSender.Method.POST, formUriBasicAuthLogin = "nateriverldstiondrephery", formUriBasicAuthPassword = "GEK5Nv7NtMkloAkufNvFgast", formKey = "", // This is required for backward compatibility but not used customReportContent = { ReportField.APP_VERSION_CODE, ReportField.APP_VERSION_NAME, ReportField.ANDROID_VERSION, ReportField.PACKAGE_NAME, ReportField.REPORT_ID, ReportField.BUILD, ReportField.STACK_TRACE }, mode = ReportingInteractionMode.TOAST, logcatArguments = {"-t", "500", "-v", "time"}, resToastText = R.string.toast_crash ) public class xdrip extends Application { @Override public void onCreate() { super.onCreate(); Fabric.with(this, new Crashlytics()); // The following line triggers the initialization of ACRA //ACRA.init(this); } } ================================================ FILE: app/src/main/res/layout/activity_add_calibration.xml ================================================