Update
Signed-off-by: Avior <florian.bouillon@delta-wings.net>
12
.gitignore
vendored
@ -8,3 +8,15 @@
|
|||||||
.externalNativeBuild
|
.externalNativeBuild
|
||||||
.cxx
|
.cxx
|
||||||
local.properties
|
local.properties
|
||||||
|
|
||||||
|
# keystore
|
||||||
|
keystore.properties
|
||||||
|
private_key.pepk
|
||||||
|
upload_key.jks
|
||||||
|
|
||||||
|
# Fastlane
|
||||||
|
fastlane_secret_keys.json
|
||||||
|
|
||||||
|
# App
|
||||||
|
/app/debug
|
||||||
|
/app/release
|
12
.idea/misc.xml
generated
@ -13,28 +13,36 @@
|
|||||||
<entry key="..\:/Git/Dzeio/OpenHealth/app/src/main/res/menu/activity_main_drawer.xml" value="0.10989583333333333" />
|
<entry key="..\:/Git/Dzeio/OpenHealth/app/src/main/res/menu/activity_main_drawer.xml" value="0.10989583333333333" />
|
||||||
<entry key="..\:/Git/Dzeio/OpenHealth/app/src/main/res/menu/fullscreen_dialog.xml" value="0.31197916666666664" />
|
<entry key="..\:/Git/Dzeio/OpenHealth/app/src/main/res/menu/fullscreen_dialog.xml" value="0.31197916666666664" />
|
||||||
<entry key="..\:/Git/Dzeio/OpenHealth/app/src/main/res/menu/main.xml" value="0.1875" />
|
<entry key="..\:/Git/Dzeio/OpenHealth/app/src/main/res/menu/main.xml" value="0.1875" />
|
||||||
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/debug/res/drawable/ic_logo_app.xml" value="0.3645" />
|
||||||
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/drawable-v24/ic_launcher_foreground.xml" value="0.3645" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/drawable/half_circle.xml" value="0.421" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/drawable/half_circle.xml" value="0.421" />
|
||||||
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/drawable/ic_baseline_add_24.xml" value="0.143" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/drawable/ic_ellipse_2.xml" value="0.4165" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/drawable/ic_ellipse_2.xml" value="0.4165" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/drawable/ic_logo_app.xml" value="0.163" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/drawable/ic_logo_app.xml" value="0.163" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/activity_main.xml" value="0.5" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/drawable/ic_outline_hexagon_24.xml" value="0.143" />
|
||||||
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/activity_main.xml" value="0.1" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/app_bar_main.xml" value="0.5192107995846313" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/app_bar_main.xml" value="0.5192107995846313" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/content_main.xml" value="0.5135869565217391" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/content_main.xml" value="0.5135869565217391" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/dialog_add_weight.xml" value="0.2604166666666667" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/dialog_add_weight.xml" value="0.2604166666666667" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/dialog_edit_weight.xml" value="0.33" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/dialog_edit_weight.xml" value="0.33" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/dialog_register_weight.xml" value="0.5" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/dialog_register_weight.xml" value="0.5" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/dialog_water_edit_water.xml" value="0.2674772036474164" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/dialog_water_edit_water.xml" value="0.2674772036474164" />
|
||||||
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_extension.xml" value="0.35833333333333334" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_extensions.xml" value="0.55" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_extensions.xml" value="0.55" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_gallery.xml" value="0.3109375" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_gallery.xml" value="0.3109375" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_home.xml" value="0.6845493562231759" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_home.xml" value="0.5897435897435898" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_import.xml" value="0.55" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_import.xml" value="0.55" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_list_weight.xml" value="0.33" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_list_weight.xml" value="0.33" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_main_water_home.xml" value="0.225" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_main_water_home.xml" value="0.225" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_water_home.xml" value="0.55" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/fragment_water_home.xml" value="0.55" />
|
||||||
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/layout_extension_item.xml" value="0.39947916666666666" />
|
||||||
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/layout_item_list.xml" value="1.1" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/layout_item_weight.xml" value="0.5" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/layout_item_weight.xml" value="0.5" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/nav_header_main.xml" value="0.3109375" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/layout/nav_header_main.xml" value="0.3109375" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/menu/activity_main_drawer.xml" value="0.39947916666666666" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/menu/activity_main_drawer.xml" value="0.39947916666666666" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/menu/fullscreen_dialog.xml" value="0.40185185185185185" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/menu/fullscreen_dialog.xml" value="0.40185185185185185" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/menu/main.xml" value="0.39947916666666666" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/menu/main.xml" value="0.39947916666666666" />
|
||||||
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml" value="0.3645" />
|
||||||
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/xml/preferences.xml" value="0.55" />
|
<entry key="..\:/git/Dzeio/OpenHealth/app/src/main/res/xml/preferences.xml" value="0.55" />
|
||||||
</map>
|
</map>
|
||||||
</option>
|
</option>
|
||||||
|
214
Gemfile.lock
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
GEM
|
||||||
|
remote: https://rubygems.org/
|
||||||
|
specs:
|
||||||
|
CFPropertyList (3.0.5)
|
||||||
|
rexml
|
||||||
|
addressable (2.8.0)
|
||||||
|
public_suffix (>= 2.0.2, < 5.0)
|
||||||
|
artifactory (3.0.15)
|
||||||
|
atomos (0.1.3)
|
||||||
|
aws-eventstream (1.2.0)
|
||||||
|
aws-partitions (1.543.0)
|
||||||
|
aws-sdk-core (3.125.0)
|
||||||
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
|
aws-partitions (~> 1, >= 1.525.0)
|
||||||
|
aws-sigv4 (~> 1.1)
|
||||||
|
jmespath (~> 1.0)
|
||||||
|
aws-sdk-kms (1.53.0)
|
||||||
|
aws-sdk-core (~> 3, >= 3.125.0)
|
||||||
|
aws-sigv4 (~> 1.1)
|
||||||
|
aws-sdk-s3 (1.110.0)
|
||||||
|
aws-sdk-core (~> 3, >= 3.125.0)
|
||||||
|
aws-sdk-kms (~> 1)
|
||||||
|
aws-sigv4 (~> 1.4)
|
||||||
|
aws-sigv4 (1.4.0)
|
||||||
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
|
babosa (1.0.4)
|
||||||
|
claide (1.0.3)
|
||||||
|
colored (1.2)
|
||||||
|
colored2 (3.1.2)
|
||||||
|
commander (4.6.0)
|
||||||
|
highline (~> 2.0.0)
|
||||||
|
declarative (0.0.20)
|
||||||
|
digest-crc (0.6.4)
|
||||||
|
rake (>= 12.0.0, < 14.0.0)
|
||||||
|
domain_name (0.5.20190701)
|
||||||
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
|
dotenv (2.7.6)
|
||||||
|
emoji_regex (3.2.3)
|
||||||
|
excon (0.89.0)
|
||||||
|
faraday (1.8.0)
|
||||||
|
faraday-em_http (~> 1.0)
|
||||||
|
faraday-em_synchrony (~> 1.0)
|
||||||
|
faraday-excon (~> 1.1)
|
||||||
|
faraday-httpclient (~> 1.0.1)
|
||||||
|
faraday-net_http (~> 1.0)
|
||||||
|
faraday-net_http_persistent (~> 1.1)
|
||||||
|
faraday-patron (~> 1.0)
|
||||||
|
faraday-rack (~> 1.0)
|
||||||
|
multipart-post (>= 1.2, < 3)
|
||||||
|
ruby2_keywords (>= 0.0.4)
|
||||||
|
faraday-cookie_jar (0.0.7)
|
||||||
|
faraday (>= 0.8.0)
|
||||||
|
http-cookie (~> 1.0.0)
|
||||||
|
faraday-em_http (1.0.0)
|
||||||
|
faraday-em_synchrony (1.0.0)
|
||||||
|
faraday-excon (1.1.0)
|
||||||
|
faraday-httpclient (1.0.1)
|
||||||
|
faraday-net_http (1.0.1)
|
||||||
|
faraday-net_http_persistent (1.2.0)
|
||||||
|
faraday-patron (1.0.0)
|
||||||
|
faraday-rack (1.0.0)
|
||||||
|
faraday_middleware (1.2.0)
|
||||||
|
faraday (~> 1.0)
|
||||||
|
fastimage (2.2.6)
|
||||||
|
fastlane (2.199.0)
|
||||||
|
CFPropertyList (>= 2.3, < 4.0.0)
|
||||||
|
addressable (>= 2.8, < 3.0.0)
|
||||||
|
artifactory (~> 3.0)
|
||||||
|
aws-sdk-s3 (~> 1.0)
|
||||||
|
babosa (>= 1.0.3, < 2.0.0)
|
||||||
|
bundler (>= 1.12.0, < 3.0.0)
|
||||||
|
colored
|
||||||
|
commander (~> 4.6)
|
||||||
|
dotenv (>= 2.1.1, < 3.0.0)
|
||||||
|
emoji_regex (>= 0.1, < 4.0)
|
||||||
|
excon (>= 0.71.0, < 1.0.0)
|
||||||
|
faraday (~> 1.0)
|
||||||
|
faraday-cookie_jar (~> 0.0.6)
|
||||||
|
faraday_middleware (~> 1.0)
|
||||||
|
fastimage (>= 2.1.0, < 3.0.0)
|
||||||
|
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||||
|
google-apis-androidpublisher_v3 (~> 0.3)
|
||||||
|
google-apis-playcustomapp_v1 (~> 0.1)
|
||||||
|
google-cloud-storage (~> 1.31)
|
||||||
|
highline (~> 2.0)
|
||||||
|
json (< 3.0.0)
|
||||||
|
jwt (>= 2.1.0, < 3)
|
||||||
|
mini_magick (>= 4.9.4, < 5.0.0)
|
||||||
|
multipart-post (~> 2.0.0)
|
||||||
|
naturally (~> 2.2)
|
||||||
|
optparse (~> 0.1.1)
|
||||||
|
plist (>= 3.1.0, < 4.0.0)
|
||||||
|
rubyzip (>= 2.0.0, < 3.0.0)
|
||||||
|
security (= 0.1.3)
|
||||||
|
simctl (~> 1.6.3)
|
||||||
|
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||||
|
terminal-table (>= 1.4.5, < 2.0.0)
|
||||||
|
tty-screen (>= 0.6.3, < 1.0.0)
|
||||||
|
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||||
|
word_wrap (~> 1.0.0)
|
||||||
|
xcodeproj (>= 1.13.0, < 2.0.0)
|
||||||
|
xcpretty (~> 0.3.0)
|
||||||
|
xcpretty-travis-formatter (>= 0.0.3)
|
||||||
|
gh_inspector (1.1.3)
|
||||||
|
google-apis-androidpublisher_v3 (0.14.0)
|
||||||
|
google-apis-core (>= 0.4, < 2.a)
|
||||||
|
google-apis-core (0.4.1)
|
||||||
|
addressable (~> 2.5, >= 2.5.1)
|
||||||
|
googleauth (>= 0.16.2, < 2.a)
|
||||||
|
httpclient (>= 2.8.1, < 3.a)
|
||||||
|
mini_mime (~> 1.0)
|
||||||
|
representable (~> 3.0)
|
||||||
|
retriable (>= 2.0, < 4.a)
|
||||||
|
rexml
|
||||||
|
webrick
|
||||||
|
google-apis-iamcredentials_v1 (0.9.0)
|
||||||
|
google-apis-core (>= 0.4, < 2.a)
|
||||||
|
google-apis-playcustomapp_v1 (0.6.0)
|
||||||
|
google-apis-core (>= 0.4, < 2.a)
|
||||||
|
google-apis-storage_v1 (0.10.0)
|
||||||
|
google-apis-core (>= 0.4, < 2.a)
|
||||||
|
google-cloud-core (1.6.0)
|
||||||
|
google-cloud-env (~> 1.0)
|
||||||
|
google-cloud-errors (~> 1.0)
|
||||||
|
google-cloud-env (1.5.0)
|
||||||
|
faraday (>= 0.17.3, < 2.0)
|
||||||
|
google-cloud-errors (1.2.0)
|
||||||
|
google-cloud-storage (1.35.0)
|
||||||
|
addressable (~> 2.8)
|
||||||
|
digest-crc (~> 0.4)
|
||||||
|
google-apis-iamcredentials_v1 (~> 0.1)
|
||||||
|
google-apis-storage_v1 (~> 0.1)
|
||||||
|
google-cloud-core (~> 1.6)
|
||||||
|
googleauth (>= 0.16.2, < 2.a)
|
||||||
|
mini_mime (~> 1.0)
|
||||||
|
googleauth (1.1.0)
|
||||||
|
faraday (>= 0.17.3, < 2.0)
|
||||||
|
jwt (>= 1.4, < 3.0)
|
||||||
|
memoist (~> 0.16)
|
||||||
|
multi_json (~> 1.11)
|
||||||
|
os (>= 0.9, < 2.0)
|
||||||
|
signet (>= 0.16, < 2.a)
|
||||||
|
highline (2.0.3)
|
||||||
|
http-cookie (1.0.4)
|
||||||
|
domain_name (~> 0.5)
|
||||||
|
httpclient (2.8.3)
|
||||||
|
jmespath (1.4.0)
|
||||||
|
json (2.6.1)
|
||||||
|
jwt (2.3.0)
|
||||||
|
memoist (0.16.2)
|
||||||
|
mini_magick (4.11.0)
|
||||||
|
mini_mime (1.1.2)
|
||||||
|
multi_json (1.15.0)
|
||||||
|
multipart-post (2.0.0)
|
||||||
|
nanaimo (0.3.0)
|
||||||
|
naturally (2.2.1)
|
||||||
|
optparse (0.1.1)
|
||||||
|
os (1.1.4)
|
||||||
|
plist (3.6.0)
|
||||||
|
public_suffix (4.0.6)
|
||||||
|
rake (13.0.6)
|
||||||
|
representable (3.1.1)
|
||||||
|
declarative (< 0.1.0)
|
||||||
|
trailblazer-option (>= 0.1.1, < 0.2.0)
|
||||||
|
uber (< 0.2.0)
|
||||||
|
retriable (3.1.2)
|
||||||
|
rexml (3.2.5)
|
||||||
|
rouge (2.0.7)
|
||||||
|
ruby2_keywords (0.0.5)
|
||||||
|
rubyzip (2.3.2)
|
||||||
|
security (0.1.3)
|
||||||
|
signet (0.16.0)
|
||||||
|
addressable (~> 2.8)
|
||||||
|
faraday (>= 0.17.3, < 2.0)
|
||||||
|
jwt (>= 1.5, < 3.0)
|
||||||
|
multi_json (~> 1.10)
|
||||||
|
simctl (1.6.8)
|
||||||
|
CFPropertyList
|
||||||
|
naturally
|
||||||
|
terminal-notifier (2.0.0)
|
||||||
|
terminal-table (1.8.0)
|
||||||
|
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||||
|
trailblazer-option (0.1.2)
|
||||||
|
tty-cursor (0.7.1)
|
||||||
|
tty-screen (0.8.1)
|
||||||
|
tty-spinner (0.9.3)
|
||||||
|
tty-cursor (~> 0.7)
|
||||||
|
uber (0.1.0)
|
||||||
|
unf (0.1.4)
|
||||||
|
unf_ext
|
||||||
|
unf_ext (0.0.8-x64-mingw32)
|
||||||
|
unicode-display_width (1.8.0)
|
||||||
|
webrick (1.7.0)
|
||||||
|
word_wrap (1.0.0)
|
||||||
|
xcodeproj (1.21.0)
|
||||||
|
CFPropertyList (>= 2.3.3, < 4.0)
|
||||||
|
atomos (~> 0.1.3)
|
||||||
|
claide (>= 1.0.2, < 2.0)
|
||||||
|
colored2 (~> 3.1)
|
||||||
|
nanaimo (~> 0.3.0)
|
||||||
|
rexml (~> 3.2.4)
|
||||||
|
xcpretty (0.3.0)
|
||||||
|
rouge (~> 2.0.7)
|
||||||
|
xcpretty-travis-formatter (1.0.1)
|
||||||
|
xcpretty (~> 0.2, >= 0.0.7)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
x64-mingw32
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
fastlane
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
2.2.32
|
@ -9,36 +9,87 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
|
||||||
|
release {
|
||||||
|
|
||||||
|
def keystorePropertiesFile = rootProject.file("./keystore.properties")
|
||||||
|
def keystoreProperties = new Properties()
|
||||||
|
try {
|
||||||
|
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||||
|
} catch (FileNotFoundException ignored) {
|
||||||
|
keystoreProperties = null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keystoreProperties != null) {
|
||||||
|
storePassword keystoreProperties["storePassword"]
|
||||||
|
keyPassword keystoreProperties["keyPassword"]
|
||||||
|
keyAlias keystoreProperties["keyAlias"]
|
||||||
|
storeFile file(keystoreProperties["storeFile"])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
compileSdk 31
|
compileSdk 31
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
|
// App ID
|
||||||
applicationId "com.dzeio.openhealth"
|
applicationId "com.dzeio.openhealth"
|
||||||
|
|
||||||
|
// Android 5 Lollipop
|
||||||
minSdk 21
|
minSdk 21
|
||||||
|
|
||||||
|
// Android 12
|
||||||
targetSdk 31
|
targetSdk 31
|
||||||
|
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
|
||||||
|
// Semantic Versioning
|
||||||
|
versionName "1.0.0"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|
||||||
release {
|
release {
|
||||||
minifyEnabled false
|
minifyEnabled true
|
||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
signingConfig signingConfigs.release
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug {
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
applicationIdSuffix ".dev"
|
||||||
|
versionNameSuffix '-dev'
|
||||||
|
debuggable true
|
||||||
|
|
||||||
|
// make it debuggable
|
||||||
|
renderscriptDebuggable true
|
||||||
|
|
||||||
|
// Optimization Level
|
||||||
|
renderscriptOptimLevel 0
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = '1.8'
|
jvmTarget = '1.8'
|
||||||
}
|
}
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding true
|
viewBinding true
|
||||||
dataBinding true
|
dataBinding true
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -81,6 +132,7 @@ dependencies {
|
|||||||
|
|
||||||
// Samsung Health
|
// Samsung Health
|
||||||
implementation files('libs/samsung-health-data-1.5.0.aar')
|
implementation files('libs/samsung-health-data-1.5.0.aar')
|
||||||
|
implementation "com.google.code.gson:gson:2.8.9"
|
||||||
|
|
||||||
// ROOM
|
// ROOM
|
||||||
def room_version = "2.4.0"
|
def room_version = "2.4.0"
|
||||||
|
BIN
app/src/debug/ic_launcher-playstore.png
Normal file
After Width: | Height: | Size: 13 KiB |
47
app/src/debug/res/drawable-v24/ic_launcher_foreground.xml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<group android:scaleX="0.15074074"
|
||||||
|
android:scaleY="0.15074074"
|
||||||
|
android:translateX="29.58"
|
||||||
|
android:translateY="29.58">
|
||||||
|
<path
|
||||||
|
android:pathData="M199.975,232V199.975H312V124.025H199.975V12H124.025V124.025H12V199.975H124.025V312H199.975V286.5"
|
||||||
|
android:strokeWidth="23"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeLineCap="square">
|
||||||
|
<aapt:attr name="android:strokeColor">
|
||||||
|
<gradient
|
||||||
|
android:startY="12"
|
||||||
|
android:startX="12"
|
||||||
|
android:endY="312"
|
||||||
|
android:endX="312"
|
||||||
|
android:type="linear">
|
||||||
|
<item android:offset="0" android:color="#FF80E27E"/>
|
||||||
|
<item android:offset="1" android:color="#FF4CAF50"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:pathData="M275,161.5H48M161.5,48V275"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="13"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeLineCap="square">
|
||||||
|
<aapt:attr name="android:strokeColor">
|
||||||
|
<gradient
|
||||||
|
android:startY="24.7418"
|
||||||
|
android:startX="22.416"
|
||||||
|
android:endY="301.98"
|
||||||
|
android:endX="298.723"
|
||||||
|
android:type="linear">
|
||||||
|
<item android:offset="0" android:color="#FF80E27E"/>
|
||||||
|
<item android:offset="1" android:color="#FF4CAF50"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
</group>
|
||||||
|
</vector>
|
42
app/src/debug/res/drawable/ic_logo_app.xml
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="324dp"
|
||||||
|
android:height="324dp"
|
||||||
|
android:viewportWidth="324"
|
||||||
|
android:viewportHeight="324">
|
||||||
|
<path
|
||||||
|
android:pathData="M199.975,232V199.975H312V124.025H199.975V12H124.025V124.025H12V199.975H124.025V312H199.975V286.5"
|
||||||
|
android:strokeWidth="23"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeLineCap="square">
|
||||||
|
<aapt:attr name="android:strokeColor">
|
||||||
|
<gradient
|
||||||
|
android:startY="12"
|
||||||
|
android:startX="12"
|
||||||
|
android:endY="312"
|
||||||
|
android:endX="312"
|
||||||
|
android:type="linear">
|
||||||
|
<item android:offset="0" android:color="#FF80E27E"/>
|
||||||
|
<item android:offset="1" android:color="#FF4CAF50"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:pathData="M275,161.5H48M161.5,48V275"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="13"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeLineCap="square">
|
||||||
|
<aapt:attr name="android:strokeColor">
|
||||||
|
<gradient
|
||||||
|
android:startY="24.7418"
|
||||||
|
android:startX="22.416"
|
||||||
|
android:endY="301.98"
|
||||||
|
android:endX="298.723"
|
||||||
|
android:type="linear">
|
||||||
|
<item android:offset="0" android:color="#FF80E27E"/>
|
||||||
|
<item android:offset="1" android:color="#FF4CAF50"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
</vector>
|
5
app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
BIN
app/src/debug/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
app/src/debug/res/mipmap-hdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
app/src/debug/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
app/src/debug/res/mipmap-mdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
app/src/debug/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
app/src/debug/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 9.2 KiB |
BIN
app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
After Width: | Height: | Size: 13 KiB |
4
app/src/debug/res/values/ic_launcher_background.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="ic_launcher_background">#FFFFFF</color>
|
||||||
|
</resources>
|
4
app/src/debug/res/values/strings.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">OpenHealth - Debug</string>
|
||||||
|
</resources>
|
@ -6,8 +6,8 @@
|
|||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
|
|
||||||
<!-- Phone Services -->
|
<!-- Phone Sensors -->
|
||||||
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION " />
|
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
|
||||||
|
|
||||||
<!-- Samsung Health-->
|
<!-- Samsung Health-->
|
||||||
<queries>
|
<queries>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.dzeio.openhealth
|
package com.dzeio.openhealth
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import com.google.android.material.color.DynamicColors
|
||||||
import dagger.hilt.android.HiltAndroidApp
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
|
|
||||||
@HiltAndroidApp
|
@HiltAndroidApp
|
||||||
@ -8,4 +9,11 @@ class Application : Application() {
|
|||||||
companion object {
|
companion object {
|
||||||
const val TAG = "OpenHealth"
|
const val TAG = "OpenHealth"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
|
||||||
|
// Android Dynamics Colors
|
||||||
|
DynamicColors.applyToActivitiesIfAvailable(this)
|
||||||
|
}
|
||||||
}
|
}
|
@ -10,22 +10,17 @@ import android.util.Log
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.fragment.NavHostFragment
|
import androidx.navigation.fragment.NavHostFragment
|
||||||
import androidx.navigation.fragment.findNavController
|
|
||||||
import androidx.navigation.ui.AppBarConfiguration
|
import androidx.navigation.ui.AppBarConfiguration
|
||||||
import androidx.navigation.ui.navigateUp
|
import androidx.navigation.ui.navigateUp
|
||||||
import androidx.navigation.ui.setupActionBarWithNavController
|
import androidx.navigation.ui.setupActionBarWithNavController
|
||||||
import androidx.navigation.ui.setupWithNavController
|
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import com.dzeio.openhealth.core.BaseActivity
|
import com.dzeio.openhealth.core.BaseActivity
|
||||||
import com.dzeio.openhealth.databinding.ActivityMainBinding
|
import com.dzeio.openhealth.databinding.ActivityMainBinding
|
||||||
import com.dzeio.openhealth.interfaces.NotificationChannels
|
import com.dzeio.openhealth.interfaces.NotificationChannels
|
||||||
import com.dzeio.openhealth.services.WaterReminderService
|
import com.dzeio.openhealth.services.WaterReminderService
|
||||||
import com.dzeio.openhealth.ui.home.HomeFragmentDirections
|
import com.dzeio.openhealth.ui.home.HomeFragmentDirections
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@ -45,6 +40,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
|
|
||||||
val navHostFragment =
|
val navHostFragment =
|
||||||
supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
|
supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
|
||||||
|
|
||||||
navController = navHostFragment.navController
|
navController = navHostFragment.navController
|
||||||
|
|
||||||
appBarConfiguration = AppBarConfiguration(
|
appBarConfiguration = AppBarConfiguration(
|
||||||
@ -69,9 +65,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
override fun onSupportNavigateUp(): Boolean =
|
||||||
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
|
navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
|
||||||
}
|
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(
|
override fun onRequestPermissionsResult(
|
||||||
requestCode: Int,
|
requestCode: Int,
|
||||||
@ -94,21 +89,23 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createNotificationChannel() {
|
private fun createNotificationChannel() {
|
||||||
// Create the NotificationChannel, but only on API 26+ because
|
|
||||||
// the NotificationChannel class is new and not in the support library
|
|
||||||
|
|
||||||
val notificationManager: NotificationManager =
|
val notificationManager: NotificationManager =
|
||||||
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
for (channel in NotificationChannels.values()) {
|
for (channel in NotificationChannels.values()) {
|
||||||
notificationManager.createNotificationChannel(
|
Log.d("MainActivity", channel.channelName)
|
||||||
NotificationChannel(
|
try {
|
||||||
channel.id,
|
notificationManager.createNotificationChannel(
|
||||||
channel.channelName,
|
NotificationChannel(
|
||||||
channel.importance
|
channel.id,
|
||||||
|
channel.channelName,
|
||||||
|
channel.importance
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
} catch (e: Exception) {
|
||||||
|
Log.e("MainActivity", "Error Creating Notification Channel", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
package com.dzeio.openhealth.adapters
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import com.dzeio.openhealth.core.BaseAdapter
|
||||||
|
import com.dzeio.openhealth.core.BaseViewHolder
|
||||||
|
import com.dzeio.openhealth.databinding.LayoutExtensionItemBinding
|
||||||
|
import com.dzeio.openhealth.extensions.Extension
|
||||||
|
|
||||||
|
class ExtensionAdapter() : BaseAdapter<Extension, LayoutExtensionItemBinding>() {
|
||||||
|
|
||||||
|
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> LayoutExtensionItemBinding
|
||||||
|
get() = LayoutExtensionItemBinding::inflate
|
||||||
|
|
||||||
|
var onItemClick: ((weight: Extension) -> Unit)? = null
|
||||||
|
|
||||||
|
override fun onBindData(
|
||||||
|
holder: BaseViewHolder<LayoutExtensionItemBinding>,
|
||||||
|
item: Extension,
|
||||||
|
position: Int
|
||||||
|
) {
|
||||||
|
holder.binding.name.text = item.name
|
||||||
|
holder.binding.status.text = item.getStatus()
|
||||||
|
holder.binding.card.setOnClickListener {
|
||||||
|
onItemClick?.invoke(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,11 @@
|
|||||||
package com.dzeio.openhealth.adapters
|
package com.dzeio.openhealth.adapters
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import com.dzeio.openhealth.core.BaseAdapter
|
import com.dzeio.openhealth.core.BaseAdapter
|
||||||
import com.dzeio.openhealth.core.BaseViewHolder
|
import com.dzeio.openhealth.core.BaseViewHolder
|
||||||
import com.dzeio.openhealth.data.water.Water
|
import com.dzeio.openhealth.data.water.Water
|
||||||
import com.dzeio.openhealth.data.weight.Weight
|
|
||||||
import com.dzeio.openhealth.databinding.LayoutItemListBinding
|
import com.dzeio.openhealth.databinding.LayoutItemListBinding
|
||||||
import com.dzeio.openhealth.databinding.LayoutItemWeightBinding
|
|
||||||
|
|
||||||
class WaterAdapter() : BaseAdapter<Water, LayoutItemListBinding>() {
|
class WaterAdapter() : BaseAdapter<Water, LayoutItemListBinding>() {
|
||||||
|
|
||||||
@ -23,7 +20,7 @@ class WaterAdapter() : BaseAdapter<Water, LayoutItemListBinding>() {
|
|||||||
position: Int
|
position: Int
|
||||||
) {
|
) {
|
||||||
holder.binding.value.text = "${item.value}ml"
|
holder.binding.value.text = "${item.value}ml"
|
||||||
holder.binding.datetime.text = item.formatTimestamp()
|
holder.binding.datetime.text = "${item.formatTimestamp()} ${item.timestamp}"
|
||||||
holder.binding.edit.setOnClickListener {
|
holder.binding.edit.setOnClickListener {
|
||||||
onItemClick?.invoke(item)
|
onItemClick?.invoke(item)
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,25 @@
|
|||||||
package com.dzeio.openhealth.adapters
|
package com.dzeio.openhealth.adapters
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import com.dzeio.openhealth.core.BaseAdapter
|
import com.dzeio.openhealth.core.BaseAdapter
|
||||||
import com.dzeio.openhealth.core.BaseViewHolder
|
import com.dzeio.openhealth.core.BaseViewHolder
|
||||||
import com.dzeio.openhealth.data.weight.Weight
|
import com.dzeio.openhealth.data.weight.Weight
|
||||||
import com.dzeio.openhealth.databinding.LayoutItemWeightBinding
|
import com.dzeio.openhealth.databinding.LayoutItemListBinding
|
||||||
|
|
||||||
class WeightAdapter() : BaseAdapter<Weight, LayoutItemWeightBinding>() {
|
class WeightAdapter() : BaseAdapter<Weight, LayoutItemListBinding>() {
|
||||||
|
|
||||||
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> LayoutItemWeightBinding
|
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> LayoutItemListBinding
|
||||||
get() = LayoutItemWeightBinding::inflate
|
get() = LayoutItemListBinding::inflate
|
||||||
|
|
||||||
var onItemClick: ((weight: Weight) -> Unit)? = null
|
var onItemClick: ((weight: Weight) -> Unit)? = null
|
||||||
|
|
||||||
override fun onBindData(
|
override fun onBindData(
|
||||||
holder: BaseViewHolder<LayoutItemWeightBinding>,
|
holder: BaseViewHolder<LayoutItemListBinding>,
|
||||||
item: Weight,
|
item: Weight,
|
||||||
position: Int
|
position: Int
|
||||||
) {
|
) {
|
||||||
holder.binding.weight.text = "${item.weight}kg"
|
holder.binding.value.text = "${item.weight}kg"
|
||||||
holder.binding.datetime.text = item.formatTimestamp()
|
holder.binding.datetime.text = item.formatTimestamp()
|
||||||
holder.binding.edit.setOnClickListener {
|
holder.binding.edit.setOnClickListener {
|
||||||
onItemClick?.invoke(item)
|
onItemClick?.invoke(item)
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
package com.dzeio.openhealth.data.water
|
package com.dzeio.openhealth.data.water
|
||||||
|
|
||||||
import androidx.room.*
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Query
|
||||||
import com.dzeio.openhealth.core.BaseDao
|
import com.dzeio.openhealth.core.BaseDao
|
||||||
import dagger.Provides
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface WaterDao : BaseDao<Water> {
|
interface WaterDao : BaseDao<Water> {
|
||||||
|
|
||||||
@Query("SELECT * FROM Water ORDER BY timestamp")
|
@Query("SELECT * FROM Water ORDER BY timestamp DESC")
|
||||||
fun getAll(): Flow<List<Water>>
|
fun getAll(): Flow<List<Water>>
|
||||||
|
|
||||||
@Query("SELECT * FROM Water where id = :weightId")
|
@Query("SELECT * FROM Water where id = :weightId")
|
||||||
|
@ -7,8 +7,9 @@ import java.sql.Date
|
|||||||
import java.text.DateFormat.getDateInstance
|
import java.text.DateFormat.getDateInstance
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
data class Weight (
|
data class Weight(
|
||||||
@PrimaryKey(autoGenerate = true) var id: Long = 0,
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
var id: Long = 0,
|
||||||
var weight: Float = 0f,
|
var weight: Float = 0f,
|
||||||
@ColumnInfo(index = true)
|
@ColumnInfo(index = true)
|
||||||
var timestamp: Long = System.currentTimeMillis(),
|
var timestamp: Long = System.currentTimeMillis(),
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
package com.dzeio.openhealth.extensions
|
|
||||||
|
|
||||||
enum class DataType {
|
|
||||||
WEIGHT
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* STEP_COUNT_CUMULATIVE
|
|
||||||
* ACTIVITY_SEGMENT
|
|
||||||
* SLEEP_SEGMENT
|
|
||||||
* CALORIES_EXPENDED
|
|
||||||
* BASAL_METABOLIC_RATE
|
|
||||||
* POWER_SAMPLE
|
|
||||||
* HEART_RATE_BPM
|
|
||||||
* LOCATION_SAMPLE
|
|
||||||
*/
|
|
@ -2,18 +2,103 @@ package com.dzeio.openhealth.extensions
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
import com.dzeio.openhealth.data.weight.Weight
|
import com.dzeio.openhealth.data.weight.Weight
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension Schema
|
||||||
|
*
|
||||||
|
* Version: 1.0.0
|
||||||
|
*/
|
||||||
abstract class Extension {
|
abstract class Extension {
|
||||||
|
|
||||||
enum class Data {
|
data class ImportState<T>(
|
||||||
WEIGHT,
|
val state: States = States.WIP,
|
||||||
STEPS
|
val list: List<T> = ArrayList()
|
||||||
|
)
|
||||||
|
|
||||||
|
enum class States {
|
||||||
|
WIP,
|
||||||
|
DONE,
|
||||||
|
CANCELLED
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract val sourceID: String
|
enum class Data {
|
||||||
|
/**
|
||||||
|
* Special case to handle basic errors from other activities
|
||||||
|
*/
|
||||||
|
NOTHING,
|
||||||
|
WEIGHT,
|
||||||
|
STEPS
|
||||||
|
|
||||||
open fun init(activity: Activity): Array<Data> = arrayOf()
|
/**
|
||||||
|
* STEP_COUNT_CUMULATIVE
|
||||||
|
* ACTIVITY_SEGMENT
|
||||||
|
* SLEEP_SEGMENT
|
||||||
|
* CALORIES_EXPENDED
|
||||||
|
* BASAL_METABOLIC_RATE
|
||||||
|
* POWER_SAMPLE
|
||||||
|
* HEART_RATE_BPM
|
||||||
|
* LOCATION_SAMPLE
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the Source ID
|
||||||
|
*
|
||||||
|
* DO NOT CHANGE IT AFTER THE EXTENSION IS IN PRODUCTION
|
||||||
|
*/
|
||||||
|
abstract val id: String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Extension Display Name
|
||||||
|
*/
|
||||||
|
abstract val name: String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize hte Extension
|
||||||
|
*
|
||||||
|
* It is run Before any functions is launched and after events handlers are set
|
||||||
|
*/
|
||||||
|
abstract fun init(activity: Activity): Array<Data>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A status shown on the extension list page
|
||||||
|
*/
|
||||||
|
open fun getStatus(): String {
|
||||||
|
return "No Status set..."
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function that will check
|
||||||
|
*/
|
||||||
|
abstract fun isAvailable(): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* idk
|
||||||
|
*/
|
||||||
|
abstract fun isConnected(): Boolean
|
||||||
|
|
||||||
|
open fun connect(): LiveData<States> {
|
||||||
|
return MutableLiveData(States.DONE)
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun importWeight(): LiveData<ImportState<Weight>> {
|
||||||
|
return MutableLiveData(ImportState(States.DONE))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* function run when outgoing sync is enabled and new value is added
|
||||||
|
* or manual export is launched
|
||||||
|
*/
|
||||||
|
open fun exportWeight(weight: Weight): LiveData<States> {
|
||||||
|
return MutableLiveData(States.DONE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activity Events
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as Activity/Fragment onRequestPermissionResult
|
* Same as Activity/Fragment onRequestPermissionResult
|
||||||
@ -26,8 +111,4 @@ abstract class Extension {
|
|||||||
* Same as Activity/Fragment onActivityResult
|
* Same as Activity/Fragment onActivityResult
|
||||||
*/
|
*/
|
||||||
open fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {}
|
open fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {}
|
||||||
|
|
||||||
open fun <T> import(data: Data, cb: (item: T, end: Boolean) -> Unit) {}
|
|
||||||
|
|
||||||
open fun importWeight(callback: (weight: Weight, end: Boolean) -> Unit) {}
|
|
||||||
}
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.dzeio.openhealth.extensions
|
||||||
|
|
||||||
|
class ExtensionFactory {
|
||||||
|
companion object {
|
||||||
|
fun getExtension(extension: String): Extension? {
|
||||||
|
return when (extension) {
|
||||||
|
"GoogleFit" -> {
|
||||||
|
GoogleFit()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,69 +7,114 @@ import android.content.pm.PackageManager
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
import com.dzeio.openhealth.data.weight.Weight
|
import com.dzeio.openhealth.data.weight.Weight
|
||||||
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
||||||
import com.google.android.gms.fitness.Fitness
|
import com.google.android.gms.fitness.Fitness
|
||||||
import com.google.android.gms.fitness.FitnessOptions
|
import com.google.android.gms.fitness.FitnessOptions
|
||||||
import com.google.android.gms.fitness.data.DataPoint
|
|
||||||
import com.google.android.gms.fitness.data.DataType
|
import com.google.android.gms.fitness.data.DataType
|
||||||
import com.google.android.gms.fitness.request.DataReadRequest
|
import com.google.android.gms.fitness.request.DataReadRequest
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class GoogleFit(
|
class GoogleFit() : Extension() {
|
||||||
private val activity: Activity,
|
|
||||||
) : Extension() {
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "GoogleFitConnector"
|
const val TAG = "GoogleFitConnector"
|
||||||
}
|
}
|
||||||
|
|
||||||
override val sourceID: String = "GoogleFit"
|
private lateinit var activity: Activity
|
||||||
|
|
||||||
|
override val id = "GoogleFit"
|
||||||
|
override val name = "Google Fit"
|
||||||
|
|
||||||
|
override fun init(activity: Activity): Array<Data> {
|
||||||
|
this.activity = activity
|
||||||
|
return arrayOf(
|
||||||
|
Data.WEIGHT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getStatus(): String {
|
||||||
|
return if (isConnected()) "Connected" else "Not Connected"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isAvailable(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isConnected(): Boolean =
|
||||||
|
GoogleSignIn.hasPermissions(getGoogleAccount(), fitnessOptions)
|
||||||
|
|
||||||
private val fitnessOptions = FitnessOptions.builder()
|
private val fitnessOptions = FitnessOptions.builder()
|
||||||
.addDataType(DataType.TYPE_WEIGHT)
|
.addDataType(DataType.TYPE_WEIGHT)
|
||||||
.addDataType(DataType.TYPE_STEP_COUNT_CUMULATIVE)
|
// .addDataType(DataType.TYPE_STEP_COUNT_CUMULATIVE)
|
||||||
|
// .addDataType(DataType.TYPE_CALORIES_EXPENDED)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
private fun checkPermissionsAndRun(data: Data) {
|
// private fun checkPermissionsAndRun(data: Data) {
|
||||||
if (permissionApproved()) {
|
// if (permissionApproved()) {
|
||||||
signIn(data)
|
// signIn(data)
|
||||||
} else {
|
// } else {
|
||||||
Log.d(TAG, "Asking for permission")
|
// Log.d(TAG, "Asking for permission")
|
||||||
// Ask for permission
|
// // Ask for permission
|
||||||
ActivityCompat.requestPermissions(
|
// ActivityCompat.requestPermissions(
|
||||||
activity,
|
// activity,
|
||||||
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
|
// arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
|
||||||
data.ordinal
|
// data.ordinal
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
private fun permissionApproved(): Boolean {
|
private fun permissionApproved(): Boolean =
|
||||||
val approved = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission(
|
PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission(
|
||||||
activity,
|
activity,
|
||||||
Manifest.permission.ACCESS_FINE_LOCATION)
|
Manifest.permission.ACCESS_FINE_LOCATION
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
return approved
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun signIn(data: Data) {
|
private val connectLiveData: MutableLiveData<States> = MutableLiveData(States.WIP)
|
||||||
if (oAuthPermissionsApproved()) {
|
|
||||||
startImport(data)
|
override fun connect(): LiveData<States> {
|
||||||
|
|
||||||
|
if (!permissionApproved()) {
|
||||||
|
ActivityCompat.requestPermissions(
|
||||||
|
activity,
|
||||||
|
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
|
||||||
|
87531
|
||||||
|
)
|
||||||
|
return connectLiveData
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isConnected()) {
|
||||||
|
connectLiveData.value = States.DONE
|
||||||
} else {
|
} else {
|
||||||
Log.d("GoogleFitImporter", "Signing In")
|
Log.d("GoogleFitImporter", "Signing In")
|
||||||
GoogleSignIn.requestPermissions(
|
GoogleSignIn.requestPermissions(
|
||||||
activity,
|
activity,
|
||||||
data.ordinal,
|
124887,
|
||||||
getGoogleAccount(), fitnessOptions)
|
getGoogleAccount(), fitnessOptions
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
return connectLiveData
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun oAuthPermissionsApproved() = GoogleSignIn.hasPermissions(getGoogleAccount(), fitnessOptions)
|
// private fun signIn(data: Data) {
|
||||||
|
// if (isConnected()) {
|
||||||
|
// startImport(data)
|
||||||
|
// } else {
|
||||||
|
// Log.d("GoogleFitImporter", "Signing In")
|
||||||
|
// GoogleSignIn.requestPermissions(
|
||||||
|
// activity,
|
||||||
|
// data.ordinal,
|
||||||
|
// getGoogleAccount(), fitnessOptions
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
private fun getGoogleAccount() = GoogleSignIn.getAccountForExtension(activity, fitnessOptions)
|
private fun getGoogleAccount() = GoogleSignIn.getAccountForExtension(activity, fitnessOptions)
|
||||||
|
|
||||||
@ -102,85 +147,116 @@ class GoogleFit(
|
|||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
runRequest(DataReadRequest.Builder()
|
runRequest(
|
||||||
.read(type)
|
DataReadRequest.Builder()
|
||||||
.setTimeRange(timeRange[0], timeRange[1], timeUnit)
|
.read(type)
|
||||||
.build(), data)
|
.setTimeRange(timeRange[0], timeRange[1], timeUnit)
|
||||||
|
.build(), data
|
||||||
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun runRequest(request: DataReadRequest, data: Data) {
|
private fun runRequest(request: DataReadRequest, data: Data) {
|
||||||
Fitness.getHistoryClient(activity, GoogleSignIn.getAccountForExtension(activity, fitnessOptions))
|
Fitness.getHistoryClient(
|
||||||
|
activity,
|
||||||
|
GoogleSignIn.getAccountForExtension(activity, fitnessOptions)
|
||||||
|
)
|
||||||
.readData(request)
|
.readData(request)
|
||||||
.addOnSuccessListener { response ->
|
.addOnSuccessListener { response ->
|
||||||
Log.d(TAG, "Received response! ${response.dataSets.size} ${response.buckets.size} ${response.status}")
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
"Received response! ${response.dataSets.size} ${response.buckets.size} ${response.status}"
|
||||||
|
)
|
||||||
for (dataSet in response.dataSets) {
|
for (dataSet in response.dataSets) {
|
||||||
Log.i(TAG, "Data returned for Data type: ${dataSet.dataType.name} ${dataSet.dataPoints.size}")
|
Log.i(
|
||||||
dataSet.dataPoints.forEachIndexed { index, dp ->
|
TAG,
|
||||||
val isLast = (index + 1) == dataSet.dataPoints.size
|
"Data returned for Data type: ${dataSet.dataType.name} ${dataSet.dataPoints.size} ${dataSet.dataSource.toDebugString()}"
|
||||||
|
)
|
||||||
|
dataSet.dataPoints.forEach { dp ->
|
||||||
|
|
||||||
// Global
|
// Global
|
||||||
Log.i(TAG,"Importing Data point:")
|
Log.i(TAG, "Importing Data point:")
|
||||||
Log.i(TAG,"\tType: ${dp.dataType.name}")
|
Log.i(TAG, "\tType: ${dp.dataType.name}")
|
||||||
Log.i(TAG,"\tStart: ${dp.getStartTimeString()}")
|
Log.i(
|
||||||
Log.i(TAG,"\tEnd: ${dp.getEndTimeString()}")
|
TAG,
|
||||||
|
"\tStart: ${Date(dp.getStartTime(TimeUnit.SECONDS) * 1000L).toLocaleString()}"
|
||||||
|
)
|
||||||
|
Log.i(
|
||||||
|
TAG,
|
||||||
|
"\tEnd: ${Date(dp.getEndTime(TimeUnit.SECONDS) * 1000L).toLocaleString()}"
|
||||||
|
)
|
||||||
|
|
||||||
// Field Specifics
|
// Field Specifics
|
||||||
for (field in dp.dataType.fields) {
|
for (field in dp.dataType.fields) {
|
||||||
Log.i(TAG,"\tField: ${field.name} Value: ${dp.getValue(field)}")
|
Log.i(TAG, "\tField: ${field.name} Value: ${dp.getValue(field)}")
|
||||||
when (data) {
|
when (data) {
|
||||||
Data.WEIGHT -> {
|
Data.WEIGHT -> {
|
||||||
val weight = Weight()
|
val weight = Weight()
|
||||||
weight.timestamp = dp.getStartTime(TimeUnit.MILLISECONDS)
|
weight.timestamp = dp.getStartTime(TimeUnit.MILLISECONDS)
|
||||||
weight.weight = dp.getValue(field).asFloat()
|
weight.weight = dp.getValue(field).asFloat()
|
||||||
weightCallback(weight, isLast)
|
val list = weightLiveData.value?.list?.toMutableList()
|
||||||
|
?: ArrayList()
|
||||||
|
list.add(weight)
|
||||||
|
weightLiveData.value =
|
||||||
|
|
||||||
|
ImportState(States.WIP, list)
|
||||||
}
|
}
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
when (data) {
|
||||||
|
Data.WEIGHT -> {
|
||||||
|
weightLiveData.value =
|
||||||
|
ImportState(
|
||||||
|
States.DONE, weightLiveData.value?.list
|
||||||
|
?: ArrayList()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.addOnFailureListener { e ->
|
.addOnFailureListener { e ->
|
||||||
Log.e(TAG,"There was an error reading data from Google Fit", e)
|
Log.e(TAG, "There was an error reading data from Google Fit", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun DataPoint.getStartTimeString(): String = Date(this.getStartTime(TimeUnit.SECONDS) * 1000L).toLocaleString()
|
/**
|
||||||
|
* Currently not usable
|
||||||
private fun DataPoint.getEndTimeString(): String = Date(this.getEndTime(TimeUnit.SECONDS) * 1000L).toLocaleString()
|
*/
|
||||||
|
|
||||||
override fun onRequestPermissionResult(
|
override fun onRequestPermissionResult(
|
||||||
requestCode: Int,
|
requestCode: Int,
|
||||||
permission: Array<String>,
|
permission: Array<String>,
|
||||||
grantResult: IntArray
|
grantResult: IntArray
|
||||||
) {
|
) {
|
||||||
signIn(Data.values()[requestCode])
|
connect()
|
||||||
|
// signIn(Data.values()[requestCode])
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
signIn(Data.values()[requestCode])
|
if (requestCode == 0) {
|
||||||
}
|
return
|
||||||
|
|
||||||
private lateinit var weightCallback: (weight: Weight, end: Boolean) -> Unit
|
|
||||||
|
|
||||||
override fun importWeight(callback: (weight: Weight, end: Boolean) -> Unit) {
|
|
||||||
this.weightCallback = callback
|
|
||||||
checkPermissionsAndRun(Data.WEIGHT)
|
|
||||||
}
|
|
||||||
|
|
||||||
private lateinit var callback : (item: Any, end: Boolean) -> Unit
|
|
||||||
|
|
||||||
override fun <T> import(data: Data, cb: (item: T, end: Boolean) -> Unit) {
|
|
||||||
callback = cb as (item: Any, end: Boolean) -> Unit
|
|
||||||
when (data) {
|
|
||||||
Data.WEIGHT -> {
|
|
||||||
checkPermissionsAndRun(data)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
Log.d(TAG, "PRRRRRRRRRRRRR")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
connectLiveData.value = States.DONE
|
||||||
|
//signIn(Data.values()[requestCode])
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var weightLiveData: MutableLiveData<ImportState<Weight>>
|
||||||
|
|
||||||
|
override fun importWeight(): LiveData<ImportState<Weight>> {
|
||||||
|
|
||||||
|
weightLiveData = MutableLiveData(
|
||||||
|
ImportState(
|
||||||
|
States.WIP
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
startImport(Data.WEIGHT)
|
||||||
|
|
||||||
|
// checkPermissionsAndRun(Data.WEIGHT)
|
||||||
|
|
||||||
|
return weightLiveData
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,374 +0,0 @@
|
|||||||
package com.dzeio.openhealth.extensions
|
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.os.Build
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import androidx.core.app.ActivityCompat
|
|
||||||
import com.dzeio.openhealth.data.weight.Weight
|
|
||||||
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
|
||||||
import com.google.android.gms.fitness.Fitness
|
|
||||||
import com.google.android.gms.fitness.FitnessOptions
|
|
||||||
import com.google.android.gms.fitness.data.DataPoint
|
|
||||||
import com.google.android.gms.fitness.data.DataSet
|
|
||||||
import com.google.android.gms.fitness.data.DataSource
|
|
||||||
import com.google.android.gms.fitness.data.DataType
|
|
||||||
import com.google.android.gms.fitness.request.DataReadRequest
|
|
||||||
import com.google.android.gms.fitness.request.DataSourcesRequest
|
|
||||||
import com.google.android.gms.fitness.request.OnDataPointListener
|
|
||||||
import com.google.android.gms.fitness.request.SensorRequest
|
|
||||||
import java.text.DateFormat
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.*
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
enum class ActionRequestCode {
|
|
||||||
FIND_DATA_SOURCES
|
|
||||||
}
|
|
||||||
|
|
||||||
class GoogleFit(
|
|
||||||
private val activity: Activity,
|
|
||||||
) {
|
|
||||||
companion object {
|
|
||||||
const val TAG = "GoogleFitConnector"
|
|
||||||
}
|
|
||||||
// private val fitnessOptions = FitnessOptions.builder()
|
|
||||||
// .addDataType(DataType.TYPE_ACTIVITY_SEGMENT, FitnessOptions.ACCESS_READ)
|
|
||||||
// .addDataType(DataType.TYPE_ACTIVITY_SEGMENT, FitnessOptions.ACCESS_WRITE)
|
|
||||||
//
|
|
||||||
// .addDataType(DataType.TYPE_HEART_RATE_BPM, FitnessOptions.ACCESS_READ)
|
|
||||||
// .addDataType(DataType.TYPE_HEART_RATE_BPM, FitnessOptions.ACCESS_WRITE)
|
|
||||||
//
|
|
||||||
// .addDataType(DataType.TYPE_HEIGHT, FitnessOptions.ACCESS_READ)
|
|
||||||
// .addDataType(DataType.TYPE_HEIGHT, FitnessOptions.ACCESS_WRITE)
|
|
||||||
//
|
|
||||||
// .addDataType(DataType.TYPE_WEIGHT, FitnessOptions.ACCESS_READ)
|
|
||||||
// .addDataType(DataType.TYPE_WEIGHT, FitnessOptions.ACCESS_WRITE)
|
|
||||||
// .build()
|
|
||||||
|
|
||||||
private val fitnessOptions = FitnessOptions.builder()
|
|
||||||
// .addDataType(DataType.TYPE_STEP_COUNT_CUMULATIVE)
|
|
||||||
// .addDataType(DataType.TYPE_ACTIVITY_SEGMENT)
|
|
||||||
// .addDataType(DataType.TYPE_SLEEP_SEGMENT)
|
|
||||||
// .addDataType(DataType.TYPE_CALORIES_EXPENDED)
|
|
||||||
// .addDataType(DataType.TYPE_BASAL_METABOLIC_RATE)
|
|
||||||
// .addDataType(DataType.TYPE_POWER_SAMPLE)
|
|
||||||
// .addDataType(DataType.TYPE_HEART_RATE_BPM)
|
|
||||||
// .addDataType(DataType.TYPE_LOCATION_SAMPLE)
|
|
||||||
.addDataType(DataType.TYPE_WEIGHT)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
private val runningQOrLater =
|
|
||||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
|
||||||
|
|
||||||
// [START dataPointListener_variable_reference]
|
|
||||||
// Need to hold a reference to this listener, as it's passed into the "unregister"
|
|
||||||
// method in order to stop all sensors from sending data to this listener.
|
|
||||||
private var dataPointListener: OnDataPointListener? = null
|
|
||||||
|
|
||||||
fun import() {
|
|
||||||
checkPermissionsAndRun(ActionRequestCode.FIND_DATA_SOURCES)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkPermissionsAndRun(actionRequestCode: ActionRequestCode) {
|
|
||||||
if (permissionApproved()) {
|
|
||||||
signIn(actionRequestCode)
|
|
||||||
} else {
|
|
||||||
requestRuntimePermissions(actionRequestCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun requestRuntimePermissions(requestCode: ActionRequestCode) {
|
|
||||||
val shouldProvideRationale =
|
|
||||||
ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.ACCESS_FINE_LOCATION)
|
|
||||||
|
|
||||||
// Provide an additional rationale to the user. This would happen if the user denied the
|
|
||||||
// request previously, but didn't check the "Don't ask again" checkbox.
|
|
||||||
requestCode.let {
|
|
||||||
if (shouldProvideRationale) {
|
|
||||||
Log.i(TAG, "Displaying permission rationale to provide additional context.")
|
|
||||||
// ProgressDialog.show(activity, "Waiting for authorization...", "")
|
|
||||||
ActivityCompat.requestPermissions(activity,
|
|
||||||
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
|
|
||||||
requestCode.ordinal)
|
|
||||||
} else {
|
|
||||||
Log.i(TAG, "Requesting permission")
|
|
||||||
// Request permission. It's possible this can be auto answered if device policy
|
|
||||||
// sets the permission in a given state or the user denied the permission
|
|
||||||
// previously and checked "Never ask again".
|
|
||||||
ActivityCompat.requestPermissions(activity,
|
|
||||||
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
|
|
||||||
requestCode.ordinal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun permissionApproved(): Boolean {
|
|
||||||
val approved = if (runningQOrLater) {
|
|
||||||
PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission(
|
|
||||||
activity,
|
|
||||||
Manifest.permission.ACCESS_FINE_LOCATION)
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
return approved
|
|
||||||
}
|
|
||||||
|
|
||||||
fun signIn(requestCode: ActionRequestCode) {
|
|
||||||
if (oAuthPermissionsApproved()) {
|
|
||||||
performActionForRequestCode(requestCode)
|
|
||||||
} else {
|
|
||||||
requestCode.let {
|
|
||||||
GoogleSignIn.requestPermissions(
|
|
||||||
activity,
|
|
||||||
it.ordinal,
|
|
||||||
getGoogleAccount(), fitnessOptions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun oAuthPermissionsApproved() = GoogleSignIn.hasPermissions(getGoogleAccount(), fitnessOptions)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a Google account for use in creating the Fitness client. This is achieved by either
|
|
||||||
* using the last signed-in account, or if necessary, prompting the user to sign in.
|
|
||||||
* `getAccountForExtension` is recommended over `getLastSignedInAccount` as the latter can
|
|
||||||
* return `null` if there has been no sign in before.
|
|
||||||
*/
|
|
||||||
private fun getGoogleAccount() = GoogleSignIn.getAccountForExtension(activity, fitnessOptions)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs the desired method, based on the specified request code. The request code is typically
|
|
||||||
* passed to the Fit sign-in flow, and returned with the success callback. This allows the
|
|
||||||
* caller to specify which method, post-sign-in, should be called.
|
|
||||||
*
|
|
||||||
* @param requestCode The code corresponding to the action to perform.
|
|
||||||
*/
|
|
||||||
fun performActionForRequestCode(requestCode: ActionRequestCode) = when (requestCode) {
|
|
||||||
ActionRequestCode.FIND_DATA_SOURCES -> findFitnessDataSources()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Finds available data sources and attempts to register on a specific [DataType]. */
|
|
||||||
private fun findFitnessDataSources() { // [START find_data_sources]
|
|
||||||
// Note: Fitness.SensorsApi.findDataSources() requires the ACCESS_FINE_LOCATION permission.
|
|
||||||
Fitness.getSensorsClient(activity, getGoogleAccount())
|
|
||||||
.findDataSources(
|
|
||||||
DataSourcesRequest.Builder()
|
|
||||||
.setDataTypes(DataType.TYPE_LOCATION_SAMPLE)
|
|
||||||
.setDataSourceTypes(DataSource.TYPE_RAW)
|
|
||||||
.build())
|
|
||||||
.addOnSuccessListener { dataSources ->
|
|
||||||
for (dataSource in dataSources) {
|
|
||||||
Log.i(TAG, "Data source found: $dataSource")
|
|
||||||
Log.i(TAG, "Data Source type: " + dataSource.dataType.name)
|
|
||||||
// Let's register a listener to receive Activity data!
|
|
||||||
if (dataSource.dataType == DataType.TYPE_LOCATION_SAMPLE && dataPointListener == null) {
|
|
||||||
Log.i(TAG, "Data source for LOCATION_SAMPLE found! Registering.")
|
|
||||||
registerFitnessDataListener(dataSource, DataType.TYPE_LOCATION_SAMPLE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.addOnFailureListener { e -> Log.e(TAG, "failed", e) }
|
|
||||||
// [END find_data_sources]
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a listener with the Sensors API for the provided [DataSource] and [DataType] combo.
|
|
||||||
*/
|
|
||||||
private fun registerFitnessDataListener(dataSource: DataSource, dataType: DataType) {
|
|
||||||
// [START register_data_listener]
|
|
||||||
dataPointListener = OnDataPointListener { dataPoint ->
|
|
||||||
for (field in dataPoint.dataType.fields) {
|
|
||||||
val value = dataPoint.getValue(field)
|
|
||||||
Log.i(TAG, "Detected DataPoint field: ${field.name}")
|
|
||||||
Log.i(TAG, "Detected DataPoint value: $value")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Fitness.getSensorsClient(activity, getGoogleAccount())
|
|
||||||
.add(
|
|
||||||
SensorRequest.Builder()
|
|
||||||
.setDataSource(dataSource) // Optional but recommended for custom data sets.
|
|
||||||
.setDataType(dataType) // Can't be omitted.
|
|
||||||
.setSamplingRate(10, TimeUnit.SECONDS)
|
|
||||||
.build(),
|
|
||||||
dataPointListener!!
|
|
||||||
)
|
|
||||||
.addOnCompleteListener { task ->
|
|
||||||
if (task.isSuccessful) {
|
|
||||||
Log.i(TAG, "Listener registered!")
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "Listener not registered.", task.exception)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// [END register_data_listener]
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
|
||||||
fun getHistory() {
|
|
||||||
|
|
||||||
val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
|
|
||||||
val now = Date()
|
|
||||||
calendar.time = now
|
|
||||||
val endTime = calendar.timeInMillis
|
|
||||||
calendar.set(Calendar.YEAR, 2013) // Set year to 2013 to be sure to get data from when Google Fit Started to today
|
|
||||||
val startTime = calendar.timeInMillis
|
|
||||||
val readRequest = DataReadRequest.Builder()
|
|
||||||
.aggregate(DataType.AGGREGATE_CALORIES_EXPENDED)
|
|
||||||
.bucketByActivityType(1, TimeUnit.SECONDS)
|
|
||||||
.setTimeRange(startTime, endTime, TimeUnit.SECONDS)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
Fitness.getHistoryClient(activity, GoogleSignIn.getAccountForExtension(activity, fitnessOptions))
|
|
||||||
.readData(readRequest)
|
|
||||||
.addOnSuccessListener { response ->
|
|
||||||
// The aggregate query puts datasets into buckets, so flatten into a
|
|
||||||
// single list of datasets
|
|
||||||
for (dataSet in response.buckets.flatMap { it.dataSets }) {
|
|
||||||
dumpDataSet(dataSet)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.addOnFailureListener { e ->
|
|
||||||
Log.w(TAG,"There was an error reading data from Google Fit", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fun importWeight(callback : () -> Unit) {
|
|
||||||
|
|
||||||
val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
|
|
||||||
val now = Date()
|
|
||||||
calendar.time = now
|
|
||||||
val endTime = calendar.timeInMillis
|
|
||||||
calendar.set(Calendar.YEAR, 2013) // Set year to 2013 to be sure to get data from when Google Fit Started to today
|
|
||||||
val startTime = calendar.timeInMillis
|
|
||||||
|
|
||||||
val dateFormat = DateFormat.getDateInstance()
|
|
||||||
Log.i(TAG, "Range Start: ${dateFormat.format(startTime)}")
|
|
||||||
Log.i(TAG, "Range End: ${dateFormat.format(endTime)}")
|
|
||||||
|
|
||||||
|
|
||||||
runRequest(DataReadRequest.Builder()
|
|
||||||
// The data request can specify multiple data types to return, effectively
|
|
||||||
// combining multiple data queries into one call.
|
|
||||||
// In this example, it's very unlikely that the request is for several hundred
|
|
||||||
// datapoints each consisting of a few steps and a timestamp. The more likely
|
|
||||||
// scenario is wanting to see how many steps were walked per day, for 7 days.
|
|
||||||
// .aggregate()
|
|
||||||
.read(DataType.TYPE_WEIGHT)
|
|
||||||
|
|
||||||
// Analogous to a "Group By" in SQL, defines how data should be aggregated.
|
|
||||||
// bucketByTime allows for a time span, whereas bucketBySession would allow
|
|
||||||
// bucketing by "sessions", which would need to be defined in code.
|
|
||||||
// .bucketByTime(1, TimeUnit.MINUTES)
|
|
||||||
// .bucketByActivityType(1, TimeUnit.SECONDS)
|
|
||||||
// .bucketBySession()
|
|
||||||
.setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
|
|
||||||
.build(), callback)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun runRequest(request: DataReadRequest, callback: () -> Unit) {
|
|
||||||
Fitness.getHistoryClient(activity, GoogleSignIn.getAccountForExtension(activity, fitnessOptions))
|
|
||||||
.readData(request)
|
|
||||||
.addOnSuccessListener { response ->
|
|
||||||
// The aggregate query puts datasets into buckets, so flatten into a
|
|
||||||
// single list of datasets
|
|
||||||
Log.d(TAG, "Received response! ${response.dataSets.size} ${response.buckets.size}")
|
|
||||||
for (dataSet in response.dataSets) {
|
|
||||||
dumpDataSet(dataSet)
|
|
||||||
}
|
|
||||||
for (dataSet in response.buckets.flatMap { it.dataSets }) {
|
|
||||||
dumpDataSet(dataSet)
|
|
||||||
}
|
|
||||||
callback.invoke()
|
|
||||||
}
|
|
||||||
.addOnFailureListener { e ->
|
|
||||||
Log.w(TAG,"There was an error reading data from Google Fit", e)
|
|
||||||
callback.invoke()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun dumpDataSet(dataSet: DataSet) {
|
|
||||||
Log.i(TAG, "Data returned for Data type: ${dataSet.dataType.name} ${dataSet.dataPoints.size}")
|
|
||||||
for (dp in dataSet.dataPoints) {
|
|
||||||
val weight = Weight()
|
|
||||||
Log.i(TAG,"Data point:")
|
|
||||||
Log.i(TAG,"\tType: ${dp.dataType.name}")
|
|
||||||
Log.i(TAG,"\tStart: ${dp.getStartTimeString()}")
|
|
||||||
Log.i(TAG,"\tEnd: ${dp.getEndTimeString()}")
|
|
||||||
weight.timestamp = dp.getStartTime(TimeUnit.SECONDS)
|
|
||||||
weight.source = "GoogleFit"
|
|
||||||
for (field in dp.dataType.fields) {
|
|
||||||
weight.weight = dp.getValue(field).asFloat()
|
|
||||||
Log.i(TAG,"\tField: ${field.name.toString()} Value: ${dp.getValue(field)}")
|
|
||||||
}
|
|
||||||
// AppDatabase.getInstance(activity).weightDao().insert(weight)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun DataPoint.getStartTimeString(): String =
|
|
||||||
SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS", Locale.FRANCE)
|
|
||||||
.format(Date(this.getStartTime(TimeUnit.SECONDS) * 1000L))
|
|
||||||
|
|
||||||
fun DataPoint.getEndTimeString(): String =
|
|
||||||
SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS", Locale.FRANCE)
|
|
||||||
.format(Date(this.getEndTime(TimeUnit.SECONDS) * 1000L))
|
|
||||||
|
|
||||||
|
|
||||||
/** Unregisters the listener with the Sensors API. */
|
|
||||||
private fun unregisterFitnessDataListener() {
|
|
||||||
if (dataPointListener == null) {
|
|
||||||
// This code only activates one listener at a time. If there's no listener, there's
|
|
||||||
// nothing to unregister.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// [START unregister_data_listener]
|
|
||||||
// Waiting isn't actually necessary as the unregister call will complete regardless,
|
|
||||||
// even if called from within onStop, but a callback can still be added in order to
|
|
||||||
// inspect the results.
|
|
||||||
Fitness.getSensorsClient(activity, getGoogleAccount())
|
|
||||||
.remove(dataPointListener!!)
|
|
||||||
.addOnCompleteListener { task ->
|
|
||||||
if (task.isSuccessful && task.result!!) {
|
|
||||||
Log.i(TAG, "Listener was removed!")
|
|
||||||
} else {
|
|
||||||
Log.i(TAG, "Listener was not removed.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// [END unregister_data_listener]
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns a [DataReadRequest] for all step count changes in the past week. */
|
|
||||||
private fun queryFitnessData(): DataReadRequest {
|
|
||||||
// [START build_read_data_request]
|
|
||||||
// Setting a start and end date using a range of 1 week before this moment.
|
|
||||||
val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
|
|
||||||
val now = Date()
|
|
||||||
calendar.time = now
|
|
||||||
val endTime = calendar.timeInMillis
|
|
||||||
calendar.add(Calendar.YEAR, -1)
|
|
||||||
val startTime = calendar.timeInMillis
|
|
||||||
|
|
||||||
val dateFormat = DateFormat.getDateInstance()
|
|
||||||
Log.i(TAG, "Range Start: ${dateFormat.format(startTime)}")
|
|
||||||
Log.i(TAG, "Range End: ${dateFormat.format(endTime)}")
|
|
||||||
|
|
||||||
return DataReadRequest.Builder()
|
|
||||||
// The data request can specify multiple data types to return, effectively
|
|
||||||
// combining multiple data queries into one call.
|
|
||||||
// In this example, it's very unlikely that the request is for several hundred
|
|
||||||
// datapoints each consisting of a few steps and a timestamp. The more likely
|
|
||||||
// scenario is wanting to see how many steps were walked per day, for 7 days.
|
|
||||||
.aggregate(DataType.TYPE_STEP_COUNT_DELTA)
|
|
||||||
// Analogous to a "Group By" in SQL, defines how data should be aggregated.
|
|
||||||
// bucketByTime allows for a time span, whereas bucketBySession would allow
|
|
||||||
// bucketing by "sessions", which would need to be defined in code.
|
|
||||||
.bucketByTime(1, TimeUnit.SECONDS)
|
|
||||||
// .bucketBySession()
|
|
||||||
.setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -5,11 +5,12 @@ import android.content.Intent
|
|||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.dzeio.openhealth.extensions.Extension
|
|
||||||
import com.dzeio.openhealth.data.weight.Weight
|
import com.dzeio.openhealth.data.weight.Weight
|
||||||
import com.samsung.android.sdk.healthdata.*
|
import com.samsung.android.sdk.healthdata.HealthConnectionErrorResult
|
||||||
import com.samsung.android.sdk.healthdata.HealthConstants.StepCount
|
import com.samsung.android.sdk.healthdata.HealthConstants.StepCount
|
||||||
|
import com.samsung.android.sdk.healthdata.HealthDataStore
|
||||||
import com.samsung.android.sdk.healthdata.HealthDataStore.ConnectionListener
|
import com.samsung.android.sdk.healthdata.HealthDataStore.ConnectionListener
|
||||||
|
import com.samsung.android.sdk.healthdata.HealthPermissionManager
|
||||||
import com.samsung.android.sdk.healthdata.HealthPermissionManager.*
|
import com.samsung.android.sdk.healthdata.HealthPermissionManager.*
|
||||||
|
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ import com.samsung.android.sdk.healthdata.HealthPermissionManager.*
|
|||||||
*/
|
*/
|
||||||
class SamsungHealth(
|
class SamsungHealth(
|
||||||
private val context: Activity
|
private val context: Activity
|
||||||
) : Extension() {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "SamsungHealthConnector"
|
const val TAG = "SamsungHealthConnector"
|
||||||
@ -33,6 +34,7 @@ class SamsungHealth(
|
|||||||
requestPermission()
|
requestPermission()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onConnectionFailed(p0: HealthConnectionErrorResult?) {
|
override fun onConnectionFailed(p0: HealthConnectionErrorResult?) {
|
||||||
Log.d(TAG, "Health data service is not available.")
|
Log.d(TAG, "Health data service is not available.")
|
||||||
}
|
}
|
||||||
@ -43,7 +45,7 @@ class SamsungHealth(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val store : HealthDataStore = HealthDataStore(context, listener)
|
private val store: HealthDataStore = HealthDataStore(context, listener)
|
||||||
|
|
||||||
private fun isPermissionAcquired(): Boolean {
|
private fun isPermissionAcquired(): Boolean {
|
||||||
val permKey = PermissionKey(StepCount.HEALTH_DATA_TYPE, PermissionType.READ)
|
val permKey = PermissionKey(StepCount.HEALTH_DATA_TYPE, PermissionType.READ)
|
||||||
@ -86,23 +88,25 @@ class SamsungHealth(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val reporter = StepCountReporter(store, stepCountObserver, Handler(Looper.getMainLooper()))
|
private val reporter =
|
||||||
|
StepCountReporter(store, stepCountObserver, Handler(Looper.getMainLooper()))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connector
|
* Connector
|
||||||
*/
|
*/
|
||||||
|
|
||||||
override val sourceID: String = "SamsungHealth"
|
val sourceID: String = "SamsungHealth"
|
||||||
|
|
||||||
override fun onRequestPermissionResult(
|
fun onRequestPermissionResult(
|
||||||
requestCode: Int,
|
requestCode: Int,
|
||||||
permission: Array<String>,
|
permission: Array<String>,
|
||||||
grantResult: IntArray
|
grantResult: IntArray
|
||||||
) {}
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {}
|
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {}
|
||||||
|
|
||||||
override fun importWeight(callback: (weight: Weight, end: Boolean) -> Unit) {
|
fun importWeight(callback: (weight: Weight, end: Boolean) -> Unit) {
|
||||||
store.connectService()
|
store.connectService()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,12 +1,10 @@
|
|||||||
package com.dzeio.openhealth.interfaces
|
package com.dzeio.openhealth.interfaces
|
||||||
|
|
||||||
import android.app.NotificationManager
|
|
||||||
|
|
||||||
enum class NotificationChannels(
|
enum class NotificationChannels(
|
||||||
val id: String,
|
val id: String,
|
||||||
val channelName: String,
|
val channelName: String,
|
||||||
val importance: Int
|
val importance: Int
|
||||||
) {
|
) {
|
||||||
// 3 is IMPORTANCE_DEFAULT
|
// 3 is IMPORTANCE_DEFAULT
|
||||||
DEFAULT("default", "Default Channel", 3)
|
DEFAULT("openhealth_default", "Default Channel", 3)
|
||||||
}
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
package com.dzeio.openhealth.ui.extension
|
||||||
|
|
||||||
|
import android.app.ProgressDialog
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.navigation.fragment.navArgs
|
||||||
|
import com.dzeio.openhealth.core.BaseFragment
|
||||||
|
import com.dzeio.openhealth.databinding.FragmentExtensionBinding
|
||||||
|
import com.dzeio.openhealth.extensions.Extension
|
||||||
|
import com.dzeio.openhealth.extensions.ExtensionFactory
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import java.lang.Exception
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class ExtensionFragment :
|
||||||
|
BaseFragment<ExtensionViewModel, FragmentExtensionBinding>(ExtensionViewModel::class.java) {
|
||||||
|
|
||||||
|
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentExtensionBinding =
|
||||||
|
FragmentExtensionBinding::inflate
|
||||||
|
|
||||||
|
|
||||||
|
private val args: ExtensionFragmentArgs by navArgs()
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
val extension = ExtensionFactory.getExtension(args.extension)
|
||||||
|
?: throw Exception("No Extension found!")
|
||||||
|
|
||||||
|
extension.init(requireActivity())
|
||||||
|
|
||||||
|
binding.importButton.setOnClickListener {
|
||||||
|
val dialog = ProgressDialog(requireContext())
|
||||||
|
dialog.setTitle("Importing...")
|
||||||
|
dialog.setMessage("Imported 0 values")
|
||||||
|
dialog.show()
|
||||||
|
val data = extension.importWeight()
|
||||||
|
data.observe(viewLifecycleOwner) { state ->
|
||||||
|
Log.d("ExtensionFragment", state.state.name)
|
||||||
|
Log.d("ExtensionFragment", state.list.size.toString())
|
||||||
|
dialog.setMessage("Imported ${state.list.size} values")
|
||||||
|
if (state.state == Extension.States.DONE) {
|
||||||
|
dialog.setMessage("Finishing Import...")
|
||||||
|
lifecycleScope.launchWhenStarted {
|
||||||
|
state.list.forEach {
|
||||||
|
it.source = extension.id
|
||||||
|
viewModel.importWeight(it)
|
||||||
|
}
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.dzeio.openhealth.ui.extension
|
||||||
|
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import com.dzeio.openhealth.core.BaseViewModel
|
||||||
|
import com.dzeio.openhealth.data.weight.Weight
|
||||||
|
import com.dzeio.openhealth.data.weight.WeightRepository
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class ExtensionViewModel @Inject internal constructor(
|
||||||
|
private val weightRepository: WeightRepository
|
||||||
|
) : BaseViewModel() {
|
||||||
|
|
||||||
|
val text = MutableLiveData<String>().apply {
|
||||||
|
value = "This is slideshow Fragment"
|
||||||
|
}
|
||||||
|
val importProgress = MutableLiveData<Int>().apply {
|
||||||
|
value = 0
|
||||||
|
}
|
||||||
|
// If -1 progress is undetermined
|
||||||
|
// If 0 no progress bar
|
||||||
|
// Else progress bar
|
||||||
|
val importProgressTotal = MutableLiveData<Int>().apply {
|
||||||
|
value = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun importWeight(weight: Weight) = weightRepository.addWeight(weight)
|
||||||
|
suspend fun deleteFromSource(source: String) = weightRepository.deleteFromSource(source)
|
||||||
|
}
|
@ -10,13 +10,13 @@ import android.view.LayoutInflater
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.dzeio.openhealth.extensions.Extension
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.dzeio.openhealth.extensions.GoogleFit
|
import com.dzeio.openhealth.adapters.ExtensionAdapter
|
||||||
//import com.dzeio.openhealth.connectors.GoogleFit
|
|
||||||
import com.dzeio.openhealth.extensions.samsunghealth.SamsungHealth
|
|
||||||
import com.dzeio.openhealth.core.BaseFragment
|
import com.dzeio.openhealth.core.BaseFragment
|
||||||
import com.dzeio.openhealth.databinding.FragmentExtensionsBinding
|
import com.dzeio.openhealth.databinding.FragmentExtensionsBinding
|
||||||
|
import com.dzeio.openhealth.extensions.Extension
|
||||||
|
import com.dzeio.openhealth.extensions.GoogleFit
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@ -24,92 +24,55 @@ class ExtensionsFragment :
|
|||||||
BaseFragment<ExtensionsViewModel, FragmentExtensionsBinding>(ExtensionsViewModel::class.java) {
|
BaseFragment<ExtensionsViewModel, FragmentExtensionsBinding>(ExtensionsViewModel::class.java) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "ImportFragment"
|
const val TAG = "ExtensionsFragment"
|
||||||
}
|
}
|
||||||
|
|
||||||
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentExtensionsBinding =
|
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentExtensionsBinding =
|
||||||
FragmentExtensionsBinding::inflate
|
FragmentExtensionsBinding::inflate
|
||||||
|
|
||||||
private lateinit var progressDialog: ProgressDialog
|
private lateinit var activeExtension: Extension
|
||||||
|
|
||||||
private lateinit var fit: Extension
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
progressDialog = ProgressDialog(requireContext())
|
val recycler = binding.list
|
||||||
|
|
||||||
progressDialog.apply {
|
val manager = LinearLayoutManager(requireContext())
|
||||||
setCancelable(false)
|
recycler.layoutManager = manager
|
||||||
setTitle("Importing from source...")
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.importGoogleFit.setOnClickListener {
|
val adapter = ExtensionAdapter()
|
||||||
importFromGoogleFit()
|
adapter.onItemClick = {
|
||||||
}
|
activeExtension = it
|
||||||
binding.importSamsungHealth.setOnClickListener {
|
Log.d(it.id, it.name)
|
||||||
importFromSamsungHealth()
|
if (it.isConnected()) {
|
||||||
}
|
Log.d(it.id, "Continue!")
|
||||||
}
|
findNavController().navigate(
|
||||||
|
ExtensionsFragmentDirections.actionNavExtensionsToNavExtension(
|
||||||
private fun importFromGoogleFit() {
|
it.id
|
||||||
progressDialog.show()
|
)
|
||||||
fit = GoogleFit(requireActivity())
|
)
|
||||||
|
} else {
|
||||||
var imported = 0
|
val ls = it.connect()
|
||||||
|
ls.observe(viewLifecycleOwner) { st ->
|
||||||
lifecycleScope.launchWhenStarted {
|
Log.d("States", st.name)
|
||||||
viewModel.deleteFromSource(fit.sourceID)
|
|
||||||
}.invokeOnCompletion {
|
|
||||||
//progressDialog.show()
|
|
||||||
fit.importWeight { weight, end ->
|
|
||||||
Log.d("Importer", "Importing $weight")
|
|
||||||
weight.source = fit.sourceID
|
|
||||||
progressDialog.setTitle("Importing from source... ${++imported}")
|
|
||||||
lifecycleScope.launchWhenStarted {
|
|
||||||
viewModel.importWeight(weight)
|
|
||||||
}
|
}
|
||||||
if (end) {
|
|
||||||
Log.d("Importer", "Finished Importing")
|
|
||||||
progressDialog.dismiss()
|
|
||||||
return@importWeight
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
recycler.adapter = adapter
|
||||||
|
|
||||||
|
val list = arrayOf(
|
||||||
|
GoogleFit()
|
||||||
|
).toList()
|
||||||
|
|
||||||
}
|
list.forEach {
|
||||||
|
it.init(requireActivity())
|
||||||
private fun importFromSamsungHealth() {
|
|
||||||
progressDialog.show()
|
|
||||||
fit = SamsungHealth(requireActivity())
|
|
||||||
|
|
||||||
var imported = 0
|
|
||||||
|
|
||||||
lifecycleScope.launchWhenStarted {
|
|
||||||
viewModel.deleteFromSource(fit.sourceID)
|
|
||||||
}.invokeOnCompletion {
|
|
||||||
//progressDialog.show()
|
|
||||||
fit.importWeight { weight, end ->
|
|
||||||
Log.d("Importer", "Importing $weight")
|
|
||||||
weight.source = fit.sourceID
|
|
||||||
progressDialog.setTitle("Importing from source... ${++imported}")
|
|
||||||
lifecycleScope.launchWhenStarted {
|
|
||||||
viewModel.importWeight(weight)
|
|
||||||
}
|
|
||||||
if (end) {
|
|
||||||
Log.d("Importer", "Finished Importing")
|
|
||||||
progressDialog.dismiss()
|
|
||||||
return@importWeight
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
adapter.set(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
fit.onActivityResult(requestCode, resultCode, data)
|
activeExtension.onActivityResult(requestCode, resultCode, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.O)
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
@ -126,7 +89,7 @@ class ExtensionsFragment :
|
|||||||
|
|
||||||
grantResults[0] == PackageManager.PERMISSION_GRANTED -> {
|
grantResults[0] == PackageManager.PERMISSION_GRANTED -> {
|
||||||
Log.d(TAG, "Granted")
|
Log.d(TAG, "Granted")
|
||||||
fit.onRequestPermissionResult(requestCode, permissions, grantResults)
|
activeExtension.onRequestPermissionResult(requestCode, permissions, grantResults)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// Permission denied.
|
// Permission denied.
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package com.dzeio.openhealth.ui.home
|
package com.dzeio.openhealth.ui.home
|
||||||
|
|
||||||
import android.animation.ValueAnimator
|
import android.animation.ValueAnimator
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.Bitmap
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.graphics.Rect
|
import android.graphics.RectF
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -13,28 +13,21 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.dzeio.openhealth.Application
|
import com.dzeio.openhealth.Application
|
||||||
import com.dzeio.openhealth.R
|
|
||||||
import com.dzeio.openhealth.core.BaseFragment
|
import com.dzeio.openhealth.core.BaseFragment
|
||||||
import com.dzeio.openhealth.data.water.Water
|
import com.dzeio.openhealth.data.water.Water
|
||||||
import com.dzeio.openhealth.databinding.FragmentHomeBinding
|
|
||||||
import com.dzeio.openhealth.data.weight.Weight
|
import com.dzeio.openhealth.data.weight.Weight
|
||||||
|
import com.dzeio.openhealth.databinding.FragmentHomeBinding
|
||||||
import com.dzeio.openhealth.ui.weight.AddWeightDialog
|
import com.dzeio.openhealth.ui.weight.AddWeightDialog
|
||||||
import com.dzeio.openhealth.utils.BitmapUtils
|
|
||||||
import com.dzeio.openhealth.utils.DrawUtils
|
import com.dzeio.openhealth.utils.DrawUtils
|
||||||
import com.github.mikephil.charting.components.AxisBase
|
import com.dzeio.openhealth.utils.GraphUtils
|
||||||
import com.github.mikephil.charting.components.Description
|
|
||||||
import com.github.mikephil.charting.components.XAxis
|
|
||||||
import com.github.mikephil.charting.data.Entry
|
import com.github.mikephil.charting.data.Entry
|
||||||
import com.github.mikephil.charting.data.LineData
|
import com.github.mikephil.charting.data.LineData
|
||||||
import com.github.mikephil.charting.data.LineDataSet
|
import com.github.mikephil.charting.data.LineDataSet
|
||||||
import com.github.mikephil.charting.formatter.ValueFormatter
|
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.text.DateFormat
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.*
|
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
@ -53,8 +46,6 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
viewModel.init()
|
|
||||||
|
|
||||||
binding.addWeight.setOnClickListener {
|
binding.addWeight.setOnClickListener {
|
||||||
AddWeightDialog().show(requireActivity().supportFragmentManager, null)
|
AddWeightDialog().show(requireActivity().supportFragmentManager, null)
|
||||||
}
|
}
|
||||||
@ -112,14 +103,23 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
|
|||||||
}
|
}
|
||||||
|
|
||||||
binding.listWeight.setOnClickListener {
|
binding.listWeight.setOnClickListener {
|
||||||
Log.d("T", "Trying to move")
|
|
||||||
|
|
||||||
findNavController().navigate(HomeFragmentDirections.actionNavHomeToNavListWeight())
|
findNavController().navigate(HomeFragmentDirections.actionNavHomeToNavListWeight())
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.gotoWaterHome.setOnClickListener {
|
binding.gotoWaterHome.setOnClickListener {
|
||||||
findNavController().navigate(HomeFragmentDirections.actionNavHomeToNavWaterHome())
|
findNavController().navigate(HomeFragmentDirections.actionNavHomeToNavWaterHome())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GraphUtils.lineChartSetup(
|
||||||
|
binding.weightGraph,
|
||||||
|
MaterialColors.getColor(
|
||||||
|
requireView(),
|
||||||
|
com.google.android.material.R.attr.colorPrimary
|
||||||
|
), MaterialColors.getColor(
|
||||||
|
requireView(),
|
||||||
|
com.google.android.material.R.attr.colorOnBackground
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateGraph(list: List<Weight>) {
|
private fun updateGraph(list: List<Weight>) {
|
||||||
@ -130,35 +130,9 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
|
|||||||
}
|
}
|
||||||
|
|
||||||
val dataSet = LineDataSet(entries, "Label")
|
val dataSet = LineDataSet(entries, "Label")
|
||||||
|
|
||||||
binding.weightGraph.apply {
|
binding.weightGraph.apply {
|
||||||
|
|
||||||
// Setup
|
|
||||||
isAutoScaleMinMaxEnabled = true
|
|
||||||
legend.isEnabled = false
|
|
||||||
isDragEnabled = true
|
|
||||||
isScaleYEnabled = false
|
|
||||||
description = Description().apply { isEnabled = false }
|
|
||||||
isScaleXEnabled = true
|
|
||||||
setPinchZoom(false)
|
|
||||||
setDrawGridBackground(false)
|
|
||||||
setDrawBorders(false)
|
|
||||||
axisLeft.setLabelCount(0, true)
|
|
||||||
|
|
||||||
xAxis.apply {
|
|
||||||
valueFormatter = object : ValueFormatter() {
|
|
||||||
override fun getAxisLabel(value: Float, axis: AxisBase?): String {
|
|
||||||
return SimpleDateFormat(
|
|
||||||
"yyyy-MM-dd",
|
|
||||||
Locale.getDefault()
|
|
||||||
).format(Date(value.toLong()))
|
|
||||||
//return super.getAxisLabel(value, axis)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
position = XAxis.XAxisPosition.BOTTOM
|
|
||||||
setDrawGridLines(false)
|
|
||||||
setLabelCount(3, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply new dataset
|
// Apply new dataset
|
||||||
data = LineData(dataSet)
|
data = LineData(dataSet)
|
||||||
|
|
||||||
@ -181,6 +155,8 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
|
|||||||
viewModel.fetchWeights().collectLatest {
|
viewModel.fetchWeights().collectLatest {
|
||||||
updateGraph(it)
|
updateGraph(it)
|
||||||
}
|
}
|
||||||
|
updateWater(0)
|
||||||
|
updateWater(1234)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.water.observe(viewLifecycleOwner) {
|
viewModel.water.observe(viewLifecycleOwner) {
|
||||||
@ -192,38 +168,75 @@ class HomeFragment : BaseFragment<HomeViewModel, FragmentHomeBinding>(HomeViewMo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateWater(0)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateWater(water: Int) {
|
private fun updateWater(water: Int) {
|
||||||
val oldValue = binding.fragmentHomeWaterCurrent.text.toString().replace("ml", "").toInt()
|
val oldValue = binding.fragmentHomeWaterCurrent.text.toString().replace("ml", "").toInt()
|
||||||
binding.fragmentHomeWaterCurrent.text = "${water}ml"
|
binding.fragmentHomeWaterCurrent.text = "${water}ml"
|
||||||
|
|
||||||
val graph = BitmapUtils.convertToMutable(
|
var width = 1500
|
||||||
requireContext(),
|
var height = 750
|
||||||
BitmapFactory.decodeResource(resources, R.drawable.ellipse)
|
|
||||||
|
if (binding.background.width != 0) {
|
||||||
|
width = binding.background.width
|
||||||
|
height = binding.background.height
|
||||||
|
}
|
||||||
|
|
||||||
|
val graph = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||||
|
Log.d("Test2", "$width $height")
|
||||||
|
|
||||||
|
val canvas = Canvas(graph)
|
||||||
|
val rect = RectF(
|
||||||
|
10f,
|
||||||
|
15f,
|
||||||
|
90f,
|
||||||
|
85f
|
||||||
)
|
)
|
||||||
|
|
||||||
graph?.let { btmp ->
|
// DrawUtils.drawRect(
|
||||||
ValueAnimator.ofFloat(min(oldValue.toFloat(), intake), min(water.toFloat(), intake))
|
// canvas,
|
||||||
.apply {
|
// RectF(
|
||||||
duration = 300
|
// 0f,
|
||||||
addUpdateListener {
|
// 0f,
|
||||||
val canvas = Canvas(btmp)
|
// 100f,
|
||||||
DrawUtils.drawArc(
|
// 100f
|
||||||
canvas,
|
// ),
|
||||||
100 * it.animatedValue as Float / intake,
|
// MaterialColors.getColor(
|
||||||
MaterialColors.getColor(
|
// requireView(),
|
||||||
requireView(),
|
// com.google.android.material.R.attr.colorOnPrimary
|
||||||
com.google.android.material.R.attr.colorPrimary
|
// ),
|
||||||
)
|
// 3f
|
||||||
)
|
// )
|
||||||
canvas.save()
|
|
||||||
binding.background.setImageBitmap(graph)
|
DrawUtils.drawArc(
|
||||||
}
|
canvas,
|
||||||
start()
|
100f,
|
||||||
|
rect,
|
||||||
|
MaterialColors.getColor(
|
||||||
|
requireView(),
|
||||||
|
com.google.android.material.R.attr.colorOnPrimary
|
||||||
|
),
|
||||||
|
3f
|
||||||
|
)
|
||||||
|
|
||||||
|
Log.d("Test", "${min(oldValue.toFloat(), intake)} ${min(water.toFloat(), intake)}")
|
||||||
|
ValueAnimator.ofFloat(min(oldValue.toFloat(), intake), min(water.toFloat(), intake))
|
||||||
|
.apply {
|
||||||
|
duration = 300
|
||||||
|
addUpdateListener {
|
||||||
|
DrawUtils.drawArc(
|
||||||
|
canvas,
|
||||||
|
100 * it.animatedValue as Float / intake,
|
||||||
|
rect,
|
||||||
|
MaterialColors.getColor(
|
||||||
|
requireView(),
|
||||||
|
com.google.android.material.R.attr.colorPrimary
|
||||||
|
), 6f
|
||||||
|
)
|
||||||
|
canvas.save()
|
||||||
|
binding.background.setImageBitmap(graph)
|
||||||
}
|
}
|
||||||
}
|
start()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package com.dzeio.openhealth.ui.home
|
package com.dzeio.openhealth.ui.home
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.dzeio.openhealth.core.BaseViewModel
|
import com.dzeio.openhealth.core.BaseViewModel
|
||||||
@ -18,8 +19,23 @@ class HomeViewModel @Inject internal constructor(
|
|||||||
private val waterRepository: WaterRepository
|
private val waterRepository: WaterRepository
|
||||||
) : BaseViewModel() {
|
) : BaseViewModel() {
|
||||||
|
|
||||||
|
init {
|
||||||
|
viewModelScope.launch {
|
||||||
|
waterRepository.todayWater().collectLatest {
|
||||||
|
_water.value = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
fun fetchWeights() = weightRepository.getWeights()
|
fun fetchWeights() = weightRepository.getWeights()
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
fun lastWeight() = weightRepository.lastWeight()
|
fun lastWeight() = weightRepository.lastWeight()
|
||||||
|
|
||||||
fun fetchWeight(id: Long) = weightRepository.getWeight(id)
|
fun fetchWeight(id: Long) = weightRepository.getWeight(id)
|
||||||
@ -28,17 +44,11 @@ class HomeViewModel @Inject internal constructor(
|
|||||||
|
|
||||||
suspend fun addWeight(weight: Weight) = weightRepository.addWeight(weight)
|
suspend fun addWeight(weight: Weight) = weightRepository.addWeight(weight)
|
||||||
|
|
||||||
fun fetchTodayWater() = waterRepository.todayWater()
|
suspend fun fetchTodayWater() = waterRepository.todayWater()
|
||||||
|
|
||||||
val water: MutableLiveData<Water?> = MutableLiveData(null)
|
private val _water = MutableLiveData<Water?>(null)
|
||||||
|
val water: LiveData<Water?> = _water
|
||||||
|
|
||||||
fun init() {
|
|
||||||
viewModelScope.launch {
|
|
||||||
waterRepository.todayWater().collectLatest {
|
|
||||||
water.postValue(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun updateWater(water: Water) {
|
fun updateWater(water: Water) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
@ -49,7 +59,7 @@ class HomeViewModel @Inject internal constructor(
|
|||||||
fun deleteWater(item: Water) {
|
fun deleteWater(item: Water) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
waterRepository.deleteWater(item)
|
waterRepository.deleteWater(item)
|
||||||
water.postValue(null)
|
_water.postValue(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,9 @@
|
|||||||
package com.dzeio.openhealth.ui.water
|
package com.dzeio.openhealth.ui.water
|
||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.Window
|
import android.view.Window
|
||||||
@ -13,8 +15,12 @@ import androidx.navigation.fragment.navArgs
|
|||||||
import com.dzeio.openhealth.R
|
import com.dzeio.openhealth.R
|
||||||
import com.dzeio.openhealth.core.BaseFullscreenDialog
|
import com.dzeio.openhealth.core.BaseFullscreenDialog
|
||||||
import com.dzeio.openhealth.databinding.DialogWaterEditWaterBinding
|
import com.dzeio.openhealth.databinding.DialogWaterEditWaterBinding
|
||||||
|
import com.google.android.material.datepicker.CalendarConstraints
|
||||||
|
import com.google.android.material.datepicker.DateValidatorPointBackward
|
||||||
|
import com.google.android.material.datepicker.MaterialDatePicker
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class EditWaterDialog :
|
class EditWaterDialog :
|
||||||
@ -40,13 +46,53 @@ class EditWaterDialog :
|
|||||||
|
|
||||||
viewModel.water.observe(viewLifecycleOwner) {
|
viewModel.water.observe(viewLifecycleOwner) {
|
||||||
binding.editTextNumber.setText(it.value.toString())
|
binding.editTextNumber.setText(it.value.toString())
|
||||||
|
binding.date.text = it.formatTimestamp()
|
||||||
}
|
}
|
||||||
binding.editTextNumber.doOnTextChanged { text, start, before, count ->
|
binding.editTextNumber.doOnTextChanged { text, start, before, count ->
|
||||||
newValue = text.toString().toInt()
|
val value = text.toString()
|
||||||
|
newValue = if (value == "") 0
|
||||||
|
else text.toString().toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.date.setOnClickListener {
|
||||||
|
val water = viewModel.water.value!!
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
val date = Date(water.timestamp)
|
||||||
|
val datePicker = MaterialDatePicker.Builder.datePicker()
|
||||||
|
.setTitleText("Select Date")
|
||||||
|
.setSelection(water.timestamp)
|
||||||
|
.setCalendarConstraints(
|
||||||
|
CalendarConstraints.Builder()
|
||||||
|
.setValidator(DateValidatorPointBackward.now())
|
||||||
|
.setEnd(Date().time)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val fragManager = requireActivity().supportFragmentManager
|
||||||
|
|
||||||
|
datePicker.addOnPositiveButtonClickListener { tsp ->
|
||||||
|
|
||||||
|
water.timestamp = tsp
|
||||||
|
binding.date.setText(water.formatTimestamp())
|
||||||
|
|
||||||
|
}
|
||||||
|
datePicker.show(fragManager, "dialog")
|
||||||
|
Log.d("Tag", "${date.year + 1900}, ${date.month}, ${date.day}")
|
||||||
|
// val dg = DatePickerDialog(requireActivity())
|
||||||
|
// dg.setOnDateSetListener { _, year, month, day ->
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// dg.updateDate(date.year + 1900, date.month, date.day)
|
||||||
|
// dg.show()
|
||||||
|
} else {
|
||||||
|
TODO("VERSION.SDK_INT < N")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.init(args.id)
|
viewModel.init(args.id)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun save() {
|
private fun save() {
|
||||||
|
@ -1,30 +1,20 @@
|
|||||||
package com.dzeio.openhealth.ui.water
|
package com.dzeio.openhealth.ui.water
|
||||||
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.dzeio.openhealth.adapters.WaterAdapter
|
import com.dzeio.openhealth.adapters.WaterAdapter
|
||||||
import com.dzeio.openhealth.adapters.WeightAdapter
|
|
||||||
import com.dzeio.openhealth.core.BaseFragment
|
import com.dzeio.openhealth.core.BaseFragment
|
||||||
import com.dzeio.openhealth.databinding.FragmentListWeightBinding
|
|
||||||
import com.dzeio.openhealth.databinding.FragmentMainWaterHomeBinding
|
import com.dzeio.openhealth.databinding.FragmentMainWaterHomeBinding
|
||||||
import com.dzeio.openhealth.ui.home.HomeViewModel
|
|
||||||
import com.dzeio.openhealth.ui.weight.ListWeightFragmentDirections
|
|
||||||
import com.dzeio.openhealth.utils.GraphUtils
|
import com.dzeio.openhealth.utils.GraphUtils
|
||||||
import com.github.mikephil.charting.data.BarData
|
import com.github.mikephil.charting.data.BarData
|
||||||
import com.github.mikephil.charting.data.BarDataSet
|
import com.github.mikephil.charting.data.BarDataSet
|
||||||
import com.github.mikephil.charting.data.BarEntry
|
import com.github.mikephil.charting.data.BarEntry
|
||||||
import com.github.mikephil.charting.data.Entry
|
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
|
||||||
import java.time.Instant
|
|
||||||
import java.time.temporal.ChronoUnit
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@ -60,6 +50,9 @@ class WaterHomeFragment :
|
|||||||
chart, MaterialColors.getColor(
|
chart, MaterialColors.getColor(
|
||||||
requireView(),
|
requireView(),
|
||||||
com.google.android.material.R.attr.colorPrimary
|
com.google.android.material.R.attr.colorPrimary
|
||||||
|
), MaterialColors.getColor(
|
||||||
|
requireView(),
|
||||||
|
com.google.android.material.R.attr.colorOnBackground
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -73,7 +66,7 @@ class WaterHomeFragment :
|
|||||||
epoch.time = Date(0)
|
epoch.time = Date(0)
|
||||||
epoch.add(Calendar.MILLISECOND, it.timestamp.toInt())
|
epoch.add(Calendar.MILLISECOND, it.timestamp.toInt())
|
||||||
return@map BarEntry(
|
return@map BarEntry(
|
||||||
epoch.get(Calendar.DATE).toFloat(),
|
(epoch.timeInMillis / 1000 / 60 / 60).toFloat(),
|
||||||
it.value.toFloat()
|
it.value.toFloat()
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -1,24 +1,23 @@
|
|||||||
package com.dzeio.openhealth.utils
|
package com.dzeio.openhealth.utils
|
||||||
|
|
||||||
import android.graphics.*
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.graphics.RectF
|
||||||
|
|
||||||
object DrawUtils {
|
object DrawUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fuck Graphics
|
* Fuck Graphics
|
||||||
*/
|
*/
|
||||||
fun drawArc(canvas: Canvas, percent: Float, pColor: Int) {
|
fun drawArc(canvas: Canvas, percent: Float, rect: RectF, pColor: Int, strokeWidth: Float = 1f) {
|
||||||
canvas.width
|
|
||||||
val spacing = 120f
|
|
||||||
val r1 = RectF(
|
val r1 = RectF(
|
||||||
spacing,
|
canvas.realSize(true, rect.left),
|
||||||
spacing,
|
canvas.realSize(false, rect.top),
|
||||||
canvas.width - spacing,
|
canvas.realSize(true, rect.right),
|
||||||
canvas.height * 2 - spacing * 3
|
canvas.realSize(false, rect.bottom, 2)
|
||||||
)
|
)
|
||||||
val paint = Paint()
|
val paint = Paint().apply {
|
||||||
paint.apply {
|
this.strokeWidth = canvas.realSize(true, strokeWidth)
|
||||||
strokeWidth = 200f
|
|
||||||
style = Paint.Style.STROKE
|
style = Paint.Style.STROKE
|
||||||
color = pColor
|
color = pColor
|
||||||
isAntiAlias = true
|
isAntiAlias = true
|
||||||
@ -27,4 +26,33 @@ object DrawUtils {
|
|||||||
canvas.drawArc(r1, 180f, 180 * percent / 100f, false, paint)
|
canvas.drawArc(r1, 180f, 180 * percent / 100f, false, paint)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fuck Graphics
|
||||||
|
*/
|
||||||
|
fun drawRect(canvas: Canvas, rect: RectF, pColor: Int, strokeWidth: Float = 1f) {
|
||||||
|
val r1 = RectF(
|
||||||
|
canvas.realSize(true, rect.left),
|
||||||
|
canvas.realSize(false, rect.top),
|
||||||
|
canvas.realSize(true, rect.right),
|
||||||
|
canvas.realSize(false, rect.bottom)
|
||||||
|
)
|
||||||
|
val paint = Paint().apply {
|
||||||
|
this.strokeWidth = canvas.realSize(true, strokeWidth)
|
||||||
|
style = Paint.Style.STROKE
|
||||||
|
color = pColor
|
||||||
|
isAntiAlias = true
|
||||||
|
}
|
||||||
|
canvas.drawRect(r1, paint)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Canvas.realSize(isWidth: Boolean, value: Float): Float {
|
||||||
|
val it = if (isWidth) this.width else this.height
|
||||||
|
return it * value / 100
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Canvas.realSize(isWidth: Boolean, value: Float, multiplier: Int): Float {
|
||||||
|
val it = (if (isWidth) this.width else this.height) * multiplier
|
||||||
|
return it * value / 100
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,35 +1,34 @@
|
|||||||
package com.dzeio.openhealth.utils
|
package com.dzeio.openhealth.utils
|
||||||
|
|
||||||
import android.graphics.Color
|
|
||||||
import com.github.mikephil.charting.charts.BarChart
|
import com.github.mikephil.charting.charts.BarChart
|
||||||
import com.github.mikephil.charting.charts.BarLineChartBase
|
import com.github.mikephil.charting.charts.BarLineChartBase
|
||||||
import com.github.mikephil.charting.charts.Chart
|
|
||||||
import com.github.mikephil.charting.charts.LineChart
|
import com.github.mikephil.charting.charts.LineChart
|
||||||
import com.github.mikephil.charting.components.AxisBase
|
import com.github.mikephil.charting.components.AxisBase
|
||||||
import com.github.mikephil.charting.components.Description
|
import com.github.mikephil.charting.components.Description
|
||||||
import com.github.mikephil.charting.components.XAxis
|
import com.github.mikephil.charting.components.XAxis
|
||||||
import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData
|
import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData
|
||||||
import com.github.mikephil.charting.data.ChartData
|
|
||||||
import com.github.mikephil.charting.data.Entry
|
import com.github.mikephil.charting.data.Entry
|
||||||
import com.github.mikephil.charting.data.LineData
|
|
||||||
import com.github.mikephil.charting.formatter.ValueFormatter
|
import com.github.mikephil.charting.formatter.ValueFormatter
|
||||||
import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet
|
import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet
|
||||||
import com.github.mikephil.charting.interfaces.datasets.IDataSet
|
|
||||||
import com.google.android.material.color.MaterialColors
|
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
object GraphUtils {
|
object GraphUtils {
|
||||||
|
|
||||||
fun lineChartSetup(chart: LineChart, mainColor: Int) {
|
fun lineChartSetup(chart: LineChart, mainColor: Int, textColor: Int) {
|
||||||
barLineChartSetup(chart, mainColor)
|
barLineChartSetup(chart, mainColor, textColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun barChartSetup(chart: BarChart, mainColor: Int) {
|
fun barChartSetup(chart: BarChart, mainColor: Int, textColor: Int) {
|
||||||
barLineChartSetup(chart, mainColor)
|
barLineChartSetup(chart, mainColor, textColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : BarLineScatterCandleBubbleData<out IBarLineScatterCandleBubbleDataSet<out Entry>>?> barLineChartSetup(chart: BarLineChartBase<T>, mainColor: Int) {
|
private fun <T : BarLineScatterCandleBubbleData<out IBarLineScatterCandleBubbleDataSet<out Entry>>?> barLineChartSetup(
|
||||||
|
chart: BarLineChartBase<T>,
|
||||||
|
mainColor: Int,
|
||||||
|
textColor: Int
|
||||||
|
) {
|
||||||
|
|
||||||
chart.apply {
|
chart.apply {
|
||||||
|
|
||||||
// Setup
|
// Setup
|
||||||
@ -49,18 +48,25 @@ object GraphUtils {
|
|||||||
position = XAxis.XAxisPosition.BOTTOM
|
position = XAxis.XAxisPosition.BOTTOM
|
||||||
setDrawGridLines(false)
|
setDrawGridLines(false)
|
||||||
setLabelCount(3, true)
|
setLabelCount(3, true)
|
||||||
textColor = Color.WHITE
|
this.textColor = textColor
|
||||||
|
//setDrawGridLines(false)
|
||||||
|
//setDrawZeroLine(false)
|
||||||
|
setDrawAxisLine(false)
|
||||||
|
disableGridDashedLine()
|
||||||
|
invalidateOutline()
|
||||||
}
|
}
|
||||||
|
|
||||||
axisLeft.apply {
|
axisLeft.apply {
|
||||||
axisLineColor = mainColor
|
axisLineColor = mainColor
|
||||||
textColor = Color.WHITE
|
this.textColor = textColor
|
||||||
|
// setDrawZeroLine(false)
|
||||||
setLabelCount(0, true)
|
setLabelCount(0, true)
|
||||||
|
setDrawGridLines(false)
|
||||||
}
|
}
|
||||||
axisRight.apply {
|
axisRight.apply {
|
||||||
textColor = Color.WHITE
|
this.textColor = textColor
|
||||||
}
|
}
|
||||||
setNoDataTextColor(Color.WHITE)
|
setNoDataTextColor(textColor)
|
||||||
|
|
||||||
|
|
||||||
isAutoScaleMinMaxEnabled = true
|
isAutoScaleMinMaxEnabled = true
|
||||||
@ -70,8 +76,8 @@ object GraphUtils {
|
|||||||
description = Description().apply { isEnabled = false }
|
description = Description().apply { isEnabled = false }
|
||||||
isScaleXEnabled = true
|
isScaleXEnabled = true
|
||||||
setPinchZoom(false)
|
setPinchZoom(false)
|
||||||
//setDrawGridBackground(false)
|
setDrawGridBackground(false)
|
||||||
//setDrawBorders(false)
|
setDrawBorders(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,7 +2,8 @@
|
|||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="24">
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
<path
|
<path
|
||||||
android:pathData="M12.0001,1.7144L20.9078,6.8572V17.1429L12.0001,22.2858L3.0924,17.1429V6.8572L12.0001,1.7144Z"
|
android:pathData="M12.0001,1.7144L20.9078,6.8572V17.1429L12.0001,22.2858L3.0924,17.1429V6.8572L12.0001,1.7144Z"
|
||||||
android:strokeWidth="2"
|
android:strokeWidth="2"
|
||||||
|
@ -1,55 +1,46 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/drawer_layout"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:openDrawer="start"
|
|
||||||
tools:context=".MainActivity">
|
tools:context=".MainActivity">
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
style="@style/Widget.Material3.AppBarLayout"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
app:titleCentered="true"
|
||||||
|
style="@style/ThemeOverlay.Material3.Toolbar.Surface"
|
||||||
|
android:layout_height="?attr/actionBarSize" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
>
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
|
<androidx.fragment.app.FragmentContainerView
|
||||||
|
android:id="@+id/nav_host_fragment"
|
||||||
|
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
style="?attr/appBarLayoutStyle"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
style="?attr/toolbarStyle"
|
|
||||||
app:titleCentered="true"
|
|
||||||
android:layout_height="?attr/actionBarSize" />
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
|
||||||
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
|
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
app:defaultNavHost="true"
|
||||||
android:id="@+id/nav_host_fragment"
|
app:navGraph="@navigation/mobile_navigation" />
|
||||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
</androidx.core.widget.NestedScrollView>
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
|
|
||||||
app:defaultNavHost="true"
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
app:navGraph="@navigation/mobile_navigation" />
|
|
||||||
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
|
|
||||||
</androidx.drawerlayout.widget.DrawerLayout>
|
|
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
@ -8,8 +9,20 @@
|
|||||||
android:id="@+id/editTextNumber"
|
android:id="@+id/editTextNumber"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
android:ems="10"
|
android:ems="10"
|
||||||
android:inputType="number"
|
android:inputType="number"
|
||||||
tools:layout_editor_absoluteX="101dp"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
tools:layout_editor_absoluteY="107dp" />
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/date"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:text="2021-12-21"
|
||||||
|
app:layout_constraintEnd_toEndOf="@+id/editTextNumber"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/editTextNumber"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/editTextNumber" />
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
39
app/src/main/res/layout/fragment_extension.xml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:weightSum="2">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/import_button"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Force Import" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/export_button"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Force Export" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView2"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/extension_informations" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -1,122 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:orientation="vertical"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".ui.extensions.ExtensionsFragment">
|
android:id="@+id/list"
|
||||||
|
tools:listitem="@layout/layout_extension_item"
|
||||||
|
tools:context=".ui.extensions.ExtensionsFragment" />
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
style="?attr/materialCardViewFilledStyle"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:id="@+id/import_google_fit"
|
|
||||||
android:layout_marginEnd="16dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="40dp"
|
|
||||||
android:layout_height="40dp"
|
|
||||||
android:layout_marginHorizontal="16dp"
|
|
||||||
android:layout_marginVertical="16dp"
|
|
||||||
android:src="@drawable/ic_logo_fit" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginVertical="16dp"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/TextAppearance.Material3.TitleMedium"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Google Fit" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/TextAppearance.Material3.BodyMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Last Sync: Yesterday" />
|
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="24dp"
|
|
||||||
android:layout_height="24dp"
|
|
||||||
android:layout_marginHorizontal="16dp"
|
|
||||||
android:layout_marginVertical="16dp"
|
|
||||||
android:src="@drawable/ic_baseline_extension_24" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:id="@+id/import_samsung_health"
|
|
||||||
style="?attr/materialCardViewOutlinedStyle"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginEnd="16dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="40dp"
|
|
||||||
android:layout_height="40dp"
|
|
||||||
android:layout_marginHorizontal="16dp"
|
|
||||||
android:layout_marginVertical="16dp"
|
|
||||||
android:src="@drawable/logo_shealth" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginVertical="16dp"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/TextAppearance.Material3.TitleMedium"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Samsung Health" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/TextAppearance.Material3.BodyMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Currently Unavailable" />
|
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="24dp"
|
|
||||||
android:layout_height="24dp"
|
|
||||||
android:layout_marginHorizontal="16dp"
|
|
||||||
android:layout_marginVertical="16dp"
|
|
||||||
android:src="@drawable/ic_baseline_extension_24" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
</LinearLayout>
|
|
@ -39,8 +39,8 @@
|
|||||||
style="@style/TextAppearance.Material3.TitleMedium"
|
style="@style/TextAppearance.Material3.TitleMedium"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Weight"
|
android:layout_weight="1"
|
||||||
android:layout_weight="1" />
|
android:text="Weight" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@ -75,7 +75,7 @@
|
|||||||
android:id="@+id/fragment_home_water_remove"
|
android:id="@+id/fragment_home_water_remove"
|
||||||
android:layout_width="18dp"
|
android:layout_width="18dp"
|
||||||
android:layout_height="18dp"
|
android:layout_height="18dp"
|
||||||
android:layout_marginEnd="4dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:src="@drawable/ic_outline_hexagon_24"
|
android:src="@drawable/ic_outline_hexagon_24"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/linearLayout"
|
app:layout_constraintBottom_toBottomOf="@+id/linearLayout"
|
||||||
@ -86,11 +86,12 @@
|
|||||||
android:id="@+id/linearLayout"
|
android:id="@+id/linearLayout"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="@+id/background">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/fragment_home_water_current"
|
android:id="@+id/fragment_home_water_current"
|
||||||
@ -116,7 +117,7 @@
|
|||||||
android:id="@+id/fragment_home_water_add"
|
android:id="@+id/fragment_home_water_add"
|
||||||
android:layout_width="18dp"
|
android:layout_width="18dp"
|
||||||
android:layout_height="18dp"
|
android:layout_height="18dp"
|
||||||
android:layout_marginStart="4dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:src="@drawable/ic_baseline_add_24"
|
android:src="@drawable/ic_baseline_add_24"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/linearLayout"
|
app:layout_constraintBottom_toBottomOf="@+id/linearLayout"
|
||||||
@ -128,9 +129,9 @@
|
|||||||
android:id="@+id/background"
|
android:id="@+id/background"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
app:layout_constraintDimensionRatio="2:1"
|
|
||||||
android:src="@drawable/ic_outline_hexagon_24"
|
android:src="@drawable/ic_outline_hexagon_24"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintDimensionRatio="2:1"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
@ -10,5 +10,5 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
|
|
||||||
tools:listitem="@layout/layout_item_weight"
|
tools:listitem="@layout/layout_item_list"
|
||||||
tools:context=".ui.weight.ListWeightFragment" />
|
tools:context=".ui.weight.ListWeightFragment" />
|
@ -56,7 +56,6 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
|
tools:listitem="@layout/layout_item_list" />
|
||||||
tools:listitem="@layout/layout_item_weight" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
63
app/src/main/res/layout/layout_extension_item.xml
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
style="?attr/materialCardViewFilledStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:id="@+id/card"
|
||||||
|
android:layout_marginEnd="16dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:id="@+id/logo"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginVertical="16dp"
|
||||||
|
android:src="@drawable/ic_logo_fit" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginVertical="16dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/TextAppearance.Material3.TitleMedium"
|
||||||
|
android:id="@+id/name"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Google Fit" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/status"
|
||||||
|
style="@style/TextAppearance.Material3.BodyMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Last Sync: Yesterday" />
|
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/icon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginVertical="16dp"
|
||||||
|
android:src="@drawable/ic_baseline_extension_24" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
@ -1,59 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
style="?attr/materialCardViewFilledStyle"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:id="@+id/edit"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_margin="16dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
tools:layout_editor_absoluteX="16dp"
|
|
||||||
tools:layout_editor_absoluteY="0dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/weight"
|
|
||||||
style="@style/TextAppearance.Material3.TitleMedium"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="xkg"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/datetime"
|
|
||||||
style="@style/TextAppearance.Material3.BodyMedium"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:text="Taken: yyyy-mm-dd"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/weight" />
|
|
||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="24dp"
|
|
||||||
android:layout_height="24dp"
|
|
||||||
android:src="@drawable/ic_baseline_edit_24"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
@ -46,12 +46,8 @@
|
|||||||
android:label="@string/menu_import"
|
android:label="@string/menu_import"
|
||||||
tools:layout="@layout/fragment_extensions" >
|
tools:layout="@layout/fragment_extensions" >
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_nav_import_to_nav_settings"
|
android:id="@+id/action_nav_extensions_to_nav_extension"
|
||||||
app:destination="@id/nav_settings"
|
app:destination="@id/nav_extension" />
|
||||||
app:enterAnim="@android:anim/slide_in_left"
|
|
||||||
app:exitAnim="@android:anim/slide_out_right"
|
|
||||||
app:popEnterAnim="@android:anim/slide_in_left"
|
|
||||||
app:popExitAnim="@android:anim/slide_out_right" />
|
|
||||||
</fragment>
|
</fragment>
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
@ -66,13 +62,6 @@
|
|||||||
app:exitAnim="@android:anim/slide_out_right"
|
app:exitAnim="@android:anim/slide_out_right"
|
||||||
app:popEnterAnim="@android:anim/slide_in_left"
|
app:popEnterAnim="@android:anim/slide_in_left"
|
||||||
app:popExitAnim="@android:anim/slide_out_right" />
|
app:popExitAnim="@android:anim/slide_out_right" />
|
||||||
<action
|
|
||||||
android:id="@+id/action_nav_list_weight_to_nav_settings"
|
|
||||||
app:destination="@id/nav_settings"
|
|
||||||
app:enterAnim="@android:anim/slide_in_left"
|
|
||||||
app:exitAnim="@android:anim/slide_out_right"
|
|
||||||
app:popEnterAnim="@android:anim/slide_in_left"
|
|
||||||
app:popExitAnim="@android:anim/slide_out_right" />
|
|
||||||
</fragment>
|
</fragment>
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
@ -93,15 +82,12 @@
|
|||||||
android:label="@string/nav_water_home"
|
android:label="@string/nav_water_home"
|
||||||
tools:layout="@layout/fragment_main_water_home">
|
tools:layout="@layout/fragment_main_water_home">
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_nav_water_home_to_nav_settings"
|
android:id="@+id/action_nav_water_home_to_nav_water_edit"
|
||||||
app:destination="@id/nav_settings"
|
app:destination="@id/nav_water_edit"
|
||||||
app:enterAnim="@android:anim/slide_in_left"
|
app:enterAnim="@android:anim/slide_in_left"
|
||||||
app:exitAnim="@android:anim/slide_out_right"
|
app:exitAnim="@android:anim/slide_out_right"
|
||||||
app:popEnterAnim="@android:anim/slide_in_left"
|
app:popEnterAnim="@android:anim/slide_in_left"
|
||||||
app:popExitAnim="@android:anim/slide_out_right" />
|
app:popExitAnim="@android:anim/slide_out_right" />
|
||||||
<action
|
|
||||||
android:id="@+id/action_nav_water_home_to_nav_water_edit"
|
|
||||||
app:destination="@id/nav_water_edit" />
|
|
||||||
</fragment>
|
</fragment>
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
@ -124,4 +110,19 @@
|
|||||||
app:argType="long" />
|
app:argType="long" />
|
||||||
|
|
||||||
</fragment>
|
</fragment>
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/nav_extension"
|
||||||
|
android:name="com.dzeio.openhealth.ui.extension.ExtensionFragment"
|
||||||
|
tools:layout="@layout/fragment_extension"
|
||||||
|
app:enterAnim="@android:anim/slide_in_left"
|
||||||
|
app:exitAnim="@android:anim/slide_out_right"
|
||||||
|
app:popEnterAnim="@android:anim/slide_in_left"
|
||||||
|
app:popExitAnim="@android:anim/slide_out_right">
|
||||||
|
|
||||||
|
<argument
|
||||||
|
android:name="extension"
|
||||||
|
app:argType="string" />
|
||||||
|
|
||||||
|
</fragment>
|
||||||
</navigation>
|
</navigation>
|
@ -16,4 +16,5 @@
|
|||||||
<string name="nav_list_water">Water Intake</string>
|
<string name="nav_list_water">Water Intake</string>
|
||||||
<string name="nav_water_home">Water Intake</string>
|
<string name="nav_water_home">Water Intake</string>
|
||||||
<string name="menu_extensions">Extensions</string>
|
<string name="menu_extensions">Extensions</string>
|
||||||
|
<string name="extension_informations">Imports are done at app startup\nExports are done when new inputs are give to the app</string>
|
||||||
</resources>
|
</resources>
|
@ -1,29 +1,10 @@
|
|||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<!-- Base application theme. -->
|
|
||||||
<style name="Theme.OpenHealth" parent="Theme.Material3.DynamicColors.DayNight">
|
|
||||||
<!-- <!– Primary brand color. –>-->
|
|
||||||
<!-- <item name="colorPrimary">@color/purple_500</item>-->
|
|
||||||
<!-- <item name="colorPrimaryVariant">@color/purple_700</item>-->
|
|
||||||
<!-- <item name="colorOnPrimary">@color/white</item>-->
|
|
||||||
<!-- <!– Secondary brand color. –>-->
|
|
||||||
<!-- <item name="colorSecondary">@color/teal_200</item>-->
|
|
||||||
<!-- <item name="colorSecondaryVariant">@color/teal_700</item>-->
|
|
||||||
<!-- <item name="colorOnSecondary">@color/black</item>-->
|
|
||||||
<!-- <!– Status bar color. –>-->
|
|
||||||
<!-- <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>-->
|
|
||||||
<!-- Customize your theme here. -->
|
|
||||||
|
|
||||||
<item name="android:windowTranslucentStatus">true</item>
|
<style name="Theme.OpenHealth" parent="Theme.Material3.DayNight.NoActionBar" />
|
||||||
<item name="materialAlertDialogTheme">@style/ThemeOverlay.Material3.MaterialAlertDialog</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style name="Theme.OpenHealth.NoActionBar">
|
<style name="Theme.OpenHealth.NoActionBar" parent="Theme.OpenHealth">
|
||||||
<item name="windowActionBar">false</item>
|
<item name="windowActionBar">false</item>
|
||||||
<item name="windowNoTitle">true</item>
|
<item name="windowNoTitle">true</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Theme.OpenHealth.AppBarOverlay" parent="ThemeOverlay.MaterialComponents.Dark.ActionBar" />
|
|
||||||
|
|
||||||
<style name="Theme.OpenHealth.PopupOverlay" parent="ThemeOverlay.Material3.MaterialAlertDialog" />
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
2
fastlane/Appfile
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
json_key_file("./fastlane_secret_keys.json") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one
|
||||||
|
package_name("com.dzeio.openhealth") # e.g. com.krausefx.app
|
47
fastlane/Fastfile
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
opt_out_usage
|
||||||
|
# This file contains the fastlane.tools configuration
|
||||||
|
# You can find the documentation at https://docs.fastlane.tools
|
||||||
|
#
|
||||||
|
# For a list of all available actions, check out
|
||||||
|
#
|
||||||
|
# https://docs.fastlane.tools/actions
|
||||||
|
#
|
||||||
|
# For a list of all available plugins, check out
|
||||||
|
#
|
||||||
|
# https://docs.fastlane.tools/plugins/available-plugins
|
||||||
|
#
|
||||||
|
|
||||||
|
# Uncomment the line if you want fastlane to automatically update itself
|
||||||
|
# update_fastlane
|
||||||
|
|
||||||
|
default_platform(:android)
|
||||||
|
|
||||||
|
platform :android do
|
||||||
|
desc "Runs all the tests"
|
||||||
|
lane :test do
|
||||||
|
gradle(task: "test")
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "Assemble Release Variant"
|
||||||
|
lane :build do
|
||||||
|
gradle(task: "clean assembleRelease")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
desc "Submit a new Beta Build to Crashlytics Beta"
|
||||||
|
lane :beta do
|
||||||
|
gradle(task: "clean assembleRelease")
|
||||||
|
crashlytics
|
||||||
|
|
||||||
|
# sh "your_script.sh"
|
||||||
|
# You can also use other beta testing services here
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "Deploy a new version to the Google Play"
|
||||||
|
lane :deploy do
|
||||||
|
gradle(task: "clean assembleRelease")
|
||||||
|
upload_to_play_store(
|
||||||
|
track: ""
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
56
fastlane/README.md
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
fastlane documentation
|
||||||
|
----
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
Make sure you have the latest version of the Xcode command line tools installed:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
xcode-select --install
|
||||||
|
```
|
||||||
|
|
||||||
|
For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)
|
||||||
|
|
||||||
|
# Available Actions
|
||||||
|
|
||||||
|
## Android
|
||||||
|
|
||||||
|
### android test
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[bundle exec] fastlane android test
|
||||||
|
```
|
||||||
|
|
||||||
|
Runs all the tests
|
||||||
|
|
||||||
|
### android build
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[bundle exec] fastlane android build
|
||||||
|
```
|
||||||
|
|
||||||
|
Assemble Release Variant
|
||||||
|
|
||||||
|
### android beta
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[bundle exec] fastlane android beta
|
||||||
|
```
|
||||||
|
|
||||||
|
Submit a new Beta Build to Crashlytics Beta
|
||||||
|
|
||||||
|
### android deploy
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[bundle exec] fastlane android deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
Deploy a new version to the Google Play
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
|
||||||
|
|
||||||
|
More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools).
|
||||||
|
|
||||||
|
The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
|
1
fastlane/metadata/android/en-GB/title.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Open Health
|
0
fastlane/metadata/android/en-GB/video.txt
Normal file
25
fastlane/report.xml
Normal file
12
fastlane_secret_keys.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"type": "service_account",
|
||||||
|
"project_id": "openhealth-334622",
|
||||||
|
"private_key_id": "b7fe3bc2980ea797341e966d571c3c552138ef2b",
|
||||||
|
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC6eAWYc1Qb2DBR\nBwuKj4Z6oXDvWeiHueSJT8HyMNHjyRDRiOgZTj1APvW9P8tsamIAhlB04gpdI73b\nUmp2wM9JbzDRyJC82x/IkfciJbXvWLa32fddWlxf2lfosWHNiGxfiIU7dqcHvaVL\niIVmNg0HB+1NIA30jn0M/EBEOf/v7u9fclGV5I1PQJdfSebWMrC1wPVHeWgawFef\nqTust152fsVKEkF42EQgMs44GYUdQnPus+bW9/JyPxDmcehVJmLTwjKD7DFwDID5\nYWnthvlQItvnxPCUB6UUxnO7CtV2eDzQEiwwCS6BWj76ydWeh2azbJALxtH48qFZ\n+REMHKSJAgMBAAECggEAXLgsdBs4oeXUVJ4Pr5Thdh3LhcCrnr2g9WQa2L5Mx5ql\nicMtQdQFIeqMj89ma+DUHVWsMQpqw9hvYdyvwp/qEqY+3LmBut6shbOK8shUmJCA\nvpeb6Cfz0dfEqZh2PNiOpsxAD4rW0EMNK6tVRbcvsCTRau268rVdWfUeUa6TZG6N\nHikyOx1klUzTO+vrL5CZFa2l7yzQNsUcljUC4fllpTMPqv/04zbAkzF9677G8zmQ\nXokBtCYlWVlzf5VWW4n2EnS76YnZOMeADiY6vdUlp9PyC6hbPHa0T8/JHBhANtlL\ny0NhkTENjRMqW+LJfgjzwIwIH/1tx4eqVJJnUNfEDwKBgQDvIqhAQ3VsRD3Vc9mN\nZdPkUgLjrbjkLKYL7A8szh1TQMrJatSYn4NZQptm8jBYqb66vAaJyaEDevt0WPv/\n9SQRIZvYaPZRsMoLo4M/xfzwY8dK/Q58r4e3E98LF0ekJJ3nRfhsguNK5Fr0Q4UB\nqFg5Nj4U/mG4bW0EO7qajPvS2wKBgQDHnoZC68EWOFzk9VsHKF+0XlDzgKmj5QLS\n45mL+ApbWYUT/iH5CA+IcelstRmSTyVpA2fzsXSp5N8TFYohAh5H54H7lR+fGNZV\nMT3ycNmjMvXs1yIJ0UbxFzmw08w1am34cbP+hqFVM+uvMtzSiol9RK8+SKiQI9eE\n5Z0jOjZ5awKBgDFIESh9Pnu7bIrKvzDWpV5OUG4fZRUQ5n9afJ4dNAnuNlxf+cQi\nS21fvqruimwbP0U4bpiCxv3yoFOP6w8KtA4bwQROTUT0jA7t+aRw5vmbdnzLveqQ\nOgXOwI6Gk6sOKMR6tQGXz8OlX+Eq8QQwb04LEaw96GGbm3Xd4UzsdRE1AoGAUiH6\nkgxYbOER7664HnDRN/BalGYK5oFysPyuj7Wl5UInDDvTFJjpczWTWoQFGnrwJI4f\nNlh8bO7bjgmdxMkPVnx9sdsAoMBiZ7kUCO2/znNIVoOJ4Mo3yzjIJuZuLkg1KTT3\nXzFbrifnwDVIQGR5/43EIPdaS7xDj8294uGvyjMCgYA+JewmU5+PxxNB4ez/+o2T\n6GyIFtPvBsIQpZaP4Ydo9ey//rfYehZ/NIjxtcKyoNOdHjYpMBwnwjXO7r00HB39\nrJ7ZrjY0AVyc9v603Xd7ZzhpQQvUNiYysXnmge2OKqJknL5En+SsIR7TZ+1LLKtn\nQD1seeucMVgNa8Vod40zlw==\n-----END PRIVATE KEY-----\n",
|
||||||
|
"client_email": "fastlane@openhealth-334622.iam.gserviceaccount.com",
|
||||||
|
"client_id": "105571318626472827244",
|
||||||
|
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||||
|
"token_uri": "https://oauth2.googleapis.com/token",
|
||||||
|
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
||||||
|
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/fastlane%40openhealth-334622.iam.gserviceaccount.com"
|
||||||
|
}
|
4
keystore.properties
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
storeFile=../upload_key.jks
|
||||||
|
keyAlias=release_key
|
||||||
|
keyPassword=Babacarflo22
|
||||||
|
storePassword=Babacarflo22
|