Compare commits

..

7 Commits

Author SHA1 Message Date
estevez-dev
e59edcf948 Add all graphics 2020-11-16 20:54:20 +02:00
Yegor Vialov
d9ace82a7f
Update README.md 2020-10-21 14:44:55 +03:00
Yegor Vialov
70aecd3c59
Update README.md 2020-10-21 14:43:38 +03:00
Yegor Vialov
2b94e26702
Update README.md 2020-10-21 14:42:21 +03:00
estevez-dev
b8ff80680b Pump version code 2020-09-09 15:30:02 +03:00
estevez-dev
e633449578 Remove in-app purchase 2020-09-09 15:04:20 +03:00
Yegor Vialov
18c48ad067
Update README.md 2020-08-25 14:03:14 +03:00
47 changed files with 117 additions and 269 deletions

View File

@ -1,4 +1,10 @@
# HA Client # HA Client
| # IMPORTANT! |
|:-:|
| As you may know official Home Assistant android app continue to grow and improve. Today is the day when it has so much more and better functionality that I see no any reason not to move from HA Client to official Home Assistant app. You need to make the same as it has more device sensors, better location tracking, widgets etc.
| **I'm stopping HA Client development as now the only advantage it has over the official app is native UI that is very far from completeness.** |
| **Login with Home Assistant credentials will be finally and irrevocably broken on Fabruary 1st of 2021 with the shut down of https://ha-client.app/.** |
| I need to say a HUGE THANK YOU all for a grate support you all made for HA Client. I really appreciate each and every issue reported to GitHub, each and every message from each of you and each donation made to support development. Now it is time to cancel subscription if any and migrate your HA setup to an official Home Assistant app. You can find it in [Google Play](https://play.google.com/store/apps/details?id=io.homeassistant.companion.android). |
## Native Android client for Home Assistant ## Native Android client for Home Assistant
### With actionable notifications, location tracking and Lovelace UI support ### With actionable notifications, location tracking and Lovelace UI support

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!-- Generator: Gravit.io --><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="0 0 108 108" width="108pt" height="108pt"><defs><clipPath id="_clipPath_5ibFn1qvppfzobeihHVrZ6swuOsurOBe"><rect width="108" height="108"/></clipPath></defs><g clip-path="url(#_clipPath_5ibFn1qvppfzobeihHVrZ6swuOsurOBe)"><rect width="108" height="108" style="fill:rgb(112,154,193)" fill-opacity="0"/><g><path d=" M 70.506 38.389 L 108 72.466 L 108 108 L 77 108 L 35.066 72.466 L 38.373 63.769 L 36.268 50.216 L 43.335 44.523 L 51.841 34.578 L 63.096 42.478 L 68.586 42.478 L 70.506 38.389 Z " fill="rgb(0,0,0)" fill-opacity="0.12"/><path d=" M 28.979 53.708 L 47.736 67.31 L 38.373 58.563 L 36.268 51.52 L 28.979 53.708 Z " fill="rgb(0,0,0)" fill-opacity="0.12"/></g><path d=" M 77.131 54.24 L 72.878 54.24 L 72.878 72.415 L 56.339 72.415 L 56.339 64.85 L 62.931 58.511 L 64.609 58.784 C 67.349 58.784 69.57 56.649 69.57 54.013 C 69.57 51.378 67.349 49.242 64.609 49.242 C 61.868 49.242 59.647 51.378 59.647 54.013 L 59.883 55.626 L 56.339 59.079 L 56.339 46.63 C 57.898 45.812 58.938 44.244 58.938 42.427 C 58.938 39.792 56.717 37.656 53.976 37.656 C 51.236 37.656 49.015 39.792 49.015 42.427 C 49.015 44.244 50.054 45.812 51.614 46.63 L 51.614 59.079 L 48.07 55.626 L 48.306 54.013 C 48.306 51.378 46.084 49.242 43.344 49.242 C 40.604 49.242 38.383 51.378 38.383 54.013 C 38.383 56.648 40.604 58.784 43.344 58.784 L 45.022 58.511 L 51.614 64.85 L 51.614 72.415 L 35.075 72.415 L 35.075 54.24 L 30.94 54.24 C 29.948 54.24 28.979 54.24 28.979 53.763 C 29.003 53.263 29.995 52.309 31.011 51.332 L 51.614 31.522 C 52.393 30.772 53.197 30 53.976 30 C 54.756 30 55.559 30.772 56.339 31.522 L 65.79 40.609 L 65.79 38.338 L 70.515 38.338 L 70.515 45.153 L 77.084 51.469 C 78.029 52.377 78.997 53.309 79.021 53.786 C 79.021 54.24 78.076 54.24 77.131 54.24 Z M 43.344 51.969 C 43.908 51.969 44.449 52.184 44.848 52.567 C 45.247 52.951 45.471 53.471 45.471 54.013 C 45.471 54.555 45.247 55.076 44.848 55.459 C 44.449 55.842 43.908 56.058 43.344 56.058 C 42.78 56.058 42.239 55.842 41.841 55.459 C 41.442 55.076 41.218 54.555 41.218 54.013 C 41.218 53.471 41.442 52.951 41.841 52.567 C 42.239 52.184 42.78 51.969 43.344 51.969 Z M 64.609 51.969 C 65.79 51.969 66.735 52.877 66.735 54.013 C 66.735 55.149 65.79 56.058 64.609 56.058 C 64.045 56.058 63.504 55.842 63.105 55.459 C 62.706 55.076 62.482 54.555 62.482 54.013 C 62.482 53.471 62.706 52.951 63.105 52.567 C 63.504 52.184 64.045 51.969 64.609 51.969 Z M 53.976 40.382 C 55.158 40.382 56.103 41.291 56.103 42.427 C 56.103 43.563 55.158 44.472 53.976 44.472 C 52.795 44.472 51.85 43.563 51.85 42.427 C 51.85 41.291 52.795 40.382 53.976 40.382 Z " fill="rgb(255,255,255)"/></g></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!-- Generator: Gravit.io --><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="0 0 512 512" width="512pt" height="512pt"><defs><clipPath id="_clipPath_wSm2gq78824Wp66cqyazQcLvYNPCHkOD"><rect width="512" height="512"/></clipPath></defs><g clip-path="url(#_clipPath_wSm2gq78824Wp66cqyazQcLvYNPCHkOD)"><rect width="512" height="512" style="fill:rgb(112,154,193)" fill-opacity="0"/><g><path d=" M 383.477 134.924 L 672.91 397.984 L 672.91 672.285 L 433.606 672.285 L 109.895 397.984 L 135.43 330.847 L 119.174 226.225 L 173.731 182.275 L 239.391 105.507 L 326.273 166.491 L 368.658 166.491 L 383.477 134.924 Z " fill="rgb(0,0,0)" fill-opacity="0.12"/><path d=" M 62.91 253.182 L 207.701 358.179 L 135.43 290.655 L 119.174 236.29 L 62.91 253.182 Z " fill="rgb(0,0,0)" fill-opacity="0.12"/></g><path d=" M 434.558 257.702 L 401.728 257.702 L 401.728 398 L 274.056 398 L 274.056 339.601 L 324.943 290.672 L 337.892 292.776 C 359.049 292.776 376.194 276.291 376.194 255.948 C 376.194 235.605 359.049 219.12 337.892 219.12 C 316.739 219.12 299.591 235.608 299.591 255.948 L 301.415 268.399 L 274.056 295.056 L 274.056 198.952 C 286.094 192.638 294.119 180.538 294.119 166.508 C 294.119 146.168 276.971 129.68 255.818 129.68 C 234.664 129.68 217.516 146.168 217.516 166.508 C 217.516 180.538 225.541 192.638 237.579 198.952 L 237.579 295.056 L 210.221 268.399 L 212.045 255.948 C 212.045 235.608 194.896 219.12 173.743 219.12 C 152.59 219.12 135.442 235.608 135.442 255.948 C 135.442 276.288 152.59 292.776 173.743 292.776 L 186.693 290.672 L 237.579 339.601 L 237.579 398 L 109.907 398 L 109.907 257.702 L 77.99 257.702 C 70.329 257.702 62.851 257.702 62.851 254.019 C 63.034 250.161 70.694 242.795 78.537 235.254 L 237.579 82.329 C 243.598 76.542 249.799 70.579 255.818 70.579 C 261.836 70.579 268.038 76.542 274.056 82.329 L 347.011 152.478 L 347.011 134.941 L 383.489 134.941 L 383.489 187.553 L 434.193 236.306 C 441.488 243.321 448.966 250.511 449.149 254.194 C 449.149 257.702 441.853 257.702 434.558 257.702 Z M 173.743 240.164 C 178.097 240.164 182.272 241.827 185.35 244.787 C 188.429 247.747 190.158 251.762 190.158 255.948 C 190.158 260.134 188.429 264.149 185.35 267.109 C 182.272 270.069 178.097 271.732 173.743 271.732 C 169.39 271.732 165.214 270.069 162.136 267.109 C 159.058 264.149 157.328 260.134 157.328 255.948 C 157.328 251.762 159.058 247.747 162.136 244.787 C 165.214 241.827 169.39 240.164 173.743 240.164 Z M 337.892 240.164 C 347.011 240.164 354.307 247.179 354.307 255.948 C 354.307 264.717 347.011 271.732 337.892 271.732 C 333.539 271.732 329.363 270.069 326.285 267.109 C 323.207 264.149 321.477 260.134 321.477 255.948 C 321.477 251.762 323.207 247.747 326.285 244.787 C 329.363 241.827 333.539 240.164 337.892 240.164 Z M 255.818 150.724 C 264.937 150.724 272.233 157.739 272.233 166.508 C 272.233 175.276 264.937 182.291 255.818 182.291 C 246.698 182.291 239.403 175.276 239.403 166.508 C 239.403 157.739 246.698 150.724 255.818 150.724 Z " fill="rgb(255,255,255)"/></g></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

BIN
graphics/banner_180x120.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
graphics/banner_180x120.xcf Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

105
graphics/ha_client.svg Normal file
View File

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="33.279999"
height="33.279999"
viewBox="0 0 33.279999 33.279999"
id="svg3384"
inkscape:version="0.92.4 5da689c313, 2019-01-14"
sodipodi:docname="ha_client.svg"
inkscape:export-filename="/home/vyalovegor/Downloads/ha_client.png"
inkscape:export-xdpi="1476.92"
inkscape:export-ydpi="1476.92">
<metadata
id="metadata3392">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs3390">
<mask
maskUnits="userSpaceOnUse"
id="mask4987">
<circle
style="fill:#03a9f4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.52243769px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="circle4989"
cx="13.801129"
cy="2.4147582"
r="16.640072"
clip-path="none" />
</mask>
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2132"
inkscape:window-height="1090"
id="namedview3388"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="11.313709"
inkscape:cx="-10.334868"
inkscape:cy="12.876365"
inkscape:window-x="0"
inkscape:window-y="55"
inkscape:window-maximized="1"
inkscape:current-layer="svg3384" />
<circle
style="fill:#03a9f4;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.52243769px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path3394"
cx="16.640072"
cy="16.640072"
r="16.640072"
clip-path="none" />
<path
style="fill:#000000;fill-opacity:0.40119761;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 14.075572,-8.35118 5.9827712,1.0714435 4.4798722,13.280061 26.759769,26.035624 37.105621,5.4863769 c -23.030049,-13.8375569 0,0 -23.030049,-13.8375569 z"
id="path4198"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc"
clip-path="none"
mask="url(#mask4987)"
transform="translate(2.8389435,14.225313)" />
<rect
style="fill:#626262;fill-opacity:1"
id="rect935"
width="4.895638"
height="3.3191559"
x="13.754303"
y="23.066553" />
<rect
id="rect68"
width="9.3691645"
height="10.871766"
x="11.667261"
y="12.243573"
style="fill:#fdd835;fill-opacity:1" />
<path
id="path116"
d="M 16.296894,5.2595255 3.4710675,17.036739 H 7.3188157 V 27.505374 H 25.274973 V 17.036739 h 3.847749 m -11.543244,7.851477 h -2.565166 v -1.30858 h 2.565166 m 0.64129,-3.166762 v 1.858183 h -3.847747 v -1.858183 a 3.8477482,3.9257379 0 1 1 3.847747,0 z"
inkscape:connector-curvature="0"
style="fill:#ffffff;fill-opacity:1;stroke-width:1.29551578" />
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
graphics/ha_client.svg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
graphics/ha_client_512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M21.8,13H20V21H13V17.67L15.79,14.88L16.5,15C17.66,15 18.6,14.06 18.6,12.9C18.6,11.74 17.66,10.8 16.5,10.8A2.1,2.1 0 0,0 14.4,12.9L14.5,13.61L13,15.13V9.65C13.66,9.29 14.1,8.6 14.1,7.8A2.1,2.1 0 0,0 12,5.7A2.1,2.1 0 0,0 9.9,7.8C9.9,8.6 10.34,9.29 11,9.65V15.13L9.5,13.61L9.6,12.9A2.1,2.1 0 0,0 7.5,10.8A2.1,2.1 0 0,0 5.4,12.9A2.1,2.1 0 0,0 7.5,15L8.21,14.88L11,17.67V21H4V13H2.25C1.83,13 1.42,13 1.42,12.79C1.43,12.57 1.85,12.15 2.28,11.72L11,3C11.33,2.67 11.67,2.33 12,2.33C12.33,2.33 12.67,2.67 13,3L17,7V6H19V9L21.78,11.78C22.18,12.18 22.59,12.59 22.6,12.8C22.6,13 22.2,13 21.8,13M7.5,12A0.9,0.9 0 0,1 8.4,12.9A0.9,0.9 0 0,1 7.5,13.8A0.9,0.9 0 0,1 6.6,12.9A0.9,0.9 0 0,1 7.5,12M16.5,12C17,12 17.4,12.4 17.4,12.9C17.4,13.4 17,13.8 16.5,13.8A0.9,0.9 0 0,1 15.6,12.9A0.9,0.9 0 0,1 16.5,12M12,6.9C12.5,6.9 12.9,7.3 12.9,7.8C12.9,8.3 12.5,8.7 12,8.7C11.5,8.7 11.1,8.3 11.1,7.8C11.1,7.3 11.5,6.9 12,6.9Z" /></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M12 3L2 12H5V20H19V12H22M13 18H11V17H13M13.5 14.58V16H10.5V14.58A3 3 0 1 1 13.5 14.58Z" /></svg>

After

Width:  |  Height:  |  Size: 381 B

BIN
graphics/scre_framed.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 KiB

View File

@ -19,7 +19,6 @@ import 'package:flutter_custom_tabs/flutter_custom_tabs.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart'; import 'package:hive_flutter/hive_flutter.dart';
import 'package:device_info/device_info.dart'; import 'package:device_info/device_info.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'plugins/dynamic_multi_column_layout.dart'; import 'plugins/dynamic_multi_column_layout.dart';
import 'plugins/spoiler_card.dart'; import 'plugins/spoiler_card.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart';
@ -103,8 +102,6 @@ part 'entities/vacuum/widgets/vacuum_controls.dart';
part 'entities/vacuum/widgets/vacuum_state_button.dart'; part 'entities/vacuum/widgets/vacuum_state_button.dart';
part 'entities/error_entity_widget.dart'; part 'entities/error_entity_widget.dart';
part 'pages/settings/connection_settings.part.dart'; part 'pages/settings/connection_settings.part.dart';
part 'pages/purchase.page.dart';
part 'pages/widgets/product_purchase.widget.dart';
part 'pages/widgets/page_loading_indicator.dart'; part 'pages/widgets/page_loading_indicator.dart';
part 'pages/widgets/bottom_info_bar.dart'; part 'pages/widgets/bottom_info_bar.dart';
part 'pages/widgets/page_loading_error.dart'; part 'pages/widgets/page_loading_error.dart';
@ -160,7 +157,7 @@ part 'managers/app_settings.dart';
EventBus eventBus = new EventBus(); EventBus eventBus = new EventBus();
const String appName = 'HA Client'; const String appName = 'HA Client';
const String appVersion = String.fromEnvironment('versionName', defaultValue: '0.0.0'); const String appVersion = String.fromEnvironment('versionName', defaultValue: '0.0.0');
const whatsNewUrl = 'http://ha-client.app/service/whats_new_1.3.0.md'; const whatsNewUrl = 'http://ha-client.app/service/whats_new_1.3.1.md';
Future<void> _reportError(dynamic error, dynamic stackTrace) async { Future<void> _reportError(dynamic error, dynamic stackTrace) async {
// Print the exception to the console. // Print the exception to the console.
@ -214,18 +211,11 @@ class HAClientApp extends StatefulWidget {
} }
class _HAClientAppState extends State<HAClientApp> { class _HAClientAppState extends State<HAClientApp> {
StreamSubscription<List<PurchaseDetails>> _purchaseUpdateSubscription;
StreamSubscription _themeChangeSubscription; StreamSubscription _themeChangeSubscription;
AppTheme _currentTheme = AppTheme.defaultTheme; AppTheme _currentTheme = AppTheme.defaultTheme;
@override @override
void initState() { void initState() {
InAppPurchaseConnection.enablePendingPurchases();
final Stream purchaseUpdates =
InAppPurchaseConnection.instance.purchaseUpdatedStream;
_purchaseUpdateSubscription = purchaseUpdates.listen((purchases) {
_handlePurchaseUpdates(purchases);
});
_currentTheme = widget.theme; _currentTheme = widget.theme;
_themeChangeSubscription = eventBus.on<ChangeThemeEvent>().listen((event){ _themeChangeSubscription = eventBus.on<ChangeThemeEvent>().listen((event){
setState(() { setState(() {
@ -235,25 +225,6 @@ class _HAClientAppState extends State<HAClientApp> {
super.initState(); super.initState();
} }
void _handlePurchaseUpdates(purchase) {
if (purchase is List<PurchaseDetails>) {
if (purchase[0].status == PurchaseStatus.purchased) {
eventBus.fire(ShowPopupEvent(
popup: Popup(
title: "Thanks a lot!",
body: "Thank you for supporting HA Client development!",
positiveText: "Ok"
)
));
InAppPurchaseConnection.instance.completePurchase(purchase[0]);
} else {
Logger.d("Purchase change handler: ${purchase[0].status}");
}
} else {
Logger.e("Something wrong with purchase handling. Got: $purchase");
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new MaterialApp( return new MaterialApp(
@ -267,7 +238,6 @@ class _HAClientAppState extends State<HAClientApp> {
"/app-settings": (context) => AppSettingsPage(), "/app-settings": (context) => AppSettingsPage(),
"/connection-settings": (context) => AppSettingsPage(showSection: AppSettingsSection.connectionSettings), "/connection-settings": (context) => AppSettingsPage(showSection: AppSettingsSection.connectionSettings),
"/integration-settings": (context) => AppSettingsPage(showSection: AppSettingsSection.integrationSettings), "/integration-settings": (context) => AppSettingsPage(showSection: AppSettingsSection.integrationSettings),
"/putchase": (context) => PurchasePage(title: "Support app development"),
"/play-media": (context) => PlayMediaPage( "/play-media": (context) => PlayMediaPage(
mediaUrl: "${ModalRoute.of(context).settings.arguments != null ? (ModalRoute.of(context).settings.arguments as Map)['url'] : ''}", mediaUrl: "${ModalRoute.of(context).settings.arguments != null ? (ModalRoute.of(context).settings.arguments as Map)['url'] : ''}",
mediaType: "${ModalRoute.of(context).settings.arguments != null ? (ModalRoute.of(context).settings.arguments as Map)['type'] ?? '' : ''}", mediaType: "${ModalRoute.of(context).settings.arguments != null ? (ModalRoute.of(context).settings.arguments as Map)['type'] ?? '' : ''}",
@ -314,7 +284,6 @@ class _HAClientAppState extends State<HAClientApp> {
@override @override
void dispose() { void dispose() {
_purchaseUpdateSubscription.cancel();
_themeChangeSubscription.cancel(); _themeChangeSubscription.cancel();
super.dispose(); super.dispose();
} }

View File

@ -11,53 +11,18 @@ class StartupUserMessagesManager {
StartupUserMessagesManager._internal(); StartupUserMessagesManager._internal();
bool _needToshowDonateMessage;
bool _whatsNewMessageShown; bool _whatsNewMessageShown;
static final _donateMsgTimerKey = "user-msg-donate-timer";
static final _donateMsgShownKey = "user-msg-donate-shpown";
static final _whatsNewMessageKey = "user-msg-whats-new-url"; static final _whatsNewMessageKey = "user-msg-whats-new-url";
void checkMessagesToShow() async { void checkMessagesToShow() async {
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.reload(); await prefs.reload();
var tInt = prefs.getInt(_donateMsgTimerKey);
if (tInt == null) {
prefs.setInt(_donateMsgTimerKey, DateTime.now().millisecondsSinceEpoch);
_needToshowDonateMessage = false;
} else {
bool wasShown = prefs.getBool(_donateMsgShownKey) ?? false;
_needToshowDonateMessage = (Duration(milliseconds: DateTime.now().millisecondsSinceEpoch - tInt).inDays >= 14) && !wasShown;
}
_whatsNewMessageShown = '${prefs.getString(_whatsNewMessageKey)}' == whatsNewUrl; _whatsNewMessageShown = '${prefs.getString(_whatsNewMessageKey)}' == whatsNewUrl;
if (!_whatsNewMessageShown) { if (!_whatsNewMessageShown) {
_showWhatsNewMessage(); _showWhatsNewMessage();
} else if (_needToshowDonateMessage) {
_showSupportAppDevelopmentMessage();
} }
} }
void _showSupportAppDevelopmentMessage() {
eventBus.fire(ShowPopupEvent(
popup: Popup(
title: "Hi!",
body: "As you may have noticed this app contains no ads. Also all app features are available for you for free. I'm not planning to change this in nearest future, but still you can support this application development materially. There is one-time payment available as well as several subscription options. Thanks.",
positiveText: "Show options",
negativeText: "Later",
onPositive: () {
SharedPreferences.getInstance().then((prefs) {
prefs.setBool(_donateMsgShownKey, true);
eventBus.fire(ShowPageEvent(path: "/putchase"));
});
},
onNegative: () {
SharedPreferences.getInstance().then((prefs) {
prefs.setInt(_donateMsgTimerKey, DateTime.now().millisecondsSinceEpoch);
});
}
)
));
}
void _showWhatsNewMessage() { void _showWhatsNewMessage() {
SharedPreferences.getInstance().then((prefs) { SharedPreferences.getInstance().then((prefs) {
prefs.setString(_whatsNewMessageKey, whatsNewUrl); prefs.setString(_whatsNewMessageKey, whatsNewUrl);

View File

@ -304,15 +304,6 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
}, },
), ),
Divider(), Divider(),
new ListTile(
leading: Icon(MaterialDesignIcons.getIconDataFromIconName("mdi:food")),
title: Text("Support app development"),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).pushNamed('/putchase');
},
),
Divider(),
new ListTile( new ListTile(
leading: Icon(Icons.help), leading: Icon(Icons.help),
title: Text("Help"), title: Text("Help"),

View File

@ -1,118 +0,0 @@
part of '../main.dart';
class PurchasePage extends StatefulWidget {
PurchasePage({Key key, this.title}) : super(key: key);
final String title;
@override
_PurchasePageState createState() => new _PurchasePageState();
}
class _PurchasePageState extends State<PurchasePage> {
bool _loaded = false;
String _error = "";
List<ProductDetails> _products;
List<PurchaseDetails> _purchases;
@override
void initState() {
super.initState();
_loadProducts();
}
_loadProducts() async {
final bool available = await InAppPurchaseConnection.instance.isAvailable();
if (!available) {
setState(() {
_error = "Error connecting to store";
});
} else {
const Set<String> _kIds = {'one_time_support','just_few_bucks_per_year', 'app_fan_support_per_year', 'grateful_user_support_per_year'};
final ProductDetailsResponse response = await InAppPurchaseConnection.instance.queryProductDetails(_kIds);
if (response.notFoundIDs.isNotEmpty) {
Logger.d("Products not found: ${response.notFoundIDs}");
}
_products = response.productDetails;
_loadPreviousPurchases();
}
}
_loadPreviousPurchases() async {
final QueryPurchaseDetailsResponse response = await InAppPurchaseConnection.instance.queryPastPurchases();
if (response.error != null) {
setState(() {
_error = "Error loading previous purchases";
});
} else {
_purchases = response.pastPurchases;
for (PurchaseDetails purchase in _purchases) {
Logger.d("Previous purchase: ${purchase.status}");
}
if (_products.isEmpty) {
setState(() {
_error = "No data found in store";
});
} else {
setState(() {
_loaded = true;
});
}
}
}
List<Widget> _buildProducts() {
List<Widget> productWidgets = [
Card(
child: Padding(
padding: EdgeInsets.all(15),
child: Text(
'This will not unlock any additional functionality. This is only a donation to the HA Client open source project.',
style: Theme.of(context).textTheme.headline5,
textAlign: TextAlign.center,
)
)
)
];
for (ProductDetails product in _products) {
productWidgets.add(
ProductPurchase(
product: product,
onBuy: (product) => _buyProduct(product),
purchased: _purchases.any((purchase) { return purchase.productID == product.id;}),)
);
}
return productWidgets;
}
void _buyProduct(ProductDetails product) {
Logger.d("Starting purchase of ${product.id}");
final PurchaseParam purchaseParam = PurchaseParam(productDetails: product);
InAppPurchaseConnection.instance.buyNonConsumable(purchaseParam: purchaseParam);
Navigator.of(context).pop();
}
@override
Widget build(BuildContext context) {
List<Widget> body;
if (!_loaded) {
body = [_error.isEmpty ? PageLoadingIndicator() : PageLoadingError(errorText: _error)];
} else {
body = _buildProducts();
}
return new Scaffold(
appBar: new AppBar(
leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: (){
Navigator.pop(context);
}),
title: new Text(widget.title),
),
body: ListView(
scrollDirection: Axis.vertical,
children: body
),
);
}
}

View File

@ -1,73 +0,0 @@
part of '../../main.dart';
class ProductPurchase extends StatelessWidget {
final ProductDetails product;
final onBuy;
final purchased;
const ProductPurchase({Key key, @required this.product, @required this.onBuy, this.purchased}) : super(key: key);
@override
Widget build(BuildContext context) {
String period = "";
Color priceColor;
String buttonText = '';
String buttonTextInactive = '';
if (product.id.contains("year")) {
period += "once a year";
buttonText = "Subscribe";
buttonTextInactive = "Already";
priceColor = Colors.amber;
} else {
period += "";
buttonText = "Pay";
buttonTextInactive = "Paid";
priceColor = Colors.deepOrangeAccent;
}
return Card(
child: Padding(
padding: EdgeInsets.all(Sizes.leftWidgetPadding),
child: Flex(
direction: Axis.horizontal,
children: <Widget>[
Expanded(
flex: 5,
child: Padding(
padding: EdgeInsets.only(right: Sizes.rightWidgetPadding),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"${product.title}",
style: Theme.of(context).textTheme.body2,
),
Container(height: Sizes.rowPadding,),
Text(
"${product.description}",
overflow: TextOverflow.ellipsis,
maxLines: 4,
softWrap: true,
),
Container(height: Sizes.rowPadding,),
Text("${product.price} $period", style: Theme.of(context).textTheme.body1.copyWith(
color: priceColor
)),
],
)
),
),
Expanded(
flex: 2,
child: RaisedButton(
child: Text(this.purchased ? buttonTextInactive : buttonText, style: Theme.of(context).textTheme.button),
color: Colors.blue,
onPressed: this.purchased ? null : () => this.onBuy(this.product),
),
)
],
),
)
);
}
}

View File

@ -1,7 +1,7 @@
name: hass_client name: hass_client
description: Home Assistant Android Client description: Home Assistant Android Client
version: 1.3.0+1310 version: 1.3.1+1312
environment: environment:
@ -19,7 +19,6 @@ dependencies:
date_format: ^1.0.8 date_format: ^1.0.8
charts_flutter: ^0.8.1 charts_flutter: ^0.8.1
flutter_markdown: ^0.3.3 flutter_markdown: ^0.3.3
in_app_purchase: ^0.3.4
flutter_custom_tabs: ^0.6.0 flutter_custom_tabs: ^0.6.0
flutter_webview_plugin: ^0.3.10+1 flutter_webview_plugin: ^0.3.10+1
webview_flutter: ^0.3.19+7 webview_flutter: ^0.3.19+7