Merge pull request #2650 from Philoul/wear/new_custom_watchface
New Customizable Watchface wip
9
_docs/icons/HourHand.svg
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px"
|
||||
height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<g id="Hour_hand">
|
||||
<path fill="#FFFFFF" d="M11.999,4.906c-0.104,0-0.188,0.084-0.188,0.188V12c0,0.104,0.084,0.188,0.188,0.188
|
||||
c0.104,0,0.188-0.084,0.188-0.188V5.094C12.186,4.99,12.102,4.906,11.999,4.906z M11.999,12.094c-0.047,0-0.086-0.038-0.086-0.086
|
||||
s0.039-0.086,0.086-0.086c0.047,0,0.086,0.038,0.086,0.086S12.046,12.094,11.999,12.094z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 613 B |
9
_docs/icons/MinuteHand.svg
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px"
|
||||
height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<g id="Minute_hend">
|
||||
<path fill="#FFFFFF" d="M11.999,1.406c-0.104,0-0.188,0.084-0.188,0.188V12c0,0.104,0.084,0.188,0.188,0.188
|
||||
c0.104,0,0.188-0.084,0.188-0.188V1.594C12.186,1.49,12.102,1.406,11.999,1.406z M11.999,12.094c-0.047,0-0.086-0.038-0.086-0.086
|
||||
s0.039-0.086,0.086-0.086c0.047,0,0.086,0.038,0.086,0.086S12.046,12.094,11.999,12.094z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 615 B |
11
_docs/icons/SecondHand.svg
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px"
|
||||
height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<g id="Second_hand">
|
||||
<path fill="#FF1313" d="M12.198,11.462v-0.237c0-0.077-0.05-0.131-0.114-0.164V0.508c0-0.047-0.038-0.086-0.086-0.086
|
||||
c-0.047,0-0.086,0.038-0.086,0.086v10.553c-0.063,0.033-0.114,0.087-0.114,0.164v0.238c-0.219,0.082-0.376,0.29-0.376,0.537
|
||||
s0.157,0.455,0.376,0.537v0.92c0,0.11,0.089,0.2,0.2,0.2s0.2-0.089,0.2-0.2v-0.919c0.221-0.081,0.381-0.289,0.381-0.538
|
||||
S12.419,11.543,12.198,11.462z M11.999,12.094c-0.047,0-0.086-0.038-0.086-0.086s0.039-0.086,0.086-0.086
|
||||
c0.047,0,0.086,0.038,0.086,0.086S12.046,12.094,11.999,12.094z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 816 B |
18
_docs/icons/SimplifiedDial.svg
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px"
|
||||
height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<g id="Simplified_dial">
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="11.999" y1="0.008" x2="11.999" y2="2.708"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="11.999" y1="21.307" x2="11.999" y2="24.008"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="23.999" y1="12" x2="21.298" y2="12"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="2.699" y1="12" x2="-0.001" y2="12"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="5.999" y1="1.616" x2="7.349" y2="3.954"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="16.648" y1="20.061" x2="17.999" y2="22.4"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="22.387" y1="6.001" x2="20.048" y2="7.351"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="3.941" y1="16.651" x2="1.602" y2="18.001"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="1.606" y1="6.008" x2="3.945" y2="7.358"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="20.052" y1="16.658" x2="22.391" y2="18.008"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="17.992" y1="1.612" x2="16.642" y2="3.95"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="7.342" y1="20.057" x2="5.992" y2="22.396"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
8
_docs/icons/background.svg
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?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 version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px"
|
||||
height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<g id="Background">
|
||||
<rect x="0.103" width="24" height="24"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 430 B |
70
_docs/icons/detailed_dial.svg
Normal file
|
@ -0,0 +1,70 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px"
|
||||
height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<g id="Detailed_dial">
|
||||
<g id="Minutes">
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="10.744" y1="0.074" x2="10.885" y2="1.416"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="13.113" y1="22.599" x2="13.254" y2="23.942"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="9.504" y1="0.27" x2="9.784" y2="1.591"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="14.214" y1="22.425" x2="14.495" y2="23.745"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="8.29" y1="0.595" x2="8.708" y2="1.88"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="15.291" y1="22.136" x2="15.708" y2="23.42"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="7.118" y1="1.046" x2="7.667" y2="2.279"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="16.332" y1="21.737" x2="16.881" y2="22.97"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="4.945" y1="2.3" x2="5.739" y2="3.392"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="18.26" y1="20.623" x2="19.053" y2="21.716"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="3.969" y1="3.091" x2="4.873" y2="4.094"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="19.126" y1="19.922" x2="20.029" y2="20.925"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="3.081" y1="3.979" x2="4.084" y2="4.882"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="19.914" y1="19.133" x2="20.917" y2="20.037"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="2.291" y1="4.955" x2="3.383" y2="5.749"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="20.616" y1="18.267" x2="21.708" y2="19.061"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="1.036" y1="7.128" x2="2.27" y2="7.677"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="21.729" y1="16.339" x2="22.962" y2="16.888"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="0.586" y1="8.3" x2="1.871" y2="8.718"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="22.128" y1="15.298" x2="23.412" y2="15.715"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="0.261" y1="9.514" x2="1.582" y2="9.794"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="22.416" y1="14.221" x2="23.737" y2="14.502"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="0.065" y1="10.754" x2="1.408" y2="10.895"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="22.591" y1="13.12" x2="23.934" y2="13.261"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="0.065" y1="13.263" x2="1.408" y2="13.122"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="22.591" y1="10.894" x2="23.933" y2="10.753"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="0.262" y1="14.503" x2="1.582" y2="14.223"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="22.416" y1="9.793" x2="23.737" y2="9.512"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="0.587" y1="15.717" x2="1.871" y2="15.299"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="22.128" y1="8.716" x2="23.412" y2="8.299"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="1.037" y1="16.889" x2="2.271" y2="16.34"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="21.728" y1="7.676" x2="22.962" y2="7.126"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="2.291" y1="19.062" x2="3.384" y2="18.268"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="20.615" y1="5.747" x2="21.707" y2="4.954"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="3.082" y1="20.038" x2="4.085" y2="19.134"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="19.913" y1="4.881" x2="20.917" y2="3.978"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="3.97" y1="20.926" x2="4.874" y2="19.923"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="19.125" y1="4.093" x2="20.028" y2="3.09"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="4.946" y1="21.716" x2="5.74" y2="20.624"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="18.258" y1="3.392" x2="19.052" y2="2.299"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="7.119" y1="22.971" x2="7.668" y2="21.737"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="16.33" y1="2.278" x2="16.879" y2="1.045"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="8.292" y1="23.421" x2="8.709" y2="22.137"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="15.29" y1="1.879" x2="15.707" y2="0.595"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="9.505" y1="23.746" x2="9.786" y2="22.425"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="14.213" y1="1.591" x2="14.494" y2="0.27"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="10.746" y1="23.942" x2="10.887" y2="22.599"/>
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="13.112" y1="1.416" x2="13.253" y2="0.073"/>
|
||||
</g>
|
||||
<g id="Hours">
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="11.999" y1="0.008" x2="11.999" y2="2.708"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="11.999" y1="21.307" x2="11.999" y2="24.008"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="23.999" y1="12" x2="21.298" y2="12"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="2.699" y1="12" x2="-0.001" y2="12"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="5.999" y1="1.616" x2="7.349" y2="3.954"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="16.648" y1="20.061" x2="17.999" y2="22.4"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="22.387" y1="6.001" x2="20.048" y2="7.351"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="3.941" y1="16.651" x2="1.602" y2="18.001"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="1.606" y1="6.008" x2="3.945" y2="7.358"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="20.052" y1="16.658" x2="22.391" y2="18.008"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="17.992" y1="1.612" x2="16.642" y2="3.95"/>
|
||||
<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="0.1417" stroke-miterlimit="10" x1="7.342" y1="20.057" x2="5.992" y2="22.396"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 8 KiB |
38
_docs/icons/export_custom.svg
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?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 version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px"
|
||||
height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<g id="ExportCustom">
|
||||
<g>
|
||||
<path fill="#AAAAAA" d="M1.885,11.953c0,2.578,2.09,4.668,4.668,4.668s4.668-2.09,4.668-4.668c0-2.578-2.09-4.668-4.668-4.668
|
||||
S1.885,9.375,1.885,11.953z M3.093,11.953c0-1.911,1.549-3.46,3.46-3.46s3.46,1.549,3.46,3.46c0,1.911-1.549,3.46-3.46,3.46
|
||||
S3.093,13.864,3.093,11.953z"/>
|
||||
<polygon fill="#AAAAAA" points="4.273,4.976 8.833,4.976 9.43,8.314 3.676,8.314 "/>
|
||||
<polygon fill="#AAAAAA" points="4.273,19.024 8.833,19.024 9.43,15.686 3.676,15.686 "/>
|
||||
</g>
|
||||
<g>
|
||||
|
||||
<rect x="12.915" y="6.067" fill="none" stroke="#FFFFFF" stroke-width="0.4819" stroke-linejoin="round" stroke-miterlimit="10" width="9.405" height="11.865"/>
|
||||
<g>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.4819" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="21.172" y1="7.23" x2="14.064" y2="7.23"/>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.4819" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="21.172" y1="8.592" x2="14.064" y2="8.592"/>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.4819" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="21.172" y1="11.318" x2="14.064" y2="11.318"/>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.4819" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="21.172" y1="9.955" x2="14.064" y2="9.955"/>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.4819" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="21.172" y1="12.68" x2="14.064" y2="12.68"/>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.4819" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="21.172" y1="14.043" x2="14.064" y2="14.043"/>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.4819" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="21.172" y1="16.768" x2="14.064" y2="16.768"/>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.4819" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="21.172" y1="15.406" x2="14.064" y2="15.406"/>
|
||||
</g>
|
||||
</g>
|
||||
<polygon fill="#6AE86D" points="19.351,11.953 14.376,9.823 14.376,11.078 6.48,11.078 6.48,12.828 14.376,12.828 14.376,14.084
|
||||
"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.6 KiB |
38
_docs/icons/load_custom.svg
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?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 version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px"
|
||||
height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<g id="LoadCustom">
|
||||
<g>
|
||||
<path fill="#AAAAAA" d="M17.653,7.286c-2.578,0-4.668,2.09-4.668,4.668c0,2.578,2.09,4.668,4.668,4.668s4.668-2.09,4.668-4.668
|
||||
C22.321,9.375,20.231,7.286,17.653,7.286z M17.653,15.414c-1.911,0-3.46-1.549-3.46-3.46c0-1.911,1.549-3.46,3.46-3.46
|
||||
s3.46,1.549,3.46,3.46C21.113,13.864,19.564,15.414,17.653,15.414z"/>
|
||||
<polygon fill="#AAAAAA" points="19.932,4.976 15.373,4.976 14.776,8.314 20.53,8.314 "/>
|
||||
<polygon fill="#AAAAAA" points="19.932,19.024 15.373,19.024 14.776,15.686 20.53,15.686 "/>
|
||||
</g>
|
||||
<g>
|
||||
|
||||
<rect x="1.885" y="6.067" fill="none" stroke="#FFFFFF" stroke-width="0.4819" stroke-linejoin="round" stroke-miterlimit="10" width="9.405" height="11.865"/>
|
||||
<g>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.4819" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="3.034" y1="7.23" x2="10.142" y2="7.23"/>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.4819" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="3.034" y1="8.592" x2="10.142" y2="8.592"/>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.4819" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="3.034" y1="11.318" x2="10.142" y2="11.318"/>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.4819" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="3.034" y1="9.955" x2="10.142" y2="9.955"/>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.4819" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="3.034" y1="12.68" x2="10.142" y2="12.68"/>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.4819" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="3.034" y1="14.043" x2="10.142" y2="14.043"/>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.4819" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="3.034" y1="16.768" x2="10.142" y2="16.768"/>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.4819" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="3.034" y1="15.406" x2="10.142" y2="15.406"/>
|
||||
</g>
|
||||
</g>
|
||||
<polygon fill="#6AE86D" points="17.726,11.953 12.751,9.823 12.751,11.078 4.855,11.078 4.855,12.828 12.751,12.828
|
||||
12.751,14.084 "/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.6 KiB |
16
_docs/icons/send_custom.svg
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?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 version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px"
|
||||
height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<g id="SendCustom">
|
||||
<g>
|
||||
<path fill="#AAAAAA" d="M16.168,7.286c-2.578,0-4.668,2.09-4.668,4.668c0,2.578,2.09,4.668,4.668,4.668s4.668-2.09,4.668-4.668
|
||||
C20.836,9.375,18.746,7.286,16.168,7.286z M16.168,15.414c-1.911,0-3.46-1.549-3.46-3.46c0-1.911,1.549-3.46,3.46-3.46
|
||||
s3.46,1.549,3.46,3.46C19.628,13.864,18.079,15.414,16.168,15.414z"/>
|
||||
<polygon fill="#AAAAAA" points="18.448,4.976 13.888,4.976 13.291,8.314 19.045,8.314 "/>
|
||||
<polygon fill="#AAAAAA" points="18.448,19.024 13.888,19.024 13.291,15.686 19.045,15.686 "/>
|
||||
</g>
|
||||
<polygon fill="#6AE86D" points="16.241,11.953 11.266,9.823 11.266,11.078 3.37,11.078 3.37,12.828 11.266,12.828 11.266,14.084
|
||||
"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1 KiB |
20
_docs/icons/set_default.svg
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?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 version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="24px"
|
||||
height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
|
||||
<g id="SetDefault">
|
||||
<g>
|
||||
<path fill="#AAAAAA" d="M12.103,7.286c-2.578,0-4.668,2.09-4.668,4.668c0,2.578,2.09,4.668,4.668,4.668s4.668-2.09,4.668-4.668
|
||||
C16.771,9.375,14.681,7.286,12.103,7.286z M12.103,15.414c-1.911,0-3.46-1.549-3.46-3.46c0-1.911,1.549-3.46,3.46-3.46
|
||||
s3.46,1.549,3.46,3.46C15.563,13.864,14.014,15.414,12.103,15.414z"/>
|
||||
<polygon fill="#AAAAAA" points="14.383,4.976 9.823,4.976 9.226,8.314 14.98,8.314 "/>
|
||||
<polygon fill="#AAAAAA" points="14.383,19.024 9.823,19.024 9.226,15.686 14.98,15.686 "/>
|
||||
</g>
|
||||
<path fill="#FF1313" d="M13.768,11.999l1.886-1.886c0.081-0.081,0.081-0.213,0-0.294l-1.372-1.372
|
||||
c-0.078-0.078-0.216-0.078-0.294,0l-1.886,1.886l-1.886-1.886c-0.078-0.078-0.216-0.078-0.294,0L8.552,9.819
|
||||
C8.513,9.858,8.491,9.911,8.491,9.966c0,0.055,0.022,0.108,0.061,0.147l1.886,1.886l-1.886,1.886
|
||||
c-0.039,0.039-0.061,0.092-0.061,0.147c0,0.055,0.022,0.108,0.061,0.147l1.372,1.372c0.041,0.04,0.094,0.061,0.147,0.061
|
||||
c0.053,0,0.106-0.02,0.147-0.061l1.886-1.886l1.886,1.886c0.04,0.04,0.093,0.061,0.147,0.061c0.053,0,0.106-0.02,0.147-0.061
|
||||
l1.371-1.371c0.081-0.081,0.081-0.213,0-0.294L13.768,11.999z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -34,6 +34,7 @@ dependencies {
|
|||
api 'com.github.tony19:logback-android:2.0.0'
|
||||
|
||||
api "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinx_serialization_version"
|
||||
api "org.jetbrains.kotlinx:kotlinx-serialization-protobuf:$kotlinx_serialization_version"
|
||||
api "org.apache.commons:commons-lang3:$commonslang3_version"
|
||||
|
||||
//RxBus
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package info.nightscout.rx.events
|
||||
|
||||
import info.nightscout.rx.weardata.EventData
|
||||
|
||||
class EventMobileDataToWear(val payload: EventData.ActionSetCustomWatchface) : Event()
|
|
@ -0,0 +1,5 @@
|
|||
package info.nightscout.rx.events
|
||||
|
||||
import info.nightscout.rx.weardata.EventData
|
||||
|
||||
class EventWearCwfExported(val payload: EventData.ActionSetCustomWatchface): Event()
|
|
@ -0,0 +1,5 @@
|
|||
package info.nightscout.rx.events
|
||||
|
||||
import info.nightscout.rx.weardata.EventData
|
||||
|
||||
class EventWearDataToMobile(val payload: EventData) : Event()
|
|
@ -0,0 +1,5 @@
|
|||
package info.nightscout.rx.events
|
||||
|
||||
import info.nightscout.rx.weardata.CustomWatchfaceData
|
||||
|
||||
class EventWearUpdateGui(val customWatchfaceData: CustomWatchfaceData? = null, val exportFile: Boolean = false) : Event()
|
|
@ -0,0 +1,241 @@
|
|||
package info.nightscout.rx.weardata
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import info.nightscout.shared.R
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.json.JSONObject
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
import java.util.zip.ZipOutputStream
|
||||
|
||||
val CUSTOM_VERSION = "0.3"
|
||||
enum class CustomWatchfaceDrawableDataKey(val key: String, @DrawableRes val icon: Int?, val fileName: String) {
|
||||
UNKNOWN("unknown", null, "Unknown"),
|
||||
CUSTOM_WATCHFACE("customWatchface", R.drawable.watchface_custom, "CustomWatchface"),
|
||||
BACKGROUND("background", R.drawable.background, "Background"),
|
||||
COVERCHART("cover_chart", null, "CoverChart"),
|
||||
COVERPLATE("cover_plate", R.drawable.simplified_dial, "CoverPlate"),
|
||||
HOURHAND("hour_hand", R.drawable.hour_hand, "HourHand"),
|
||||
MINUTEHAND("minute_hand", R.drawable.minute_hand, "MinuteHand"),
|
||||
SECONDHAND("second_hand", R.drawable.second_hand, "SecondHand");
|
||||
|
||||
companion object {
|
||||
|
||||
private val keyToEnumMap = HashMap<String, CustomWatchfaceDrawableDataKey>()
|
||||
private val fileNameToEnumMap = HashMap<String, CustomWatchfaceDrawableDataKey>()
|
||||
|
||||
init {
|
||||
for (value in values()) keyToEnumMap[value.key] = value
|
||||
for (value in values()) fileNameToEnumMap[value.fileName] = value
|
||||
}
|
||||
|
||||
fun fromKey(key: String): CustomWatchfaceDrawableDataKey =
|
||||
if (keyToEnumMap.containsKey(key)) {
|
||||
keyToEnumMap[key] ?: UNKNOWN
|
||||
} else {
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
fun fromFileName(file: String): CustomWatchfaceDrawableDataKey =
|
||||
if (fileNameToEnumMap.containsKey(file.substringBeforeLast("."))) {
|
||||
fileNameToEnumMap[file.substringBeforeLast(".")] ?: UNKNOWN
|
||||
} else {
|
||||
UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum class DrawableFormat(val extension: String) {
|
||||
UNKNOWN(""),
|
||||
|
||||
//XML("xml"),
|
||||
//SVG("svg"),
|
||||
JPG("jpg"),
|
||||
PNG("png");
|
||||
|
||||
companion object {
|
||||
|
||||
private val extensionToEnumMap = HashMap<String, DrawableFormat>()
|
||||
|
||||
init {
|
||||
for (value in values()) extensionToEnumMap[value.extension] = value
|
||||
}
|
||||
|
||||
fun fromFileName(fileName: String): DrawableFormat =
|
||||
if (extensionToEnumMap.containsKey(fileName.substringAfterLast("."))) {
|
||||
extensionToEnumMap[fileName.substringAfterLast(".")] ?: UNKNOWN
|
||||
} else {
|
||||
UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class DrawableData(val value: ByteArray, val format: DrawableFormat) {
|
||||
|
||||
fun toDrawable(resources: Resources): Drawable? {
|
||||
try {
|
||||
return when (format) {
|
||||
DrawableFormat.PNG, DrawableFormat.JPG -> {
|
||||
val bitmap = BitmapFactory.decodeByteArray(value, 0, value.size)
|
||||
BitmapDrawable(resources, bitmap)
|
||||
}
|
||||
/*
|
||||
DrawableFormat.SVG -> {
|
||||
//TODO: include svg to Drawable convertor here
|
||||
null
|
||||
}
|
||||
DrawableFormat.XML -> {
|
||||
// Always return a null Drawable, even if xml file is a valid xml vector file
|
||||
val xmlInputStream = ByteArrayInputStream(value)
|
||||
val xmlPullParser = Xml.newPullParser()
|
||||
xmlPullParser.setInput(xmlInputStream, null)
|
||||
Drawable.createFromXml(resources, xmlPullParser)
|
||||
}
|
||||
*/
|
||||
else -> null
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typealias CustomWatchfaceDrawableDataMap = MutableMap<CustomWatchfaceDrawableDataKey, DrawableData>
|
||||
typealias CustomWatchfaceMetadataMap = MutableMap<CustomWatchfaceMetadataKey, String>
|
||||
|
||||
@Serializable
|
||||
data class CustomWatchfaceData(val json: String, var metadata: CustomWatchfaceMetadataMap, val drawableDatas: CustomWatchfaceDrawableDataMap)
|
||||
|
||||
enum class CustomWatchfaceMetadataKey(val key: String, @StringRes val label: Int) {
|
||||
|
||||
CWF_NAME("name", R.string.metadata_label_watchface_name),
|
||||
CWF_FILENAME("filename", R.string.metadata_wear_import_filename),
|
||||
CWF_AUTHOR("author", R.string.metadata_label_watchface_author),
|
||||
CWF_CREATED_AT("created_at", R.string.metadata_label_watchface_created_at),
|
||||
CWF_VERSION("cwf_version", R.string.metadata_label_watchface_version);
|
||||
|
||||
companion object {
|
||||
|
||||
private val keyToEnumMap = HashMap<String, CustomWatchfaceMetadataKey>()
|
||||
|
||||
init {
|
||||
for (value in values()) keyToEnumMap[value.key] = value
|
||||
}
|
||||
|
||||
fun fromKey(key: String): CustomWatchfaceMetadataKey? =
|
||||
if (keyToEnumMap.containsKey(key)) {
|
||||
keyToEnumMap[key]
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ZipWatchfaceFormat {
|
||||
companion object {
|
||||
|
||||
const val CUSTOM_WF_EXTENTION = ".zip"
|
||||
const val CUSTOM_JSON_FILE = "CustomWatchface.json"
|
||||
|
||||
fun loadCustomWatchface(cwfFile: File): CustomWatchfaceData? {
|
||||
var json = JSONObject()
|
||||
var metadata: CustomWatchfaceMetadataMap = mutableMapOf()
|
||||
val drawableDatas: CustomWatchfaceDrawableDataMap = mutableMapOf()
|
||||
|
||||
try {
|
||||
val zipInputStream = ZipInputStream(cwfFile.inputStream())
|
||||
var zipEntry: ZipEntry? = zipInputStream.nextEntry
|
||||
while (zipEntry != null) {
|
||||
val entryName = zipEntry.name
|
||||
|
||||
val buffer = ByteArray(2048)
|
||||
val byteArrayOutputStream = ByteArrayOutputStream()
|
||||
var count = zipInputStream.read(buffer)
|
||||
while (count != -1) {
|
||||
byteArrayOutputStream.write(buffer, 0, count)
|
||||
count = zipInputStream.read(buffer)
|
||||
}
|
||||
zipInputStream.closeEntry()
|
||||
|
||||
if (entryName == CUSTOM_JSON_FILE) {
|
||||
val jsonString = byteArrayOutputStream.toByteArray().toString(Charsets.UTF_8)
|
||||
json = JSONObject(jsonString)
|
||||
metadata = loadMetadata(json)
|
||||
metadata[CustomWatchfaceMetadataKey.CWF_FILENAME] = cwfFile.name
|
||||
} else {
|
||||
val customWatchfaceDrawableDataKey = CustomWatchfaceDrawableDataKey.fromFileName(entryName)
|
||||
val drawableFormat = DrawableFormat.fromFileName(entryName)
|
||||
if (customWatchfaceDrawableDataKey != CustomWatchfaceDrawableDataKey.UNKNOWN && drawableFormat != DrawableFormat.UNKNOWN) {
|
||||
drawableDatas[customWatchfaceDrawableDataKey] = DrawableData(byteArrayOutputStream.toByteArray(), drawableFormat)
|
||||
}
|
||||
}
|
||||
zipEntry = zipInputStream.nextEntry
|
||||
}
|
||||
|
||||
// Valid CWF file must contains a valid json file with a name within metadata and a custom watchface image
|
||||
if (metadata.containsKey(CustomWatchfaceMetadataKey.CWF_NAME) && drawableDatas.containsKey(CustomWatchfaceDrawableDataKey.CUSTOM_WATCHFACE))
|
||||
return CustomWatchfaceData(json.toString(4), metadata, drawableDatas)
|
||||
else
|
||||
return null
|
||||
|
||||
} catch (e: Exception) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
fun saveCustomWatchface(file: File, customWatchface: CustomWatchfaceData) {
|
||||
|
||||
try {
|
||||
val outputStream = FileOutputStream(file)
|
||||
val zipOutputStream = ZipOutputStream(BufferedOutputStream(outputStream))
|
||||
|
||||
// Ajouter le fichier JSON au ZIP
|
||||
val jsonEntry = ZipEntry(CUSTOM_JSON_FILE)
|
||||
zipOutputStream.putNextEntry(jsonEntry)
|
||||
zipOutputStream.write(customWatchface.json.toByteArray())
|
||||
zipOutputStream.closeEntry()
|
||||
|
||||
// Ajouter les fichiers divers au ZIP
|
||||
for (drawableData in customWatchface.drawableDatas) {
|
||||
val fileEntry = ZipEntry("${drawableData.key.fileName}.${drawableData.value.format.extension}")
|
||||
zipOutputStream.putNextEntry(fileEntry)
|
||||
zipOutputStream.write(drawableData.value.value)
|
||||
zipOutputStream.closeEntry()
|
||||
}
|
||||
zipOutputStream.close()
|
||||
outputStream.close()
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun loadMetadata(contents: JSONObject): CustomWatchfaceMetadataMap {
|
||||
val metadata: CustomWatchfaceMetadataMap = mutableMapOf()
|
||||
|
||||
if (contents.has("metadata")) {
|
||||
val meta = contents.getJSONObject("metadata")
|
||||
for (key in meta.keys()) {
|
||||
val metaKey = CustomWatchfaceMetadataKey.fromKey(key)
|
||||
if (metaKey != null) {
|
||||
metadata[metaKey] = meta.getString(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,8 +1,10 @@
|
|||
package info.nightscout.rx.weardata
|
||||
|
||||
import info.nightscout.rx.events.Event
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.protobuf.ProtoBuf
|
||||
import org.joda.time.DateTime
|
||||
import java.util.Objects
|
||||
|
||||
|
@ -13,13 +15,20 @@ sealed class EventData : Event() {
|
|||
|
||||
fun serialize() = Json.encodeToString(serializer(), this)
|
||||
|
||||
@ExperimentalSerializationApi
|
||||
fun serializeByte() = ProtoBuf.encodeToByteArray(serializer(), this)
|
||||
companion object {
|
||||
|
||||
fun deserialize(json: String) = try {
|
||||
Json.decodeFromString(serializer(), json)
|
||||
} catch (ignored: Exception) {
|
||||
Error(System.currentTimeMillis())
|
||||
}
|
||||
@ExperimentalSerializationApi
|
||||
fun deserializeByte(byteArray: ByteArray) = try {
|
||||
ProtoBuf.decodeFromByteArray(serializer(), byteArray)
|
||||
} catch (ignored: Exception) {
|
||||
Error(System.currentTimeMillis())
|
||||
}
|
||||
}
|
||||
|
||||
// Mobile <- Wear
|
||||
|
@ -142,6 +151,12 @@ sealed class EventData : Event() {
|
|||
@Serializable
|
||||
data class CancelNotification(val timeStamp: Long) : EventData()
|
||||
|
||||
@Serializable
|
||||
data class ActionGetCustomWatchface(
|
||||
val customWatchface: ActionSetCustomWatchface,
|
||||
val exportFile: Boolean = false
|
||||
) : EventData()
|
||||
|
||||
@Serializable
|
||||
data class ActionPing(val timeStamp: Long) : EventData()
|
||||
|
||||
|
@ -267,6 +282,16 @@ sealed class EventData : Event() {
|
|||
val validTo: Int
|
||||
) : EventData()
|
||||
}
|
||||
@Serializable
|
||||
data class ActionSetCustomWatchface(
|
||||
val customWatchfaceData: CustomWatchfaceData
|
||||
) : EventData()
|
||||
|
||||
@Serializable
|
||||
data class ActionrequestCustomWatchface(val exportFile: Boolean) : EventData()
|
||||
|
||||
@Serializable
|
||||
data class ActionrequestSetDefaultWatchface(val timeStamp: Long) : EventData()
|
||||
|
||||
@Serializable
|
||||
data class ActionProfileSwitchOpenActivity(val timeShift: Int, val percentage: Int) : EventData()
|
||||
|
|
|
@ -150,6 +150,10 @@ class DateUtil @Inject constructor(private val context: Context) {
|
|||
return DateTime(mills).toString(DateTimeFormat.forPattern(format))
|
||||
}
|
||||
|
||||
fun secondString(): String = secondString(now())
|
||||
fun secondString(mills: Long): String =
|
||||
DateTime(mills).toString(DateTimeFormat.forPattern("ss"))
|
||||
|
||||
fun minuteString(): String = minuteString(now())
|
||||
fun minuteString(mills: Long): String =
|
||||
DateTime(mills).toString(DateTimeFormat.forPattern("mm"))
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M0.103,0h24v24h-24z"/>
|
||||
</vector>
|
183
app-wear-shared/shared/src/main/res/drawable/detailed_dial.xml
Normal file
|
@ -0,0 +1,183 @@
|
|||
<vector android:height="400dp" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="400dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M10.744,0.074L10.885,1.416"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M13.113,22.599L13.254,23.942"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M9.504,0.27L9.784,1.591"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M14.214,22.425L14.495,23.745"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M8.29,0.595L8.708,1.88"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M15.291,22.136L15.708,23.42"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M7.118,1.046L7.667,2.279"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M16.332,21.737L16.881,22.97"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M4.945,2.3L5.739,3.392"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M18.26,20.623L19.053,21.716"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M3.969,3.091L4.873,4.094"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M19.126,19.922L20.029,20.925"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M3.081,3.979L4.084,4.882"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M19.914,19.133L20.917,20.037"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M2.291,4.955L3.383,5.749"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M20.616,18.267L21.708,19.061"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M1.036,7.128L2.27,7.677"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M21.729,16.339L22.962,16.888"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M0.586,8.3L1.871,8.718"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M22.128,15.298L23.412,15.715"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M0.261,9.514L1.582,9.794"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M22.416,14.221L23.737,14.502"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M0.065,10.754L1.408,10.895"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M22.591,13.12L23.934,13.261"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M0.065,13.263L1.408,13.122"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M22.591,10.894L23.933,10.753"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M0.262,14.503L1.582,14.223"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M22.416,9.793L23.737,9.512"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M0.587,15.717L1.871,15.299"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M22.128,8.716L23.412,8.299"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M1.037,16.889L2.271,16.34"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M21.728,7.676L22.962,7.126"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M2.291,19.062L3.384,18.268"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M20.615,5.747L21.707,4.954"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M3.082,20.038L4.085,19.134"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M19.913,4.881L20.917,3.978"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M3.97,20.926L4.874,19.923"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M19.125,4.093L20.028,3.09"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M4.946,21.716L5.74,20.624"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M18.258,3.392L19.052,2.299"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M7.119,22.971L7.668,21.737"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M16.33,2.278L16.879,1.045"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M8.292,23.421L8.709,22.137"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M15.29,1.879L15.707,0.595"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M9.505,23.746L9.786,22.425"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M14.213,1.591L14.494,0.27"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M10.746,23.942L10.887,22.599"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M13.112,1.416L13.253,0.073"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M11.999,0.008L11.999,2.708"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M11.999,21.307L11.999,24.008"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M23.999,12L21.298,12"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M2.699,12L-0.001,12"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M5.999,1.616L7.349,3.954"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M16.648,20.061L17.999,22.4"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M22.387,6.001L20.048,7.351"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M3.941,16.651L1.602,18.001"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M1.606,6.008L3.945,7.358"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M20.052,16.658L22.391,18.008"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M17.992,1.612L16.642,3.95"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M7.342,20.057L5.992,22.396"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
</vector>
|
|
@ -0,0 +1,4 @@
|
|||
<vector android:height="400dp" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="400dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FFFFFF" android:pathData="M11.999,4.906c-0.104,0 -0.188,0.084 -0.188,0.188V12c0,0.104 0.084,0.188 0.188,0.188c0.104,0 0.188,-0.084 0.188,-0.188V5.094C12.186,4.99 12.102,4.906 11.999,4.906zM11.999,12.094c-0.047,0 -0.086,-0.038 -0.086,-0.086s0.039,-0.086 0.086,-0.086c0.047,0 0.086,0.038 0.086,0.086S12.046,12.094 11.999,12.094z"/>
|
||||
</vector>
|
|
@ -0,0 +1,4 @@
|
|||
<vector android:height="400dp" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="400dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FFFFFF" android:pathData="M11.999,1.406c-0.104,0 -0.188,0.084 -0.188,0.188V12c0,0.104 0.084,0.188 0.188,0.188c0.104,0 0.188,-0.084 0.188,-0.188V1.594C12.186,1.49 12.102,1.406 11.999,1.406zM11.999,12.094c-0.047,0 -0.086,-0.038 -0.086,-0.086s0.039,-0.086 0.086,-0.086c0.047,0 0.086,0.038 0.086,0.086S12.046,12.094 11.999,12.094z"/>
|
||||
</vector>
|
|
@ -0,0 +1,4 @@
|
|||
<vector android:height="400dp" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="400dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF1313" android:pathData="M12.198,11.462v-0.237c0,-0.077 -0.05,-0.131 -0.114,-0.164V0.508c0,-0.047 -0.038,-0.086 -0.086,-0.086c-0.047,0 -0.086,0.038 -0.086,0.086v10.553c-0.063,0.033 -0.114,0.087 -0.114,0.164v0.238c-0.219,0.082 -0.376,0.29 -0.376,0.537s0.157,0.455 0.376,0.537v0.92c0,0.11 0.089,0.2 0.2,0.2s0.2,-0.089 0.2,-0.2v-0.919c0.221,-0.081 0.381,-0.289 0.381,-0.538S12.419,11.543 12.198,11.462zM11.999,12.094c-0.047,0 -0.086,-0.038 -0.086,-0.086s0.039,-0.086 0.086,-0.086c0.047,0 0.086,0.038 0.086,0.086S12.046,12.094 11.999,12.094z"/>
|
||||
</vector>
|
|
@ -0,0 +1,39 @@
|
|||
<vector android:height="400dp" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="400dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M11.999,0.008L11.999,2.708"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M11.999,21.307L11.999,24.008"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M23.999,12L21.298,12"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M2.699,12L-0.001,12"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M5.999,1.616L7.349,3.954"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M16.648,20.061L17.999,22.4"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M22.387,6.001L20.048,7.351"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M3.941,16.651L1.602,18.001"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M1.606,6.008L3.945,7.358"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M20.052,16.658L22.391,18.008"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M17.992,1.612L16.642,3.95"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
<path android:fillColor="#FFFFFF"
|
||||
android:pathData="M7.342,20.057L5.992,22.396"
|
||||
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
|
||||
</vector>
|
After Width: | Height: | Size: 41 KiB |
|
@ -39,4 +39,12 @@
|
|||
<string name="disconnecting">Disconnecting</string>
|
||||
<string name="waiting_for_disconnection">Waiting for disconnection</string>
|
||||
|
||||
<!-- Custom Watchface -->
|
||||
<string name="metadata_label_watchface_created_at">Created at: %1$s</string>
|
||||
<string name="metadata_label_watchface_author">Author: %1$s</string>
|
||||
<string name="metadata_label_watchface_name">Name: %1$s</string>
|
||||
<string name="metadata_wear_import_filename">File name: %1$s</string>
|
||||
<string name="metadata_label_watchface_version">Watchface version: %1$s</string>
|
||||
<string name="wear_default_watchface">Default Watchface</string>
|
||||
|
||||
</resources>
|
|
@ -1,4 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="path_rx_bridge" translatable="false">/rx_bridge</string>
|
||||
<string name="path_rx_data_bridge" translatable="false">/rx_data_bridge</string>
|
||||
</resources>
|
|
@ -2,12 +2,16 @@ package info.nightscout.interfaces.maintenance
|
|||
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import info.nightscout.rx.weardata.CustomWatchfaceData
|
||||
|
||||
interface ImportExportPrefs {
|
||||
|
||||
fun importSharedPreferences(activity: FragmentActivity, importFile: PrefsFile)
|
||||
fun importSharedPreferences(activity: FragmentActivity)
|
||||
fun importSharedPreferences(fragment: Fragment)
|
||||
fun importCustomWatchface(activity: FragmentActivity)
|
||||
fun importCustomWatchface(fragment: Fragment)
|
||||
fun exportCustomWatchface(customWatchface: CustomWatchfaceData)
|
||||
fun prefsFileExists(): Boolean
|
||||
fun verifyStoragePermissions(fragment: Fragment, onGranted: Runnable)
|
||||
fun exportSharedPreferences(f: Fragment)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package info.nightscout.interfaces.maintenance
|
||||
|
||||
import info.nightscout.rx.weardata.CustomWatchfaceData
|
||||
import java.io.File
|
||||
|
||||
interface PrefFileListProvider {
|
||||
|
@ -10,7 +11,9 @@ interface PrefFileListProvider {
|
|||
fun ensureExtraDirExists(): File
|
||||
fun newExportFile(): File
|
||||
fun newExportCsvFile(): File
|
||||
fun newCwfFile(filename: String): File
|
||||
fun listPreferenceFiles(loadMetadata: Boolean = false): MutableList<PrefsFile>
|
||||
fun listCustomWatchfaceFiles(): MutableList<CustomWatchfaceData>
|
||||
fun checkMetadata(metadata: Map<PrefsMetadataKey, PrefMetadata>): Map<PrefsMetadataKey, PrefMetadata>
|
||||
fun formatExportedAgo(utcTime: String): String
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
|
|
|
@ -18,6 +18,10 @@
|
|||
android:name=".maintenance.activities.PrefImportListActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/AppTheme" />
|
||||
<activity
|
||||
android:name=".maintenance.activities.CustomWatchfaceImportListActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/AppTheme" />
|
||||
<activity
|
||||
android:name="info.nightscout.configuration.activities.SingleFragmentActivity"
|
||||
android:exported="false"
|
||||
|
|
|
@ -8,6 +8,7 @@ import androidx.activity.result.contract.ActivityResultContracts
|
|||
import androidx.core.app.ActivityCompat
|
||||
import dagger.android.support.DaggerAppCompatActivity
|
||||
import info.nightscout.configuration.R
|
||||
import info.nightscout.configuration.maintenance.CustomWatchfaceFileContract
|
||||
import info.nightscout.configuration.maintenance.PrefsFileContract
|
||||
import info.nightscout.core.ui.dialogs.OKDialog
|
||||
import info.nightscout.core.ui.locale.LocaleHelper
|
||||
|
@ -55,6 +56,8 @@ open class DaggerAppCompatActivityWithResult : DaggerAppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
val callForCustomWatchfaceFile = registerForActivityResult(CustomWatchfaceFileContract()) { }
|
||||
|
||||
val callForBatteryOptimization = registerForActivityResult(OptimizationPermissionContract()) {
|
||||
updateButtons()
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import info.nightscout.configuration.configBuilder.RunningConfigurationImpl
|
|||
import info.nightscout.configuration.maintenance.ImportExportPrefsImpl
|
||||
import info.nightscout.configuration.maintenance.MaintenanceFragment
|
||||
import info.nightscout.configuration.maintenance.PrefFileListProviderImpl
|
||||
import info.nightscout.configuration.maintenance.activities.CustomWatchfaceImportListActivity
|
||||
import info.nightscout.configuration.maintenance.activities.LogSettingActivity
|
||||
import info.nightscout.configuration.maintenance.activities.PrefImportListActivity
|
||||
import info.nightscout.configuration.maintenance.formats.EncryptedPrefsFormat
|
||||
|
@ -34,6 +35,7 @@ abstract class ConfigurationModule {
|
|||
@ContributesAndroidInjector abstract fun contributesConfigBuilderFragment(): ConfigBuilderFragment
|
||||
@ContributesAndroidInjector abstract fun contributesCsvExportWorker(): ImportExportPrefsImpl.CsvExportWorker
|
||||
@ContributesAndroidInjector abstract fun contributesPrefImportListActivity(): PrefImportListActivity
|
||||
@ContributesAndroidInjector abstract fun contributesCustomWatchfaceImportListActivity(): CustomWatchfaceImportListActivity
|
||||
@ContributesAndroidInjector abstract fun encryptedPrefsFormatInjector(): EncryptedPrefsFormat
|
||||
@ContributesAndroidInjector abstract fun prefImportListProviderInjector(): PrefFileListProvider
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package info.nightscout.configuration.maintenance
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.contract.ActivityResultContract
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
|
||||
class CustomWatchfaceFileContract: ActivityResultContract<Void?, Unit?>() {
|
||||
|
||||
companion object {
|
||||
const val OUTPUT_PARAM = "custom_file"
|
||||
}
|
||||
|
||||
override fun parseResult(resultCode: Int, intent: Intent?): Unit? {
|
||||
return when (resultCode) {
|
||||
FragmentActivity.RESULT_OK -> Unit
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun createIntent(context: Context, input: Void?): Intent {
|
||||
return Intent(context, info.nightscout.configuration.maintenance.activities.CustomWatchfaceImportListActivity::class.java)
|
||||
}
|
||||
}
|
|
@ -55,6 +55,9 @@ import info.nightscout.rx.events.EventAppExit
|
|||
import info.nightscout.rx.events.EventDiaconnG8PumpLogReset
|
||||
import info.nightscout.rx.logging.AAPSLogger
|
||||
import info.nightscout.rx.logging.LTag
|
||||
import info.nightscout.rx.weardata.CustomWatchfaceData
|
||||
import info.nightscout.rx.weardata.CustomWatchfaceMetadataKey
|
||||
import info.nightscout.rx.weardata.ZipWatchfaceFormat
|
||||
import info.nightscout.shared.interfaces.ResourceHelper
|
||||
import info.nightscout.shared.sharedPreferences.SP
|
||||
import info.nightscout.shared.utils.DateUtil
|
||||
|
@ -297,6 +300,27 @@ class ImportExportPrefsImpl @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun importCustomWatchface(fragment: Fragment) {
|
||||
fragment.activity?.let { importCustomWatchface(it) }
|
||||
}
|
||||
override fun importCustomWatchface(activity: FragmentActivity) {
|
||||
try {
|
||||
if (activity is DaggerAppCompatActivityWithResult)
|
||||
activity.callForCustomWatchfaceFile.launch(null)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
// this exception happens on some early implementations of ActivityResult contracts
|
||||
// when registered and called for the second time
|
||||
ToastUtils.errorToast(activity, rh.gs(R.string.goto_main_try_again))
|
||||
log.error(LTag.CORE, "Internal android framework exception", e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun exportCustomWatchface(customWatchface: CustomWatchfaceData) {
|
||||
prefFileList.ensureExportDirExists()
|
||||
val newFile = prefFileList.newCwfFile(customWatchface.metadata[CustomWatchfaceMetadataKey.CWF_FILENAME] ?:"")
|
||||
ZipWatchfaceFormat.saveCustomWatchface(newFile, customWatchface)
|
||||
}
|
||||
|
||||
override fun importSharedPreferences(activity: FragmentActivity, importFile: PrefsFile) {
|
||||
|
||||
askToConfirmImport(activity, importFile) { password ->
|
||||
|
|
|
@ -17,6 +17,8 @@ import info.nightscout.interfaces.maintenance.PrefsMetadataKey
|
|||
import info.nightscout.interfaces.maintenance.PrefsStatus
|
||||
import info.nightscout.interfaces.storage.Storage
|
||||
import info.nightscout.interfaces.versionChecker.VersionCheckerUtils
|
||||
import info.nightscout.rx.weardata.CustomWatchfaceData
|
||||
import info.nightscout.rx.weardata.ZipWatchfaceFormat
|
||||
import info.nightscout.shared.interfaces.ResourceHelper
|
||||
import org.joda.time.DateTime
|
||||
import org.joda.time.Days
|
||||
|
@ -88,6 +90,20 @@ class PrefFileListProviderImpl @Inject constructor(
|
|||
return prefFiles
|
||||
}
|
||||
|
||||
override fun listCustomWatchfaceFiles(): MutableList<CustomWatchfaceData> {
|
||||
val customWatchfaceFiles = mutableListOf<CustomWatchfaceData>()
|
||||
|
||||
// searching dedicated dir, only for new CWF format
|
||||
exportsPath.walk().filter { it.isFile && it.name.endsWith(ZipWatchfaceFormat.CUSTOM_WF_EXTENTION) }.forEach { file ->
|
||||
// Here loadCustomWatchface will unzip, check and load CustomWatchface
|
||||
ZipWatchfaceFormat.loadCustomWatchface(file)?.also { customWatchface ->
|
||||
customWatchfaceFiles.add(customWatchface)
|
||||
}
|
||||
}
|
||||
|
||||
return customWatchfaceFiles
|
||||
}
|
||||
|
||||
private fun metadataFor(loadMetadata: Boolean, contents: String): PrefMetadataMap {
|
||||
if (!loadMetadata) {
|
||||
return mapOf()
|
||||
|
@ -128,6 +144,10 @@ class PrefFileListProviderImpl @Inject constructor(
|
|||
val timeLocal = LocalDateTime.now().toString(DateTimeFormat.forPattern("yyyy-MM-dd'_'HHmmss"))
|
||||
return File(exportsPath, timeLocal + "_UserEntry.csv")
|
||||
}
|
||||
override fun newCwfFile(filename: String): File {
|
||||
val timeLocal = LocalDateTime.now().toString(DateTimeFormat.forPattern("yyyy-MM-dd'_'HHmmss"))
|
||||
return File(exportsPath, "${filename}_$timeLocal${ZipWatchfaceFormat.CUSTOM_WF_EXTENTION}")
|
||||
}
|
||||
|
||||
// check metadata for known issues, change their status and add info with explanations
|
||||
override fun checkMetadata(metadata: Map<PrefsMetadataKey, PrefMetadata>): Map<PrefsMetadataKey, PrefMetadata> {
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
package info.nightscout.configuration.maintenance.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.ViewGroup
|
||||
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import info.nightscout.core.ui.activities.TranslatedDaggerAppCompatActivity
|
||||
import info.nightscout.interfaces.maintenance.PrefFileListProvider
|
||||
import info.nightscout.configuration.databinding.CustomWatchfaceImportListActivityBinding
|
||||
import info.nightscout.configuration.R
|
||||
import info.nightscout.configuration.databinding.CustomWatchfaceImportListItemBinding
|
||||
import info.nightscout.interfaces.versionChecker.VersionCheckerUtils
|
||||
import info.nightscout.rx.bus.RxBus
|
||||
import info.nightscout.rx.events.EventMobileDataToWear
|
||||
import info.nightscout.rx.logging.AAPSLogger
|
||||
import info.nightscout.rx.weardata.CUSTOM_VERSION
|
||||
import info.nightscout.rx.weardata.CustomWatchfaceData
|
||||
import info.nightscout.rx.weardata.CustomWatchfaceDrawableDataKey
|
||||
import info.nightscout.rx.weardata.CustomWatchfaceMetadataKey.*
|
||||
import info.nightscout.rx.weardata.CustomWatchfaceMetadataMap
|
||||
import info.nightscout.rx.weardata.EventData
|
||||
import info.nightscout.shared.interfaces.ResourceHelper
|
||||
import info.nightscout.shared.sharedPreferences.SP
|
||||
import javax.inject.Inject
|
||||
|
||||
class CustomWatchfaceImportListActivity: TranslatedDaggerAppCompatActivity() {
|
||||
|
||||
@Inject lateinit var rh: ResourceHelper
|
||||
@Inject lateinit var sp: SP
|
||||
@Inject lateinit var prefFileListProvider: PrefFileListProvider
|
||||
@Inject lateinit var rxBus: RxBus
|
||||
@Inject lateinit var aapsLogger: AAPSLogger
|
||||
@Inject lateinit var versionCheckerUtils: VersionCheckerUtils
|
||||
|
||||
private lateinit var binding: CustomWatchfaceImportListActivityBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = CustomWatchfaceImportListActivityBinding.inflate(layoutInflater)
|
||||
val view = binding.root
|
||||
setContentView(view)
|
||||
|
||||
title = rh.gs(R.string.wear_import_custom_watchface_title)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||
supportActionBar?.setDisplayShowTitleEnabled(true)
|
||||
|
||||
binding.recyclerview.layoutManager = LinearLayoutManager(this)
|
||||
binding.recyclerview.adapter = RecyclerViewAdapter(prefFileListProvider.listCustomWatchfaceFiles())
|
||||
}
|
||||
|
||||
inner class RecyclerViewAdapter internal constructor(private var customWatchfaceFileList: List<CustomWatchfaceData>) : RecyclerView.Adapter<RecyclerViewAdapter.PrefFileViewHolder>() {
|
||||
|
||||
inner class PrefFileViewHolder(val customWatchfaceImportListItemBinding: CustomWatchfaceImportListItemBinding) : RecyclerView.ViewHolder(customWatchfaceImportListItemBinding.root) {
|
||||
|
||||
init {
|
||||
with(customWatchfaceImportListItemBinding) {
|
||||
root.isClickable = true
|
||||
customWatchfaceImportListItemBinding.root.setOnClickListener {
|
||||
val customWatchfaceFile = filelistName.tag as CustomWatchfaceData
|
||||
val customWF = EventData.ActionSetCustomWatchface(customWatchfaceFile)
|
||||
val i = Intent()
|
||||
setResult(FragmentActivity.RESULT_OK, i)
|
||||
rxBus.send(EventMobileDataToWear(customWF))
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PrefFileViewHolder {
|
||||
val binding = CustomWatchfaceImportListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
return PrefFileViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return customWatchfaceFileList.size
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: PrefFileViewHolder, position: Int) {
|
||||
val customWatchfaceFile = customWatchfaceFileList[position]
|
||||
val metadata = customWatchfaceFile.metadata
|
||||
val drawable = customWatchfaceFile.drawableDatas[CustomWatchfaceDrawableDataKey
|
||||
.CUSTOM_WATCHFACE]?.toDrawable(resources)
|
||||
with(holder.customWatchfaceImportListItemBinding) {
|
||||
filelistName.text = rh.gs(info.nightscout.shared.R.string.metadata_wear_import_filename, metadata[CWF_FILENAME])
|
||||
filelistName.tag = customWatchfaceFile
|
||||
customWatchface.setImageDrawable(drawable)
|
||||
customName.text = rh.gs(CWF_NAME.label, metadata[CWF_NAME])
|
||||
author.text = rh.gs(CWF_AUTHOR.label, metadata[CWF_AUTHOR] ?:"")
|
||||
createdAt.text = rh.gs(CWF_CREATED_AT.label, metadata[CWF_CREATED_AT] ?:"")
|
||||
cwfVersion.text = rh.gs(CWF_VERSION.label, metadata[CWF_VERSION] ?:"")
|
||||
val colorAttr = if (checkCustomVersion(metadata)) info.nightscout.core.ui.R.attr.metadataTextOkColor else info.nightscout.core.ui.R.attr.metadataTextWarningColor
|
||||
cwfVersion.setTextColor(rh.gac(cwfVersion.context, colorAttr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
finish()
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun checkCustomVersion(metadata: CustomWatchfaceMetadataMap): Boolean {
|
||||
metadata[CWF_VERSION]?.let { version ->
|
||||
val currentAppVer = versionCheckerUtils.versionDigits(CUSTOM_VERSION)
|
||||
val metadataVer = versionCheckerUtils.versionDigits(version)
|
||||
//Only check that Loaded Watchface version is lower or equal to Wear CustomWatchface version
|
||||
return ((currentAppVer.size >= 2) && (metadataVer.size >= 2) && (currentAppVer[0] >= metadataVer[0]))
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="4dp"
|
||||
android:orientation="vertical"
|
||||
tools:context="info.nightscout.configuration.maintenance.activities.CustomWatchfaceImportListActivity">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fadeScrollbars="true"
|
||||
android:scrollbarStyle="outsideOverlay"
|
||||
android:scrollbars="vertical">
|
||||
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
|
||||
</FrameLayout>
|
|
@ -0,0 +1,125 @@
|
|||
<?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"
|
||||
android:id="@+id/careportal_cardview"
|
||||
style="@style/Widget.MaterialComponents.CardView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
app:cardCornerRadius="4dp"
|
||||
app:contentPadding="2dp"
|
||||
app:cardElevation="2dp"
|
||||
app:cardUseCompatPadding="false"
|
||||
android:layout_gravity="center">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="1dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="3dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/custom_watchface"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:src="@drawable/watchface_custom"
|
||||
android:contentDescription="@string/a11y_file" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_vertical"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/custom_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:ellipsize="none"
|
||||
android:maxLines="2"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:scrollHorizontally="false"
|
||||
android:text="Default Custom Watchface"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textColor="?attr/importListFileNameColor"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/filelist_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:ellipsize="none"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:scrollHorizontally="false"
|
||||
android:text="Filename"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?attr/importListFileNameColor"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/author"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:ellipsize="none"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:scrollHorizontally="false"
|
||||
android:text="Author: Name"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?attr/importListFileNameColor"
|
||||
android:textSize="11sp"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/created_at"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:text="created at: lqkjdshflqkdjhflqdskfhlqdsf"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?attr/importListFileNameColor"
|
||||
android:textSize="11sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/cwf_version"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:text="CWF version:"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?attr/importListFileNameColor"
|
||||
android:textSize="11sp" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
|
||||
|
|
@ -164,6 +164,9 @@
|
|||
<string name="prefdecrypt_issue_wrong_format">Missing encryption configuration, settings format is invalid!</string>
|
||||
<string name="prefdecrypt_issue_wrong_algorithm">Unsupported or not specified encryption algorithm!</string>
|
||||
|
||||
<!-- Custom Watchface -->
|
||||
<string name="wear_import_custom_watchface_title">Select Custom Watchface</string>
|
||||
|
||||
<!-- Permissions -->
|
||||
<string name="alert_dialog_storage_permission_text">Please reboot your phone or restart AAPS from the System Settings \notherwise Android APS will not have logging (important to track and verify that the algorithms are working correctly)!</string>
|
||||
|
||||
|
|
|
@ -51,6 +51,15 @@
|
|||
android:pathPrefix="@string/path_rx_bridge"
|
||||
android:scheme="wear" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<!-- listeners receive events that match the action and data filters -->
|
||||
<action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
|
||||
|
||||
<data
|
||||
android:host="*"
|
||||
android:pathPrefix="@string/path_rx_data_bridge"
|
||||
android:scheme="wear" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
</application>
|
||||
|
|
|
@ -5,13 +5,24 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import dagger.android.support.DaggerFragment
|
||||
import info.nightscout.core.ui.toast.ToastUtils
|
||||
import info.nightscout.core.utils.fabric.FabricPrivacy
|
||||
import info.nightscout.interfaces.maintenance.ImportExportPrefs
|
||||
import info.nightscout.plugins.R
|
||||
import info.nightscout.plugins.databinding.WearFragmentBinding
|
||||
import info.nightscout.plugins.general.wear.events.EventWearUpdateGui
|
||||
import info.nightscout.rx.AapsSchedulers
|
||||
import info.nightscout.rx.bus.RxBus
|
||||
import info.nightscout.rx.events.EventMobileDataToWear
|
||||
import info.nightscout.rx.events.EventMobileToWear
|
||||
import info.nightscout.rx.events.EventWearUpdateGui
|
||||
import info.nightscout.rx.logging.AAPSLogger
|
||||
import info.nightscout.rx.weardata.CustomWatchfaceData
|
||||
import info.nightscout.rx.weardata.CustomWatchfaceDrawableDataKey
|
||||
import info.nightscout.rx.weardata.CustomWatchfaceMetadataKey
|
||||
import info.nightscout.rx.weardata.EventData
|
||||
import info.nightscout.shared.extensions.toVisibility
|
||||
import info.nightscout.shared.interfaces.ResourceHelper
|
||||
import info.nightscout.shared.sharedPreferences.SP
|
||||
import info.nightscout.shared.utils.DateUtil
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||
|
@ -24,9 +35,12 @@ class WearFragment : DaggerFragment() {
|
|||
@Inject lateinit var aapsSchedulers: AapsSchedulers
|
||||
@Inject lateinit var fabricPrivacy: FabricPrivacy
|
||||
@Inject lateinit var dateUtil: DateUtil
|
||||
@Inject lateinit var importExportPrefs: ImportExportPrefs
|
||||
@Inject lateinit var sp:SP
|
||||
@Inject lateinit var rh: ResourceHelper
|
||||
@Inject lateinit var aapsLogger: AAPSLogger
|
||||
|
||||
private var _binding: WearFragmentBinding? = null
|
||||
|
||||
private val disposable = CompositeDisposable()
|
||||
|
||||
// This property is only valid between onCreateView and
|
||||
|
@ -43,6 +57,20 @@ class WearFragment : DaggerFragment() {
|
|||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.resend.setOnClickListener { rxBus.send(EventData.ActionResendData("WearFragment")) }
|
||||
binding.openSettings.setOnClickListener { rxBus.send(EventMobileToWear(EventData.OpenSettings(dateUtil.now()))) }
|
||||
|
||||
binding.loadCustom.setOnClickListener {
|
||||
importExportPrefs.verifyStoragePermissions(this) {
|
||||
importExportPrefs.importCustomWatchface(this)
|
||||
}
|
||||
}
|
||||
binding.defaultCustom.setOnClickListener {
|
||||
rxBus.send(EventMobileToWear(EventData.ActionrequestSetDefaultWatchface(dateUtil.now())))
|
||||
updateGui()
|
||||
}
|
||||
binding.exportCustom.setOnClickListener {
|
||||
wearPlugin.savedCustomWatchface?.let { importExportPrefs.exportCustomWatchface(it) }
|
||||
?: apply { rxBus.send(EventMobileToWear(EventData.ActionrequestCustomWatchface(true)))}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
@ -50,7 +78,15 @@ class WearFragment : DaggerFragment() {
|
|||
disposable += rxBus
|
||||
.toObservable(EventWearUpdateGui::class.java)
|
||||
.observeOn(aapsSchedulers.main)
|
||||
.subscribe({ updateGui() }, fabricPrivacy::logException)
|
||||
.subscribe({
|
||||
it.customWatchfaceData?.let { loadCustom(it) }
|
||||
if (it.exportFile)
|
||||
ToastUtils.okToast(activity, rh.gs(R.string.wear_new_custom_watchface_exported))
|
||||
updateGui()
|
||||
}, fabricPrivacy::logException)
|
||||
if (wearPlugin.savedCustomWatchface == null)
|
||||
rxBus.send(EventMobileToWear(EventData.ActionrequestCustomWatchface(false)))
|
||||
//EventMobileDataToWear
|
||||
updateGui()
|
||||
}
|
||||
|
||||
|
@ -67,6 +103,18 @@ class WearFragment : DaggerFragment() {
|
|||
|
||||
private fun updateGui() {
|
||||
_binding ?: return
|
||||
wearPlugin.savedCustomWatchface?.let {
|
||||
binding.customName.text = rh.gs(R.string.wear_custom_watchface, it.metadata[CustomWatchfaceMetadataKey.CWF_NAME])
|
||||
binding.coverChart.setImageDrawable(it.drawableDatas[CustomWatchfaceDrawableDataKey.CUSTOM_WATCHFACE]?.toDrawable(resources))
|
||||
} ?:apply {
|
||||
binding.customName.text = rh.gs(R.string.wear_custom_watchface, rh.gs(info.nightscout.shared.R.string.wear_default_watchface))
|
||||
binding.coverChart.setImageDrawable(null)
|
||||
}
|
||||
binding.connectedDevice.text = wearPlugin.connectedDevice
|
||||
binding.customWatchfaceLayout.visibility = (wearPlugin.connectedDevice != rh.gs(R.string.no_watch_connected)).toVisibility()
|
||||
}
|
||||
|
||||
private fun loadCustom(cwf: CustomWatchfaceData) {
|
||||
wearPlugin.savedCustomWatchface = cwf
|
||||
}
|
||||
}
|
|
@ -17,7 +17,9 @@ import info.nightscout.rx.events.EventLoopUpdateGui
|
|||
import info.nightscout.rx.events.EventMobileToWear
|
||||
import info.nightscout.rx.events.EventOverviewBolusProgress
|
||||
import info.nightscout.rx.events.EventPreferenceChange
|
||||
import info.nightscout.rx.events.EventWearUpdateGui
|
||||
import info.nightscout.rx.logging.AAPSLogger
|
||||
import info.nightscout.rx.weardata.CustomWatchfaceData
|
||||
import info.nightscout.rx.weardata.EventData
|
||||
import info.nightscout.shared.interfaces.ResourceHelper
|
||||
import info.nightscout.shared.sharedPreferences.SP
|
||||
|
@ -54,6 +56,7 @@ class WearPlugin @Inject constructor(
|
|||
private val disposable = CompositeDisposable()
|
||||
|
||||
var connectedDevice = "---"
|
||||
var savedCustomWatchface: CustomWatchfaceData? = null
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
|
@ -89,6 +92,10 @@ class WearPlugin @Inject constructor(
|
|||
.toObservable(EventLoopUpdateGui::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ dataHandlerMobile.resendData("EventLoopUpdateGui") }, fabricPrivacy::logException)
|
||||
disposable += rxBus
|
||||
.toObservable(EventWearUpdateGui::class.java)
|
||||
.observeOn(aapsSchedulers.main)
|
||||
.subscribe({ it.customWatchfaceData?.let { cwf -> savedCustomWatchface = cwf } }, fabricPrivacy::logException)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
package info.nightscout.plugins.general.wear.events
|
||||
|
||||
import info.nightscout.rx.events.Event
|
||||
|
||||
class EventWearUpdateGui : Event()
|
|
@ -41,6 +41,7 @@ import info.nightscout.interfaces.iob.GlucoseStatusProvider
|
|||
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
|
||||
import info.nightscout.interfaces.iob.IobCobCalculator
|
||||
import info.nightscout.interfaces.logging.UserEntryLogger
|
||||
import info.nightscout.interfaces.maintenance.ImportExportPrefs
|
||||
import info.nightscout.interfaces.nsclient.ProcessedDeviceStatusData
|
||||
import info.nightscout.interfaces.plugin.ActivePlugin
|
||||
import info.nightscout.interfaces.plugin.PluginBase
|
||||
|
@ -59,6 +60,7 @@ import info.nightscout.plugins.R
|
|||
import info.nightscout.rx.AapsSchedulers
|
||||
import info.nightscout.rx.bus.RxBus
|
||||
import info.nightscout.rx.events.EventMobileToWear
|
||||
import info.nightscout.rx.events.EventWearUpdateGui
|
||||
import info.nightscout.rx.logging.AAPSLogger
|
||||
import info.nightscout.rx.logging.LTag
|
||||
import info.nightscout.rx.weardata.EventData
|
||||
|
@ -107,7 +109,8 @@ class DataHandlerMobile @Inject constructor(
|
|||
private val commandQueue: CommandQueue,
|
||||
private val fabricPrivacy: FabricPrivacy,
|
||||
private val uiInteraction: UiInteraction,
|
||||
private val persistenceLayer: PersistenceLayer
|
||||
private val persistenceLayer: PersistenceLayer,
|
||||
private val importExportPrefs: ImportExportPrefs
|
||||
) {
|
||||
|
||||
private val disposable = CompositeDisposable()
|
||||
|
@ -314,6 +317,13 @@ class DataHandlerMobile @Inject constructor(
|
|||
.toObservable(EventData.ActionHeartRate::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ handleHeartRate(it) }, fabricPrivacy::logException)
|
||||
disposable += rxBus
|
||||
.toObservable(EventData.ActionGetCustomWatchface::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({
|
||||
aapsLogger.debug(LTag.WEAR, "Custom Watch face ${it.customWatchface} received from ${it.sourceNodeId}")
|
||||
handleGetCustomWatchface(it)
|
||||
}, fabricPrivacy::logException)
|
||||
}
|
||||
|
||||
private fun handleTddStatus() {
|
||||
|
@ -1247,4 +1257,15 @@ class DataHandlerMobile @Inject constructor(
|
|||
device = actionHeartRate.device)
|
||||
repository.runTransaction(InsertOrUpdateHeartRateTransaction(hr)).blockingAwait()
|
||||
}
|
||||
|
||||
|
||||
private fun handleGetCustomWatchface(command: EventData.ActionGetCustomWatchface) {
|
||||
val customWatchface = command.customWatchface
|
||||
aapsLogger.debug(LTag.WEAR, "Custom Watchface received from ${command.sourceNodeId}: ${customWatchface.customWatchfaceData.json}")
|
||||
rxBus.send(EventWearUpdateGui(customWatchface.customWatchfaceData, command.exportFile))
|
||||
if (command.exportFile)
|
||||
importExportPrefs.exportCustomWatchface(customWatchface.customWatchfaceData)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,9 +26,10 @@ import info.nightscout.interfaces.profile.ProfileFunction
|
|||
import info.nightscout.interfaces.receivers.ReceiverStatusStore
|
||||
import info.nightscout.plugins.R
|
||||
import info.nightscout.plugins.general.wear.WearPlugin
|
||||
import info.nightscout.plugins.general.wear.events.EventWearUpdateGui
|
||||
import info.nightscout.rx.events.EventWearUpdateGui
|
||||
import info.nightscout.rx.AapsSchedulers
|
||||
import info.nightscout.rx.bus.RxBus
|
||||
import info.nightscout.rx.events.EventMobileDataToWear
|
||||
import info.nightscout.rx.events.EventMobileToWear
|
||||
import info.nightscout.rx.logging.AAPSLogger
|
||||
import info.nightscout.rx.logging.LTag
|
||||
|
@ -44,6 +45,7 @@ import kotlinx.coroutines.SupervisorJob
|
|||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.tasks.await
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import javax.inject.Inject
|
||||
|
||||
class DataLayerListenerServiceMobile : WearableListenerService() {
|
||||
|
@ -80,7 +82,8 @@ class DataLayerListenerServiceMobile : WearableListenerService() {
|
|||
private val disposable = CompositeDisposable()
|
||||
|
||||
private val rxPath get() = getString(info.nightscout.shared.R.string.path_rx_bridge)
|
||||
|
||||
private val rxDataPath get() = getString(info.nightscout.shared.R.string.path_rx_data_bridge)
|
||||
@ExperimentalSerializationApi
|
||||
override fun onCreate() {
|
||||
AndroidInjection.inject(this)
|
||||
super.onCreate()
|
||||
|
@ -90,6 +93,10 @@ class DataLayerListenerServiceMobile : WearableListenerService() {
|
|||
.toObservable(EventMobileToWear::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe { sendMessage(rxPath, it.payload.serialize()) }
|
||||
disposable += rxBus
|
||||
.toObservable(EventMobileDataToWear::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe { sendMessage(rxDataPath, it.payload.serializeByte()) }
|
||||
}
|
||||
|
||||
override fun onCapabilityChanged(p0: CapabilityInfo) {
|
||||
|
@ -125,7 +132,7 @@ class DataLayerListenerServiceMobile : WearableListenerService() {
|
|||
}
|
||||
super.onDataChanged(dataEvents)
|
||||
}
|
||||
|
||||
@ExperimentalSerializationApi
|
||||
override fun onMessageReceived(messageEvent: MessageEvent) {
|
||||
super.onMessageReceived(messageEvent)
|
||||
|
||||
|
@ -136,6 +143,11 @@ class DataLayerListenerServiceMobile : WearableListenerService() {
|
|||
val command = EventData.deserialize(String(messageEvent.data))
|
||||
rxBus.send(command.also { it.sourceNodeId = messageEvent.sourceNodeId })
|
||||
}
|
||||
rxDataPath -> {
|
||||
aapsLogger.debug(LTag.WEAR, "onMessageReceived rxDataPath: ${String(messageEvent.data)}")
|
||||
val command = EventData.deserializeByte(messageEvent.data)
|
||||
rxBus.send(command.also { it.sourceNodeId = messageEvent.sourceNodeId })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +176,7 @@ class DataLayerListenerServiceMobile : WearableListenerService() {
|
|||
private fun pickBestNodeId(nodes: Set<Node>): Node? =
|
||||
nodes.firstOrNull { it.isNearby } ?: nodes.firstOrNull()
|
||||
|
||||
@Suppress("unused")
|
||||
//@Suppress("unused")
|
||||
private fun sendData(path: String, vararg params: DataMap) {
|
||||
if (wearPlugin.isEnabled()) {
|
||||
scope.launch {
|
||||
|
@ -201,7 +213,6 @@ class DataLayerListenerServiceMobile : WearableListenerService() {
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
private fun sendMessage(path: String, data: ByteArray) {
|
||||
aapsLogger.debug(LTag.WEAR, "sendMessage: $path")
|
||||
transcriptionNodeId?.also { nodeId ->
|
||||
|
|
80
plugins/main/src/main/res/drawable/export_custom.xml
Normal file
|
@ -0,0 +1,80 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M1.885,11.953c0,2.578 2.09,4.668 4.668,4.668s4.668,-2.09 4.668,-4.668c0,-2.578 -2.09,-4.668 -4.668,-4.668S1.885,9.375 1.885,11.953zM3.093,11.953c0,-1.911 1.549,-3.46 3.46,-3.46s3.46,1.549 3.46,3.46c0,1.911 -1.549,3.46 -3.46,3.46S3.093,13.864 3.093,11.953z"
|
||||
android:fillColor="#AAAAAA"/>
|
||||
<path
|
||||
android:pathData="M4.273,4.976l4.56,0l0.597,3.338l-5.754,0z"
|
||||
android:fillColor="#AAAAAA"/>
|
||||
<path
|
||||
android:pathData="M4.273,19.024l4.56,0l0.597,-3.338l-5.754,0z"
|
||||
android:fillColor="#AAAAAA"/>
|
||||
<path
|
||||
android:pathData="M12.915,6.067h9.405v11.865h-9.405z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.4819"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"/>
|
||||
<path
|
||||
android:pathData="M21.172,7.23L14.064,7.23"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.4819"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M21.172,8.592L14.064,8.592"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.4819"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M21.172,11.318L14.064,11.318"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.4819"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M21.172,9.955L14.064,9.955"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.4819"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M21.172,12.68L14.064,12.68"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.4819"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M21.172,14.043L14.064,14.043"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.4819"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M21.172,16.768L14.064,16.768"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.4819"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M21.172,15.406L14.064,15.406"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="0.4819"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M19.351,11.953l-4.975,-2.13l0,1.255l-7.896,0l0,1.75l7.896,0l0,1.256z"
|
||||
android:fillColor="#6AE86D"/>
|
||||
</vector>
|
42
plugins/main/src/main/res/drawable/load_custom.xml
Normal file
|
@ -0,0 +1,42 @@
|
|||
<vector android:height="48dp" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#AAAAAA" android:pathData="M17.653,7.286c-2.578,0 -4.668,2.09 -4.668,4.668c0,2.578 2.09,4.668 4.668,4.668s4.668,-2.09 4.668,-4.668C22.321,9.375 20.231,7.286 17.653,7.286zM17.653,15.414c-1.911,0 -3.46,-1.549 -3.46,-3.46c0,-1.911 1.549,-3.46 3.46,-3.46s3.46,1.549 3.46,3.46C21.113,13.864 19.564,15.414 17.653,15.414z"/>
|
||||
<path android:fillColor="#AAAAAA" android:pathData="M19.932,4.976l-4.559,0l-0.597,3.338l5.754,0z"/>
|
||||
<path android:fillColor="#AAAAAA" android:pathData="M19.932,19.024l-4.559,0l-0.597,-3.338l5.754,0z"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M1.885,6.067h9.405v11.865h-9.405z"
|
||||
android:strokeColor="#FFFFFF" android:strokeLineJoin="round" android:strokeWidth="0.4819"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M3.034,7.23L10.142,7.23"
|
||||
android:strokeColor="#FFFFFF" android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" android:strokeWidth="0.4819"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M3.034,8.592L10.142,8.592"
|
||||
android:strokeColor="#FFFFFF" android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" android:strokeWidth="0.4819"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M3.034,11.318L10.142,11.318"
|
||||
android:strokeColor="#FFFFFF" android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" android:strokeWidth="0.4819"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M3.034,9.955L10.142,9.955"
|
||||
android:strokeColor="#FFFFFF" android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" android:strokeWidth="0.4819"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M3.034,12.68L10.142,12.68"
|
||||
android:strokeColor="#FFFFFF" android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" android:strokeWidth="0.4819"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M3.034,14.043L10.142,14.043"
|
||||
android:strokeColor="#FFFFFF" android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" android:strokeWidth="0.4819"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M3.034,16.768L10.142,16.768"
|
||||
android:strokeColor="#FFFFFF" android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" android:strokeWidth="0.4819"/>
|
||||
<path android:fillColor="#00000000"
|
||||
android:pathData="M3.034,15.406L10.142,15.406"
|
||||
android:strokeColor="#FFFFFF" android:strokeLineCap="round"
|
||||
android:strokeLineJoin="round" android:strokeWidth="0.4819"/>
|
||||
<path android:fillColor="#6AE86D" android:pathData="M17.726,11.953l-4.975,-2.13l0,1.255l-7.896,0l0,1.75l7.896,0l0,1.256z"/>
|
||||
</vector>
|
7
plugins/main/src/main/res/drawable/send_custom.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<vector android:height="48dp" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#AAAAAA" android:pathData="M16.168,7.286c-2.578,0 -4.668,2.09 -4.668,4.668c0,2.578 2.09,4.668 4.668,4.668s4.668,-2.09 4.668,-4.668C20.836,9.375 18.746,7.286 16.168,7.286zM16.168,15.414c-1.911,0 -3.46,-1.549 -3.46,-3.46c0,-1.911 1.549,-3.46 3.46,-3.46s3.46,1.549 3.46,3.46C19.628,13.864 18.079,15.414 16.168,15.414z"/>
|
||||
<path android:fillColor="#AAAAAA" android:pathData="M18.448,4.976l-4.56,0l-0.597,3.338l5.754,0z"/>
|
||||
<path android:fillColor="#AAAAAA" android:pathData="M18.448,19.024l-4.56,0l-0.597,-3.338l5.754,0z"/>
|
||||
<path android:fillColor="#6AE86D" android:pathData="M16.241,11.953l-4.975,-2.13l0,1.255l-7.896,0l0,1.75l7.896,0l0,1.256z"/>
|
||||
</vector>
|
7
plugins/main/src/main/res/drawable/set_default.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<vector android:height="48dp" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#AAAAAA" android:pathData="M12.103,7.286c-2.578,0 -4.668,2.09 -4.668,4.668c0,2.578 2.09,4.668 4.668,4.668s4.668,-2.09 4.668,-4.668C16.771,9.375 14.681,7.286 12.103,7.286zM12.103,15.414c-1.911,0 -3.46,-1.549 -3.46,-3.46c0,-1.911 1.549,-3.46 3.46,-3.46s3.46,1.549 3.46,3.46C15.563,13.864 14.014,15.414 12.103,15.414z"/>
|
||||
<path android:fillColor="#AAAAAA" android:pathData="M14.383,4.976l-4.56,0l-0.597,3.338l5.754,0z"/>
|
||||
<path android:fillColor="#AAAAAA" android:pathData="M14.383,19.024l-4.56,0l-0.597,-3.338l5.754,0z"/>
|
||||
<path android:fillColor="#FF1313" android:pathData="M13.768,11.999l1.886,-1.886c0.081,-0.081 0.081,-0.213 0,-0.294l-1.372,-1.372c-0.078,-0.078 -0.216,-0.078 -0.294,0l-1.886,1.886l-1.886,-1.886c-0.078,-0.078 -0.216,-0.078 -0.294,0L8.552,9.819C8.513,9.858 8.491,9.911 8.491,9.966c0,0.055 0.022,0.108 0.061,0.147l1.886,1.886l-1.886,1.886c-0.039,0.039 -0.061,0.092 -0.061,0.147c0,0.055 0.022,0.108 0.061,0.147l1.372,1.372c0.041,0.04 0.094,0.061 0.147,0.061c0.053,0 0.106,-0.02 0.147,-0.061l1.886,-1.886l1.886,1.886c0.04,0.04 0.093,0.061 0.147,0.061c0.053,0 0.106,-0.02 0.147,-0.061l1.371,-1.371c0.081,-0.081 0.081,-0.213 0,-0.294L13.768,11.999z"/>
|
||||
</vector>
|
|
@ -1,42 +1,199 @@
|
|||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="2dp"
|
||||
tools:context="info.nightscout.plugins.general.wear.WearFragment">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/connected_device"
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/log"
|
||||
style="@style/Widget.MaterialComponents.CardView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical|center_horizontal"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:text="@string/no_watch_connected"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
tools:ignore="HardcodedText" />
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
app:cardCornerRadius="4dp"
|
||||
app:contentPadding="2dp"
|
||||
app:cardElevation="2dp"
|
||||
app:cardUseCompatPadding="false"
|
||||
android:layout_gravity="center">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/resend"
|
||||
style="@style/GrayButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_refresh"
|
||||
android:paddingLeft="0dp"
|
||||
android:paddingRight="0dp"
|
||||
android:text="@string/resend_all_data"
|
||||
android:textColor="?attr/treatmentButton" />
|
||||
<TextView
|
||||
android:id="@+id/connected_device"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical|center_horizontal"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:text="@string/no_watch_connected"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/open_settings"
|
||||
style="@style/GrayButton"
|
||||
<androidx.gridlayout.widget.GridLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="30dp"
|
||||
android:padding="10dip"
|
||||
app:columnCount="2">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/resend"
|
||||
style="@style/GrayButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:drawableTop="@drawable/ic_refresh"
|
||||
android:paddingLeft="0dp"
|
||||
android:paddingRight="0dp"
|
||||
android:text="@string/resend_all_data"
|
||||
android:textColor="?attr/treatmentButton"
|
||||
app:layout_gravity="fill"
|
||||
app:layout_column="0"
|
||||
app:layout_columnWeight="1"
|
||||
app:layout_row="0" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/open_settings"
|
||||
style="@style/GrayButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:drawableTop="@drawable/ic_settings"
|
||||
android:paddingLeft="0dp"
|
||||
android:paddingRight="0dp"
|
||||
android:text="@string/open_settings_on_wear"
|
||||
android:textColor="?attr/treatmentButton"
|
||||
app:layout_gravity="fill"
|
||||
app:layout_column="1"
|
||||
app:layout_columnWeight="1"
|
||||
app:layout_row="0" />
|
||||
|
||||
</androidx.gridlayout.widget.GridLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/custom_watchface_Layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/ic_settings"
|
||||
android:paddingLeft="0dp"
|
||||
android:paddingRight="0dp"
|
||||
android:text="@string/open_settings_on_wear"
|
||||
android:textColor="?attr/treatmentButton" />
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
style="@style/Widget.MaterialComponents.CardView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
app:cardCornerRadius="4dp"
|
||||
app:contentPadding="2dp"
|
||||
app:cardElevation="2dp"
|
||||
app:cardUseCompatPadding="false"
|
||||
android:layout_gravity="center">
|
||||
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/custom_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="15dp"
|
||||
android:paddingEnd="15dp"
|
||||
android:layout_marginBottom="0dp"
|
||||
android:text="@string/wear_custom_watchface" />
|
||||
|
||||
<androidx.gridlayout.widget.GridLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:padding="10dip"
|
||||
app:columnCount="2">
|
||||
|
||||
<info.nightscout.core.ui.elements.SingleClickButton
|
||||
android:id="@+id/load_custom"
|
||||
style="@style/GrayButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/load_custom"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:text="@string/wear_load_watchface"
|
||||
android:textSize="11sp"
|
||||
app:layout_columnWeight="1"
|
||||
app:layout_gravity="fill"
|
||||
app:layout_row="0"
|
||||
app:layout_column="0" />
|
||||
<!--
|
||||
<info.nightscout.core.ui.elements.SingleClickButton
|
||||
android:id="@+id/send_custom"
|
||||
style="@style/GrayButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/send_custom"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:text="@string/wear_send_watchface"
|
||||
android:textSize="11sp"
|
||||
app:layout_column="1"
|
||||
app:layout_columnWeight="1"
|
||||
app:layout_gravity="fill"
|
||||
app:layout_row="0" />
|
||||
-->
|
||||
<info.nightscout.core.ui.elements.SingleClickButton
|
||||
android:id="@+id/export_custom"
|
||||
style="@style/GrayButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/export_custom"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:text="@string/wear_export_watchface"
|
||||
android:textSize="11sp"
|
||||
android:visibility="visible"
|
||||
app:layout_columnWeight="1"
|
||||
app:layout_gravity="fill"
|
||||
app:layout_row="0"
|
||||
app:layout_column="1" />
|
||||
|
||||
<info.nightscout.core.ui.elements.SingleClickButton
|
||||
android:id="@+id/default_custom"
|
||||
style="@style/GrayButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableTop="@drawable/set_default"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:text="@string/wear_default_watchface"
|
||||
android:textSize="11sp"
|
||||
android:visibility="visible"
|
||||
app:layout_columnWeight="1"
|
||||
app:layout_gravity="fill"
|
||||
app:layout_row="1"
|
||||
app:layout_column="0" />
|
||||
|
||||
</androidx.gridlayout.widget.GridLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/custom_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="20dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/cover_chart"
|
||||
android:tag="cover_chart"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:src="@drawable/watchface_custom"
|
||||
android:visibility="visible"
|
||||
android:orientation="vertical" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
|
@ -361,6 +361,11 @@
|
|||
<string name="wear_notifysmb_summary">Show SMB on the watch like a standard bolus.</string>
|
||||
<string name="wear_predictions_summary">Show the predictions on the watchface.</string>
|
||||
<string name="wear_predictions_title">Predictions</string>
|
||||
<string name="wear_custom_watchface">Custom Watchface: %1$s</string>
|
||||
<string name="wear_load_watchface">Load Watchface</string>
|
||||
<string name="wear_send_watchface">Send Watchface</string>
|
||||
<string name="wear_export_watchface">Export Watchface</string>
|
||||
<string name="wear_new_custom_watchface_exported">Custom watchface exported</string>
|
||||
<string name="resend_all_data">Resend All Data</string>
|
||||
<string name="open_settings_on_wear">Open Settings on Wear</string>
|
||||
|
||||
|
|
|
@ -241,6 +241,31 @@
|
|||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name=".watchfaces.CustomWatchface"
|
||||
android:allowEmbedded="true"
|
||||
android:exported="false"
|
||||
android:label="@string/label_watchface_custom"
|
||||
android:permission="android.permission.BIND_WALLPAPER">
|
||||
<meta-data
|
||||
android:name="android.service.wallpaper"
|
||||
android:resource="@xml/watch_face" />
|
||||
<meta-data
|
||||
android:name="com.google.android.wearable.watchface.preview"
|
||||
android:resource="@drawable/watchface_custom" />
|
||||
<meta-data
|
||||
android:name="com.google.android.wearable.watchface.wearableConfigurationAction"
|
||||
android:value="watch_face_configuration_custom" />
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.service.wallpaper.WallpaperService" />
|
||||
<category android:name="com.google.android.wearable.watchface.category.WATCH_FACE" />
|
||||
|
||||
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
|
||||
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name=".comm.DataLayerListenerServiceWear"
|
||||
android:exported="true">
|
||||
|
@ -267,6 +292,15 @@
|
|||
android:pathPrefix="@string/path_rx_bridge"
|
||||
android:scheme="wear" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
|
||||
|
||||
<data
|
||||
android:host="*"
|
||||
android:pathPrefix="@string/path_rx_data_bridge"
|
||||
android:scheme="wear" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service
|
||||
|
@ -612,6 +646,7 @@
|
|||
<action android:name="watch_face_configuration_bigchart" />
|
||||
<action android:name="watch_face_configuration_circle" />
|
||||
<action android:name="watch_face_configuration_cockpit" />
|
||||
<action android:name="watch_face_configuration_custom" />
|
||||
<action android:name="watch_face_configuration_digitalstyle" />
|
||||
<action android:name="watch_face_configuration_home" />
|
||||
<action android:name="watch_face_configuration_home2" />
|
||||
|
|
|
@ -25,6 +25,7 @@ import info.nightscout.androidaps.tile.QuickWizardTileService
|
|||
import info.nightscout.androidaps.tile.TempTargetTileService
|
||||
import info.nightscout.rx.AapsSchedulers
|
||||
import info.nightscout.rx.bus.RxBus
|
||||
import info.nightscout.rx.events.EventWearDataToMobile
|
||||
import info.nightscout.rx.events.EventWearToMobile
|
||||
import info.nightscout.rx.logging.AAPSLogger
|
||||
import info.nightscout.rx.logging.LTag
|
||||
|
@ -179,6 +180,35 @@ class DataHandlerWear @Inject constructor(
|
|||
TileService.getUpdater(context).requestUpdate(QuickWizardTileService::class.java)
|
||||
}
|
||||
}
|
||||
disposable += rxBus
|
||||
.toObservable(EventData.ActionSetCustomWatchface::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe {
|
||||
aapsLogger.debug(LTag.WEAR, "Custom Watchface received from ${it.sourceNodeId}")
|
||||
persistence.store(it)
|
||||
persistence.readCustomWatchface()?.let {
|
||||
rxBus.send(EventWearDataToMobile(EventData.ActionGetCustomWatchface(it, false)))
|
||||
}
|
||||
}
|
||||
disposable += rxBus
|
||||
.toObservable(EventData.ActionrequestSetDefaultWatchface::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe {
|
||||
aapsLogger.debug(LTag.WEAR, "Set Default Watchface received from ${it.sourceNodeId}")
|
||||
persistence.setDefaultWatchface()
|
||||
persistence.readCustomWatchface()?.let {
|
||||
rxBus.send(EventWearDataToMobile(EventData.ActionGetCustomWatchface(it, false)))
|
||||
}
|
||||
}
|
||||
disposable += rxBus
|
||||
.toObservable(EventData.ActionrequestCustomWatchface::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe { eventData ->
|
||||
aapsLogger.debug(LTag.WEAR, "Custom Watchface requested from ${eventData.sourceNodeId}")
|
||||
persistence.readCustomWatchface()?.let {
|
||||
rxBus.send(EventWearDataToMobile(EventData.ActionGetCustomWatchface(it, eventData.exportFile)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleBolusProgress(bolusProgress: EventData.BolusProgress) {
|
||||
|
|
|
@ -12,6 +12,7 @@ import info.nightscout.androidaps.interaction.utils.Persistence
|
|||
import info.nightscout.androidaps.interaction.utils.WearUtil
|
||||
import info.nightscout.rx.AapsSchedulers
|
||||
import info.nightscout.rx.bus.RxBus
|
||||
import info.nightscout.rx.events.EventWearDataToMobile
|
||||
import info.nightscout.rx.events.EventWearToMobile
|
||||
import info.nightscout.rx.logging.AAPSLogger
|
||||
import info.nightscout.rx.logging.LTag
|
||||
|
@ -21,6 +22,7 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable
|
|||
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.tasks.await
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import javax.inject.Inject
|
||||
|
||||
class DataLayerListenerServiceWear : WearableListenerService() {
|
||||
|
@ -43,7 +45,8 @@ class DataLayerListenerServiceWear : WearableListenerService() {
|
|||
private val disposable = CompositeDisposable()
|
||||
|
||||
private val rxPath get() = getString(info.nightscout.shared.R.string.path_rx_bridge)
|
||||
|
||||
private val rxDataPath get() = getString(info.nightscout.shared.R.string.path_rx_data_bridge)
|
||||
@ExperimentalSerializationApi
|
||||
override fun onCreate() {
|
||||
AndroidInjection.inject(this)
|
||||
super.onCreate()
|
||||
|
@ -54,6 +57,12 @@ class DataLayerListenerServiceWear : WearableListenerService() {
|
|||
.subscribe {
|
||||
sendMessage(rxPath, it.payload.serialize())
|
||||
}
|
||||
disposable += rxBus
|
||||
.toObservable(EventWearDataToMobile::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe {
|
||||
sendMessage(rxDataPath, it.payload.serializeByte())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCapabilityChanged(p0: CapabilityInfo) {
|
||||
|
@ -87,7 +96,7 @@ class DataLayerListenerServiceWear : WearableListenerService() {
|
|||
}
|
||||
super.onDataChanged(dataEvents)
|
||||
}
|
||||
|
||||
@ExperimentalSerializationApi
|
||||
override fun onMessageReceived(messageEvent: MessageEvent) {
|
||||
super.onMessageReceived(messageEvent)
|
||||
|
||||
|
@ -100,6 +109,14 @@ class DataLayerListenerServiceWear : WearableListenerService() {
|
|||
transcriptionNodeId = messageEvent.sourceNodeId
|
||||
aapsLogger.debug(LTag.WEAR, "Updated node: $transcriptionNodeId")
|
||||
}
|
||||
rxDataPath -> {
|
||||
aapsLogger.debug(LTag.WEAR, "onMessageReceived: ${messageEvent.data}")
|
||||
val command = EventData.deserializeByte(messageEvent.data)
|
||||
rxBus.send(command.also { it.sourceNodeId = messageEvent.sourceNodeId })
|
||||
// Use this sender
|
||||
transcriptionNodeId = messageEvent.sourceNodeId
|
||||
aapsLogger.debug(LTag.WEAR, "Updated node: $transcriptionNodeId")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ abstract class WearServicesModule {
|
|||
@ContributesAndroidInjector abstract fun contributesBIGChart(): BigChartWatchface
|
||||
@ContributesAndroidInjector abstract fun contributesNOChart(): NoChartWatchface
|
||||
@ContributesAndroidInjector abstract fun contributesCircleWatchface(): CircleWatchface
|
||||
@ContributesAndroidInjector abstract fun contributesCustomWatchface(): CustomWatchface
|
||||
|
||||
@ContributesAndroidInjector abstract fun contributesTileBase(): TileBase
|
||||
@ContributesAndroidInjector abstract fun contributesQuickWizardTileService(): QuickWizardTileService
|
||||
|
|
|
@ -36,6 +36,8 @@ open class Persistence @Inject constructor(
|
|||
const val KEY_STALE_REPORTED = "staleReported"
|
||||
const val KEY_DATA_UPDATED = "data_updated_at"
|
||||
|
||||
const val CUSTOM_WATCHFACE = "custom_watchface"
|
||||
const val CUSTOM_DEFAULT_WATCHFACE = "custom_default_watchface"
|
||||
}
|
||||
|
||||
fun getString(key: String, defaultValue: String): String {
|
||||
|
@ -130,6 +132,23 @@ open class Persistence @Inject constructor(
|
|||
return null
|
||||
}
|
||||
|
||||
fun readCustomWatchface(isDefault: Boolean = false): EventData.ActionSetCustomWatchface? {
|
||||
try {
|
||||
var s = sp.getStringOrNull(if (isDefault) CUSTOM_DEFAULT_WATCHFACE else CUSTOM_WATCHFACE, null)
|
||||
if (s != null) {
|
||||
return deserialize(s) as EventData.ActionSetCustomWatchface
|
||||
} else {
|
||||
s = sp.getStringOrNull(CUSTOM_DEFAULT_WATCHFACE, null)
|
||||
if (s != null) {
|
||||
return deserialize(s) as EventData.ActionSetCustomWatchface
|
||||
}
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
aapsLogger.error(LTag.WEAR, exception.toString())
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun store(singleBg: SingleBg) {
|
||||
putString(BG_DATA_PERSISTENCE_KEY, singleBg.serialize())
|
||||
aapsLogger.debug(LTag.WEAR, "Stored BG data: $singleBg")
|
||||
|
@ -151,6 +170,16 @@ open class Persistence @Inject constructor(
|
|||
aapsLogger.debug(LTag.WEAR, "Stored Status data: $status")
|
||||
}
|
||||
|
||||
fun store(customWatchface: EventData.ActionSetCustomWatchface, isdefault: Boolean = false) {
|
||||
putString(if (isdefault) CUSTOM_DEFAULT_WATCHFACE else CUSTOM_WATCHFACE, customWatchface.serialize())
|
||||
aapsLogger.debug(LTag.WEAR, "Stored Custom Watchface ${customWatchface.customWatchfaceData} ${isdefault}: $customWatchface")
|
||||
}
|
||||
|
||||
fun setDefaultWatchface() {
|
||||
readCustomWatchface(true)?.let {store(it)}
|
||||
aapsLogger.debug(LTag.WEAR, "Custom Watchface reset to default")
|
||||
}
|
||||
|
||||
fun joinSet(set: Set<String>, separator: String?): String {
|
||||
val sb = StringBuilder()
|
||||
var i = 0
|
||||
|
|
|
@ -0,0 +1,495 @@
|
|||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package info.nightscout.androidaps.watchfaces
|
||||
|
||||
import android.app.ActionBar.LayoutParams
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.ColorFilter
|
||||
import android.graphics.ColorMatrix
|
||||
import android.graphics.ColorMatrixColorFilter
|
||||
import android.graphics.Point
|
||||
import android.graphics.Typeface
|
||||
import android.support.wearable.watchface.WatchFaceStyle
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.view.forEach
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.databinding.ActivityCustomBinding
|
||||
import info.nightscout.androidaps.watchfaces.utils.BaseWatchFace
|
||||
import info.nightscout.rx.weardata.CUSTOM_VERSION
|
||||
import info.nightscout.rx.weardata.CustomWatchfaceData
|
||||
import info.nightscout.rx.weardata.CustomWatchfaceDrawableDataKey
|
||||
import info.nightscout.rx.weardata.CustomWatchfaceDrawableDataMap
|
||||
import info.nightscout.rx.weardata.CustomWatchfaceMetadataKey
|
||||
import info.nightscout.rx.weardata.DrawableData
|
||||
import info.nightscout.rx.weardata.DrawableFormat
|
||||
import info.nightscout.rx.weardata.EventData
|
||||
import info.nightscout.rx.weardata.ZipWatchfaceFormat
|
||||
import info.nightscout.shared.extensions.toVisibility
|
||||
import info.nightscout.shared.extensions.toVisibilityKeepSpace
|
||||
import info.nightscout.shared.sharedPreferences.SP
|
||||
import org.joda.time.TimeOfDay
|
||||
import org.json.JSONObject
|
||||
import java.io.ByteArrayOutputStream
|
||||
import javax.inject.Inject
|
||||
|
||||
class CustomWatchface : BaseWatchFace() {
|
||||
|
||||
@Inject lateinit var context: Context
|
||||
private lateinit var binding: ActivityCustomBinding
|
||||
private var zoomFactor = 1.0
|
||||
private val displaySize = Point()
|
||||
private val TEMPLATE_RESOLUTION = 400
|
||||
private var lowBatColor = Color.RED
|
||||
private var bgColor = Color.WHITE
|
||||
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun inflateLayout(inflater: LayoutInflater): ViewBinding {
|
||||
binding = ActivityCustomBinding.inflate(inflater)
|
||||
setDefaultColors()
|
||||
persistence.store(defaultWatchface(), true)
|
||||
(context.getSystemService(WINDOW_SERVICE) as WindowManager).defaultDisplay.getSize(displaySize)
|
||||
zoomFactor = (displaySize.x).toDouble() / TEMPLATE_RESOLUTION.toDouble()
|
||||
return binding
|
||||
}
|
||||
|
||||
override fun getWatchFaceStyle(): WatchFaceStyle {
|
||||
return WatchFaceStyle.Builder(this)
|
||||
.setAcceptsTapEvents(true)
|
||||
.setHideNotificationIndicator(false)
|
||||
.setShowUnreadCountIndicator(true)
|
||||
.build()
|
||||
}
|
||||
|
||||
override fun setDataFields() {
|
||||
super.setDataFields()
|
||||
binding.direction2.setImageDrawable(resources.getDrawable(TrendArrow.fromSymbol(singleBg.slopeArrow).icon))
|
||||
}
|
||||
override fun setColorDark() {
|
||||
setWatchfaceStyle()
|
||||
binding.mainLayout.setBackgroundColor(ContextCompat.getColor(this, R.color.dark_background))
|
||||
binding.sgv.setTextColor(bgColor)
|
||||
binding.direction.setTextColor(bgColor)
|
||||
binding.direction2.colorFilter = changeDrawableColor(bgColor)
|
||||
|
||||
if (ageLevel != 1)
|
||||
binding.timestamp.setTextColor(ContextCompat.getColor(this, R.color.dark_TimestampOld))
|
||||
if (status.batteryLevel != 1)
|
||||
binding.uploaderBattery.setTextColor(lowBatColor)
|
||||
when (loopLevel) {
|
||||
-1 -> binding.loop.setBackgroundResource(R.drawable.loop_grey_25)
|
||||
1 -> binding.loop.setBackgroundResource(R.drawable.loop_green_25)
|
||||
else -> binding.loop.setBackgroundResource(R.drawable.loop_red_25)
|
||||
}
|
||||
|
||||
basalBackgroundColor = ContextCompat.getColor(this, R.color.basal_dark)
|
||||
basalCenterColor = ContextCompat.getColor(this, R.color.basal_light)
|
||||
|
||||
// rotate the second hand.
|
||||
binding.secondHand.rotation = TimeOfDay().secondOfMinute * 6f
|
||||
// rotate the minute hand.
|
||||
binding.minuteHand.rotation = TimeOfDay().minuteOfHour * 6f
|
||||
// rotate the hour hand.
|
||||
binding.hourHand.rotation = TimeOfDay().hourOfDay * 30f + TimeOfDay().minuteOfHour * 0.5f
|
||||
|
||||
setupCharts()
|
||||
}
|
||||
|
||||
override fun setColorBright() {
|
||||
setColorDark()
|
||||
}
|
||||
|
||||
override fun setColorLowRes() {
|
||||
setColorDark()
|
||||
}
|
||||
|
||||
override fun setSecond() {
|
||||
binding.time.text = "${dateUtil.hourString()}:${dateUtil.minuteString()}" + if (showSecond) ":${dateUtil.secondString()}" else ""
|
||||
binding.second.text = dateUtil.secondString()
|
||||
// rotate the second hand.
|
||||
binding.secondHand.rotation = TimeOfDay().secondOfMinute * 6f
|
||||
//aapsLogger.debug("XXXXX SetSecond $watchModeString")
|
||||
}
|
||||
|
||||
override fun updateSecondVisibility() {
|
||||
binding.second.visibility = showSecond.toVisibility()
|
||||
binding.secondHand.visibility = showSecond.toVisibility()
|
||||
}
|
||||
|
||||
private fun setWatchfaceStyle() {
|
||||
val customWatchface = persistence.readCustomWatchface() ?: persistence.readCustomWatchface(true)
|
||||
customWatchface?.let {
|
||||
try {
|
||||
val json = JSONObject(it.customWatchfaceData.json)
|
||||
val drawableDataMap = it.customWatchfaceData.drawableDatas
|
||||
enableSecond = (if (json.has("enableSecond")) json.getBoolean("enableSecond") else false) && sp.getBoolean(R.string.key_show_seconds, true)
|
||||
|
||||
highColor = if (json.has("highColor")) Color.parseColor(json.getString("highColor")) else ContextCompat.getColor(this, R.color.dark_highColor)
|
||||
midColor = if (json.has("midColor")) Color.parseColor(json.getString("midColor")) else ContextCompat.getColor(this, R.color.inrange)
|
||||
lowColor = if (json.has("lowColor")) Color.parseColor(json.getString("lowColor")) else ContextCompat.getColor(this, R.color.low)
|
||||
lowBatColor = if (json.has("lowBatColor")) Color.parseColor(json.getString("lowBatColor")) else ContextCompat.getColor(this, R.color.dark_uploaderBatteryEmpty)
|
||||
carbColor = if (json.has("carbColor")) Color.parseColor(json.getString("carbColor")) else ContextCompat.getColor(this, R.color.carbs)
|
||||
gridColor = if (json.has("gridColor")) Color.parseColor(json.getString("gridColor")) else ContextCompat.getColor(this, R.color.carbs)
|
||||
pointSize = if (json.has("pointSize")) json.getInt("pointSize") else 2
|
||||
bgColor = when (singleBg.sgvLevel) {
|
||||
1L -> highColor
|
||||
0L -> midColor
|
||||
-1L -> lowColor
|
||||
else -> midColor
|
||||
}
|
||||
|
||||
binding.mainLayout.forEach { view ->
|
||||
CustomViews.fromId(view.id)?.let { id ->
|
||||
if (json.has(id.key)) {
|
||||
var viewjson = json.getJSONObject(id.key)
|
||||
var wrapContent = LayoutParams.WRAP_CONTENT
|
||||
val width = if (viewjson.has("width")) (viewjson.getInt("width") * zoomFactor).toInt() else wrapContent
|
||||
val height = if (viewjson.has("height")) (viewjson.getInt("height") * zoomFactor).toInt() else wrapContent
|
||||
var params = FrameLayout.LayoutParams(width, height)
|
||||
params.topMargin = if (viewjson.has("topmargin")) (viewjson.getInt("topmargin") * zoomFactor).toInt() else 0
|
||||
params.leftMargin = if (viewjson.has("leftmargin")) (viewjson.getInt("leftmargin") * zoomFactor).toInt() else 0
|
||||
view.setLayoutParams(params)
|
||||
view.visibility = if (viewjson.has("visibility")) setVisibility(viewjson.getString("visibility"), id.visibility(sp)) else View.GONE
|
||||
if (view is TextView) {
|
||||
view.rotation = if (viewjson.has("rotation")) viewjson.getInt("rotation").toFloat() else 0F
|
||||
view.setTextSize(TypedValue.COMPLEX_UNIT_PX, ((if (viewjson.has("textsize")) viewjson.getInt("textsize") else 22) * zoomFactor).toFloat())
|
||||
view.gravity = setGravity(if (viewjson.has("gravity")) viewjson.getString("gravity") else "center")
|
||||
view.setTypeface(
|
||||
setFont(if (viewjson.has("font")) viewjson.getString("font") else "sans-serif"),
|
||||
setStyle(if (viewjson.has("fontStyle")) viewjson.getString("fontStyle") else "normal")
|
||||
)
|
||||
if (viewjson.has("fontColor"))
|
||||
view.setTextColor(getColor(viewjson.getString("fontColor")))
|
||||
|
||||
if (viewjson.has("textvalue"))
|
||||
view.text = viewjson.getString("textvalue")
|
||||
}
|
||||
|
||||
if (view is ImageView) {
|
||||
view.clearColorFilter()
|
||||
drawableDataMap[CustomWatchfaceDrawableDataKey.fromKey(id.key)]?.toDrawable(resources)?.also {
|
||||
if (viewjson.has("color"))
|
||||
it.colorFilter = changeDrawableColor(getColor(viewjson.getString("color")))
|
||||
else
|
||||
it.clearColorFilter()
|
||||
view.setImageDrawable(it)
|
||||
} ?: apply {
|
||||
view.setImageDrawable(CustomWatchfaceDrawableDataKey.fromKey(id.key).icon?.let { context.getDrawable(it) })
|
||||
if (viewjson.has("color"))
|
||||
view.setColorFilter(getColor(viewjson.getString("color")))
|
||||
else
|
||||
view.clearColorFilter()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
view.visibility = View.GONE
|
||||
if (view is TextView) {
|
||||
view.text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
updateSecondVisibility()
|
||||
} catch (e:Exception) {
|
||||
persistence.store(defaultWatchface(), false) // relaod correct values to avoid crash of watchface
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun defaultWatchface(): EventData.ActionSetCustomWatchface {
|
||||
val metadata = JSONObject()
|
||||
.put(CustomWatchfaceMetadataKey.CWF_NAME.key, getString(info.nightscout.shared.R.string.wear_default_watchface))
|
||||
.put(CustomWatchfaceMetadataKey.CWF_FILENAME.key, getString(info.nightscout.shared.R.string.wear_default_watchface))
|
||||
.put(CustomWatchfaceMetadataKey.CWF_AUTHOR.key, "Philoul")
|
||||
.put(CustomWatchfaceMetadataKey.CWF_CREATED_AT.key, dateUtil.dateString(dateUtil.now()))
|
||||
.put(CustomWatchfaceMetadataKey.CWF_VERSION.key, CUSTOM_VERSION)
|
||||
val json = JSONObject()
|
||||
.put("metadata", metadata)
|
||||
.put("highColor", String.format("#%06X", 0xFFFFFF and highColor))
|
||||
.put("midColor", String.format("#%06X", 0xFFFFFF and midColor))
|
||||
.put("lowColor", String.format("#%06X", 0xFFFFFF and lowColor))
|
||||
.put("lowBatColor", String.format("#%06X", 0xFFFFFF and lowBatColor))
|
||||
.put("carbColor", String.format("#%06X", 0xFFFFFF and carbColor))
|
||||
.put("gridColor", String.format("#%06X", 0xFFFFFF and Color.WHITE))
|
||||
.put("pointSize",2)
|
||||
.put("enableSecond", true)
|
||||
|
||||
binding.mainLayout.forEach { view ->
|
||||
val params = view.layoutParams as FrameLayout.LayoutParams
|
||||
CustomViews.fromId(view.id)?.let {
|
||||
if (view is TextView) {
|
||||
json.put(
|
||||
it.key,
|
||||
JSONObject()
|
||||
.put("width", (params.width / zoomFactor).toInt())
|
||||
.put("height", (params.height / zoomFactor).toInt())
|
||||
.put("topmargin", (params.topMargin / zoomFactor).toInt())
|
||||
.put("leftmargin", (params.leftMargin / zoomFactor).toInt())
|
||||
.put("rotation", view.rotation.toInt())
|
||||
.put("visibility", getVisibility(view.visibility))
|
||||
.put("textsize", view.textSize.toInt())
|
||||
.put("gravity", getGravity(view.gravity))
|
||||
.put("font", getFont(view.typeface))
|
||||
.put("fontStyle", getStyle(view.typeface.style))
|
||||
.put("fontColor", String.format("#%06X", 0xFFFFFF and view.currentTextColor))
|
||||
)
|
||||
}
|
||||
if (view is ImageView) {
|
||||
//view.backgroundTintList =
|
||||
json.put(
|
||||
it.key,
|
||||
JSONObject()
|
||||
.put("width", (params.width / zoomFactor).toInt())
|
||||
.put("height", (params.height / zoomFactor).toInt())
|
||||
.put("topmargin", (params.topMargin / zoomFactor).toInt())
|
||||
.put("leftmargin", (params.leftMargin / zoomFactor).toInt())
|
||||
.put("visibility", getVisibility(view.visibility))
|
||||
)
|
||||
}
|
||||
if (view is lecho.lib.hellocharts.view.LineChartView) {
|
||||
json.put(
|
||||
it.key,
|
||||
JSONObject()
|
||||
.put("width", (params.width / zoomFactor).toInt())
|
||||
.put("height", (params.height / zoomFactor).toInt())
|
||||
.put("topmargin", (params.topMargin / zoomFactor).toInt())
|
||||
.put("leftmargin", (params.leftMargin / zoomFactor).toInt())
|
||||
.put("visibility", getVisibility(view.visibility))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
val metadataMap = ZipWatchfaceFormat.loadMetadata(json)
|
||||
val drawableDataMap: CustomWatchfaceDrawableDataMap = mutableMapOf()
|
||||
getResourceByteArray(info.nightscout.shared.R.drawable.watchface_custom)?.let {
|
||||
val drawableData = DrawableData(it,DrawableFormat.PNG)
|
||||
drawableDataMap[CustomWatchfaceDrawableDataKey.CUSTOM_WATCHFACE] = drawableData
|
||||
}
|
||||
return EventData.ActionSetCustomWatchface(CustomWatchfaceData(json.toString(4), metadataMap, drawableDataMap))
|
||||
}
|
||||
|
||||
private fun setDefaultColors() {
|
||||
highColor = Color.parseColor("#FFFF00")
|
||||
midColor = Color.parseColor("#00FF00")
|
||||
lowColor = Color.parseColor("#FF0000")
|
||||
carbColor = ContextCompat.getColor(this, R.color.carbs)
|
||||
lowBatColor = ContextCompat.getColor(this, R.color.dark_uploaderBatteryEmpty)
|
||||
gridColor = Color.WHITE
|
||||
}
|
||||
|
||||
private fun setVisibility(visibility: String, pref: Boolean = true): Int = when (visibility) {
|
||||
"visible" -> pref.toVisibility()
|
||||
"invisible" -> pref.toVisibilityKeepSpace()
|
||||
"gone" -> View.GONE
|
||||
else -> View.GONE
|
||||
}
|
||||
|
||||
private fun getVisibility(visibility: Int): String = when (visibility) {
|
||||
View.VISIBLE -> "visible"
|
||||
View.INVISIBLE -> "invisible"
|
||||
View.GONE -> "gone"
|
||||
else -> "gone"
|
||||
}
|
||||
|
||||
private fun setGravity(gravity: String): Int = when (gravity) {
|
||||
"center" -> Gravity.CENTER
|
||||
"left" -> Gravity.LEFT
|
||||
"right" -> Gravity.RIGHT
|
||||
else -> Gravity.CENTER
|
||||
}
|
||||
|
||||
private fun getGravity(gravity: Int): String = when (gravity) {
|
||||
Gravity.CENTER -> "center"
|
||||
Gravity.LEFT -> "left"
|
||||
Gravity.RIGHT -> "right"
|
||||
else -> "center"
|
||||
}
|
||||
|
||||
private fun setFont(font: String): Typeface = when (font) {
|
||||
"sans-serif" -> Typeface.SANS_SERIF
|
||||
"default" -> Typeface.DEFAULT
|
||||
"default-bold" -> Typeface.DEFAULT_BOLD
|
||||
"monospace" -> Typeface.MONOSPACE
|
||||
"serif" -> Typeface.SERIF
|
||||
"roboto-condensed-bold" -> ResourcesCompat.getFont(context, R.font.roboto_condensed_bold)!!
|
||||
"roboto-condensed-light" -> ResourcesCompat.getFont(context, R.font.roboto_condensed_light)!!
|
||||
"roboto-condensed-regular" -> ResourcesCompat.getFont(context, R.font.roboto_condensed_regular)!!
|
||||
"roboto-slab-light" -> ResourcesCompat.getFont(context, R.font.roboto_slab_light)!!
|
||||
else -> Typeface.DEFAULT
|
||||
}
|
||||
|
||||
private fun getFont(font: Typeface): String = when (font) {
|
||||
Typeface.SANS_SERIF -> "sans-serif"
|
||||
Typeface.DEFAULT -> "default"
|
||||
Typeface.DEFAULT_BOLD -> "default-bold"
|
||||
Typeface.MONOSPACE -> "monospace"
|
||||
Typeface.SERIF -> "serif"
|
||||
ResourcesCompat.getFont(context, R.font.roboto_condensed_bold)!! -> "roboto-condensed-bold"
|
||||
ResourcesCompat.getFont(context, R.font.roboto_condensed_light)!! -> "roboto-condensed-light"
|
||||
ResourcesCompat.getFont(context, R.font.roboto_condensed_regular)!! -> "roboto-condensed-regular"
|
||||
ResourcesCompat.getFont(context, R.font.roboto_slab_light)!! -> "roboto-slab-light"
|
||||
else -> "default"
|
||||
}
|
||||
|
||||
private fun setStyle(style: String): Int = when (style) {
|
||||
"normal" -> Typeface.NORMAL
|
||||
"bold" -> Typeface.BOLD
|
||||
"bold-italic" -> Typeface.BOLD_ITALIC
|
||||
"italic" -> Typeface.ITALIC
|
||||
else -> Typeface.NORMAL
|
||||
}
|
||||
|
||||
private fun getStyle(style: Int): String = when (style) {
|
||||
Typeface.NORMAL -> "normal"
|
||||
Typeface.BOLD -> "bold"
|
||||
Typeface.BOLD_ITALIC -> "bold-italic"
|
||||
Typeface.ITALIC -> "italic"
|
||||
else -> "normal"
|
||||
}
|
||||
|
||||
fun getResourceByteArray(resourceId: Int): ByteArray? {
|
||||
val inputStream = resources.openRawResource(resourceId)
|
||||
val byteArrayOutputStream = ByteArrayOutputStream()
|
||||
|
||||
try {
|
||||
val buffer = ByteArray(1024)
|
||||
var count: Int
|
||||
while (inputStream.read(buffer).also { count = it } != -1) {
|
||||
byteArrayOutputStream.write(buffer, 0, count)
|
||||
}
|
||||
byteArrayOutputStream.close()
|
||||
inputStream.close()
|
||||
|
||||
return byteArrayOutputStream.toByteArray()
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun changeDrawableColor(color: Int): ColorFilter {
|
||||
val colorMatrix = ColorMatrix()
|
||||
colorMatrix.setSaturation(0f)
|
||||
|
||||
colorMatrix.postConcat(
|
||||
ColorMatrix(
|
||||
floatArrayOf(
|
||||
Color.red(color) / 255f, 0f, 0f, 0f, 0f,
|
||||
0f, Color.green(color) / 255f, 0f, 0f, 0f,
|
||||
0f, 0f, Color.blue(color) / 255f, 0f, 0f,
|
||||
0f, 0f, 0f, Color.alpha(color) / 255f, 0f
|
||||
)
|
||||
)
|
||||
)
|
||||
return ColorMatrixColorFilter(colorMatrix)
|
||||
}
|
||||
|
||||
private fun getColor(color: String): Int {
|
||||
if (color == "bgColor")
|
||||
return bgColor
|
||||
else
|
||||
return try {
|
||||
Color.parseColor(color)
|
||||
} catch (e: Exception) {
|
||||
Color.GRAY
|
||||
}
|
||||
}
|
||||
|
||||
enum class CustomViews(val key: String, @IdRes val id: Int, @StringRes val pref: Int?) {
|
||||
|
||||
BACKGROUND(CustomWatchfaceDrawableDataKey.BACKGROUND.key, R.id.background, null),
|
||||
CHART("chart", R.id.chart, null),
|
||||
COVER_CHART(CustomWatchfaceDrawableDataKey.COVERCHART.key, R.id.cover_chart, null),
|
||||
FREETEXT1("freetext1", R.id.freetext1, null),
|
||||
FREETEXT2("freetext2", R.id.freetext2, null),
|
||||
IOB1("iob1", R.id.iob1, R.string.key_show_iob),
|
||||
IOB2("iob2", R.id.iob2, R.string.key_show_iob),
|
||||
COB1("cob1", R.id.cob1, R.string.key_show_cob),
|
||||
COB2("cob2", R.id.cob2, R.string.key_show_cob),
|
||||
DELTA("delta", R.id.delta, R.string.key_show_delta),
|
||||
AVG_DELTA("avg_delta", R.id.avg_delta, R.string.key_show_avg_delta),
|
||||
UPLOADER_BATTERY("uploader_battery", R.id.uploader_battery, R.string.key_show_uploader_battery),
|
||||
RIG_BATTERY("rig_battery", R.id.rig_battery, R.string.key_show_rig_battery),
|
||||
BASALRATE("basalRate", R.id.basalRate, R.string.key_show_temp_basal),
|
||||
BGI("bgi", R.id.bgi, null),
|
||||
TIME("time", R.id.time, null),
|
||||
HOUR("hour", R.id.hour, null),
|
||||
MINUTE("minute", R.id.minute, null),
|
||||
SECOND("second", R.id.second, R.string.key_show_seconds),
|
||||
TIMEPERIOD("timePeriod", R.id.timePeriod, null),
|
||||
DAY_NAME("day_name", R.id.day_name, null),
|
||||
DAY("day", R.id.day, null),
|
||||
MONTH("month", R.id.month, null),
|
||||
LOOP("loop", R.id.loop, R.string.key_show_external_status),
|
||||
DIRECTION("direction", R.id.direction, R.string.key_show_direction),
|
||||
DIRECTION2("direction2", R.id.direction2, R.string.key_show_direction),
|
||||
TIMESTAMP("timestamp", R.id.timestamp, R.string.key_show_ago),
|
||||
SGV("sgv", R.id.sgv, R.string.key_show_bg),
|
||||
COVER_PLATE(CustomWatchfaceDrawableDataKey.COVERPLATE.key, R.id.cover_plate, null),
|
||||
HOUR_HABD(CustomWatchfaceDrawableDataKey.HOURHAND.key, R.id.hour_hand, null),
|
||||
MINUTE_HAND(CustomWatchfaceDrawableDataKey.MINUTEHAND.key, R.id.minute_hand, null),
|
||||
SECOND_HAND(CustomWatchfaceDrawableDataKey.SECONDHAND.key, R.id.second_hand, R.string.key_show_seconds);
|
||||
|
||||
companion object {
|
||||
|
||||
private val keyToEnumMap = HashMap<String, CustomViews>()
|
||||
private val idToEnumMap = HashMap<Int, CustomViews>()
|
||||
|
||||
init {
|
||||
for (value in values()) keyToEnumMap[value.key] = value
|
||||
for (value in values()) idToEnumMap[value.id] = value
|
||||
}
|
||||
|
||||
fun fromKey(key: String): CustomViews? =
|
||||
if (keyToEnumMap.containsKey(key)) {
|
||||
keyToEnumMap[key]
|
||||
} else {
|
||||
null
|
||||
}
|
||||
fun fromId(id: Int): CustomViews? =
|
||||
if (idToEnumMap.containsKey(id)) {
|
||||
idToEnumMap[id]
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun visibility(sp: SP): Boolean = this.pref?.let { sp.getBoolean(it, true) }
|
||||
?: true
|
||||
}
|
||||
|
||||
|
||||
enum class TrendArrow(val text: String, val symbol: String,@DrawableRes val icon: Int) {
|
||||
NONE("NONE", "??", R.drawable.ic_invalid),
|
||||
TRIPLE_UP("TripleUp", "X", R.drawable.ic_invalid),
|
||||
DOUBLE_UP("DoubleUp", "\u21c8", R.drawable.ic_doubleup),
|
||||
SINGLE_UP("SingleUp", "\u2191", R.drawable.ic_singleup),
|
||||
FORTY_FIVE_UP("FortyFiveUp", "\u2197", R.drawable.ic_fortyfiveup),
|
||||
FLAT("Flat", "\u2192", R.drawable.ic_flat),
|
||||
FORTY_FIVE_DOWN("FortyFiveDown", "\u2198",R.drawable.ic_fortyfivedown),
|
||||
SINGLE_DOWN("SingleDown", "\u2193", R.drawable.ic_singledown),
|
||||
DOUBLE_DOWN("DoubleDown", "\u21ca", R.drawable.ic_doubledown),
|
||||
TRIPLE_DOWN("TripleDown", "X",R.drawable.ic_invalid)
|
||||
;
|
||||
|
||||
companion object {
|
||||
fun fromSymbol(direction: String?) =
|
||||
values().firstOrNull { it.symbol == direction } ?: NONE
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -78,12 +78,16 @@ abstract class BaseWatchFace : WatchFace() {
|
|||
var gridColor = Color.WHITE
|
||||
var basalBackgroundColor = Color.BLUE
|
||||
var basalCenterColor = Color.BLUE
|
||||
var carbColor = Color.GREEN
|
||||
private var bolusColor = Color.MAGENTA
|
||||
private var lowResMode = false
|
||||
private var layoutSet = false
|
||||
var bIsRound = false
|
||||
var dividerMatchesBg = false
|
||||
var pointSize = 2
|
||||
var enableSecond = false
|
||||
val showSecond: Boolean
|
||||
get() = enableSecond && currentWatchMode == WatchMode.INTERACTIVE
|
||||
|
||||
// Tapping times
|
||||
private var sgvTapTime: Long = 0
|
||||
|
@ -250,7 +254,7 @@ abstract class BaseWatchFace : WatchFace() {
|
|||
}
|
||||
|
||||
override fun getInteractiveModeUpdateRate(): Long {
|
||||
return 60 * 1000L // Only call onTimeChanged every 60 seconds
|
||||
return if (showSecond) 1000L else 60 * 1000L // Only call onTimeChanged every 60 seconds
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
|
@ -271,6 +275,8 @@ abstract class BaseWatchFace : WatchFace() {
|
|||
missedReadingAlert()
|
||||
checkVibrateHourly(oldTime, newTime)
|
||||
if (!simpleUi.isEnabled(currentWatchMode)) setDataFields()
|
||||
} else if (layoutSet && !simpleUi.isEnabled(currentWatchMode) && showSecond && newTime.hasSecondChanged(oldTime)) {
|
||||
setSecond()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -358,9 +364,20 @@ abstract class BaseWatchFace : WatchFace() {
|
|||
binding.month?.text = dateUtil.monthString()
|
||||
binding.timePeriod?.visibility = android.text.format.DateFormat.is24HourFormat(this).not().toVisibility()
|
||||
binding.timePeriod?.text = dateUtil.amPm()
|
||||
if (showSecond)
|
||||
setSecond()
|
||||
}
|
||||
|
||||
private fun setColor() {
|
||||
open fun setSecond() {
|
||||
binding.time?.text = if(binding.timePeriod == null) dateUtil.timeString() else dateUtil.hourString() + ":" + dateUtil.minuteString() + if (showSecond) ":" + dateUtil.secondString() else ""
|
||||
binding.second?.text = dateUtil.secondString()
|
||||
}
|
||||
|
||||
open fun updateSecondVisibility() {
|
||||
binding.second?.visibility = showSecond.toVisibility()
|
||||
}
|
||||
|
||||
fun setColor() {
|
||||
dividerMatchesBg = sp.getBoolean(R.string.key_match_divider, false)
|
||||
when {
|
||||
lowResMode -> setColorLowRes()
|
||||
|
@ -378,9 +395,12 @@ abstract class BaseWatchFace : WatchFace() {
|
|||
}
|
||||
|
||||
override fun onWatchModeChanged(watchMode: WatchMode) {
|
||||
updateSecondVisibility() // will show second if enabledSecond and Interactive mode, hide in other situation
|
||||
setSecond() // will remove second from main date and time if not in Interactive mode
|
||||
lowResMode = isLowRes(watchMode)
|
||||
if (simpleUi.isEnabled(currentWatchMode)) simpleUi.setAntiAlias(currentWatchMode)
|
||||
else setDataFields()
|
||||
else
|
||||
setDataFields()
|
||||
invalidate()
|
||||
}
|
||||
|
||||
|
@ -409,12 +429,12 @@ abstract class BaseWatchFace : WatchFace() {
|
|||
if (lowResMode)
|
||||
BgGraphBuilder(
|
||||
sp, dateUtil, graphData.entries, treatmentData.predictions, treatmentData.temps, treatmentData.basals, treatmentData.boluses, pointSize,
|
||||
midColor, gridColor, basalBackgroundColor, basalCenterColor, bolusColor, Color.GREEN, timeframe
|
||||
midColor, gridColor, basalBackgroundColor, basalCenterColor, bolusColor, carbColor, timeframe
|
||||
)
|
||||
else
|
||||
BgGraphBuilder(
|
||||
sp, dateUtil, graphData.entries, treatmentData.predictions, treatmentData.temps, treatmentData.basals, treatmentData.boluses,
|
||||
pointSize, highColor, lowColor, midColor, gridColor, basalBackgroundColor, basalCenterColor, bolusColor, Color.GREEN, timeframe
|
||||
pointSize, highColor, lowColor, midColor, gridColor, basalBackgroundColor, basalCenterColor, bolusColor, carbColor, timeframe
|
||||
)
|
||||
binding.chart?.lineChartData = bgGraphBuilder.lineData()
|
||||
binding.chart?.isViewportCalculationEnabled = true
|
||||
|
|
|
@ -6,6 +6,7 @@ import info.nightscout.androidaps.databinding.ActivityHome2Binding
|
|||
import info.nightscout.androidaps.databinding.ActivityHomeBinding
|
||||
import info.nightscout.androidaps.databinding.ActivityBigchartBinding
|
||||
import info.nightscout.androidaps.databinding.ActivityCockpitBinding
|
||||
import info.nightscout.androidaps.databinding.ActivityCustomBinding
|
||||
import info.nightscout.androidaps.databinding.ActivityDigitalstyleBinding
|
||||
import info.nightscout.androidaps.databinding.ActivityNochartBinding
|
||||
import info.nightscout.androidaps.databinding.ActivitySteampunkBinding
|
||||
|
@ -22,11 +23,12 @@ class WatchfaceViewAdapter(
|
|||
cp: ActivityCockpitBinding? = null,
|
||||
ds: ActivityDigitalstyleBinding? = null,
|
||||
nC: ActivityNochartBinding? = null,
|
||||
sP: ActivitySteampunkBinding? = null
|
||||
sP: ActivitySteampunkBinding? = null,
|
||||
cU: ActivityCustomBinding? = null
|
||||
) {
|
||||
|
||||
init {
|
||||
if (aL == null && a2 == null && aa == null && bC == null && cp == null && ds == null && nC == null && sP == null) {
|
||||
if (aL == null && a2 == null && aa == null && bC == null && cp == null && ds == null && nC == null && sP == null && cU == null) {
|
||||
throw IllegalArgumentException("Require at least on Binding parameter")
|
||||
}
|
||||
}
|
||||
|
@ -34,39 +36,40 @@ class WatchfaceViewAdapter(
|
|||
private val errorMessage = "Missing require View Binding parameter"
|
||||
// Required attributes
|
||||
val mainLayout =
|
||||
aL?.mainLayout ?: a2?.mainLayout ?: aa?.mainLayout ?: bC?.mainLayout ?: bC?.mainLayout ?: cp?.mainLayout ?: ds?.mainLayout ?: nC?.mainLayout ?: sP?.mainLayout
|
||||
aL?.mainLayout ?: a2?.mainLayout ?: aa?.mainLayout ?: bC?.mainLayout ?: bC?.mainLayout ?: cp?.mainLayout ?: ds?.mainLayout ?: nC?.mainLayout ?: sP?.mainLayout ?: cU?.mainLayout
|
||||
?: throw IllegalArgumentException(errorMessage)
|
||||
val timestamp =
|
||||
aL?.timestamp ?: a2?.timestamp ?: aa?.timestamp ?: bC?.timestamp ?: bC?.timestamp ?: cp?.timestamp ?: ds?.timestamp ?: nC?.timestamp ?: sP?.timestamp
|
||||
aL?.timestamp ?: a2?.timestamp ?: aa?.timestamp ?: bC?.timestamp ?: bC?.timestamp ?: cp?.timestamp ?: ds?.timestamp ?: nC?.timestamp ?: sP?.timestamp ?: cU?.timestamp
|
||||
?: throw IllegalArgumentException(errorMessage)
|
||||
val root =
|
||||
aL?.root ?: a2?.root ?: aa?.root ?: bC?.root ?: bC?.root ?: cp?.root ?: ds?.root ?: nC?.root ?: sP?.root
|
||||
aL?.root ?: a2?.root ?: aa?.root ?: bC?.root ?: bC?.root ?: cp?.root ?: ds?.root ?: nC?.root ?: sP?.root ?: cU?.root
|
||||
?: throw IllegalArgumentException(errorMessage)
|
||||
|
||||
// Optional attributes
|
||||
val sgv = aL?.sgv ?: a2?.sgv ?: aa?.sgv ?: bC?.sgv ?: bC?.sgv ?: cp?.sgv ?: ds?.sgv ?: nC?.sgv
|
||||
val direction = aL?.direction ?: a2?.direction ?: aa?.direction ?: cp?.direction ?: ds?.direction
|
||||
val loop = a2?.loop ?: cp?.loop ?: sP?.loop
|
||||
val delta = aL?.delta ?: a2?.delta ?: aa?.delta ?: bC?.delta ?: bC?.delta ?: cp?.delta ?: ds?.delta ?: nC?.delta
|
||||
val avgDelta = a2?.avgDelta ?: bC?.avgDelta ?: bC?.avgDelta ?: cp?.avgDelta ?: ds?.avgDelta ?: nC?.avgDelta
|
||||
val uploaderBattery = aL?.uploaderBattery ?: a2?.uploaderBattery ?: aa?.uploaderBattery ?: cp?.uploaderBattery ?: ds?.uploaderBattery ?: sP?.uploaderBattery
|
||||
val rigBattery = a2?.rigBattery ?: cp?.rigBattery ?: ds?.rigBattery ?: sP?.rigBattery
|
||||
val basalRate = a2?.basalRate ?: cp?.basalRate ?: ds?.basalRate ?: sP?.basalRate
|
||||
val bgi = a2?.bgi ?: ds?.bgi
|
||||
val AAPSv2 = a2?.AAPSv2 ?: cp?.AAPSv2 ?: ds?.AAPSv2 ?: sP?.AAPSv2
|
||||
val cob1 = a2?.cob1 ?: ds?.cob1
|
||||
val cob2 = a2?.cob2 ?: cp?.cob2 ?: ds?.cob2 ?: sP?.cob2
|
||||
val time = aL?.time ?: a2?.time ?: aa?.time ?: bC?.time ?: bC?.time ?: cp?.time ?: nC?.time
|
||||
val minute = ds?.minute
|
||||
val hour = ds?.hour
|
||||
val day = a2?.day ?: ds?.day
|
||||
val month = a2?.month ?: ds?.month
|
||||
val iob1 = a2?.iob1 ?: ds?.iob1
|
||||
val iob2 = a2?.iob2 ?: cp?.iob2 ?: ds?.iob2 ?: sP?.iob2
|
||||
val chart = a2?.chart ?: aa?.chart ?: bC?.chart ?: bC?.chart ?: ds?.chart ?: sP?.chart
|
||||
val sgv = aL?.sgv ?: a2?.sgv ?: aa?.sgv ?: bC?.sgv ?: bC?.sgv ?: cp?.sgv ?: ds?.sgv ?: nC?.sgv ?: cU?.sgv
|
||||
val direction = aL?.direction ?: a2?.direction ?: aa?.direction ?: cp?.direction ?: ds?.direction ?: cU?.direction
|
||||
val loop = a2?.loop ?: cp?.loop ?: sP?.loop ?: cU?.loop
|
||||
val delta = aL?.delta ?: a2?.delta ?: aa?.delta ?: bC?.delta ?: bC?.delta ?: cp?.delta ?: ds?.delta ?: nC?.delta ?: cU?.delta
|
||||
val avgDelta = a2?.avgDelta ?: bC?.avgDelta ?: bC?.avgDelta ?: cp?.avgDelta ?: ds?.avgDelta ?: nC?.avgDelta ?: cU?.avgDelta
|
||||
val uploaderBattery = aL?.uploaderBattery ?: a2?.uploaderBattery ?: aa?.uploaderBattery ?: cp?.uploaderBattery ?: ds?.uploaderBattery ?: sP?.uploaderBattery ?: cU?.uploaderBattery
|
||||
val rigBattery = a2?.rigBattery ?: cp?.rigBattery ?: ds?.rigBattery ?: sP?.rigBattery ?: cU?.rigBattery
|
||||
val basalRate = a2?.basalRate ?: cp?.basalRate ?: ds?.basalRate ?: sP?.basalRate ?: cU?.basalRate
|
||||
val bgi = a2?.bgi ?: ds?.bgi ?: cU?.bgi
|
||||
val AAPSv2 = a2?.AAPSv2 ?: cp?.AAPSv2 ?: ds?.AAPSv2 ?: sP?.AAPSv2 ?: cU?.AAPSv2
|
||||
val cob1 = a2?.cob1 ?: ds?.cob1 ?: cU?.cob1
|
||||
val cob2 = a2?.cob2 ?: cp?.cob2 ?: ds?.cob2 ?: sP?.cob2 ?: cU?.cob2
|
||||
val time = aL?.time ?: a2?.time ?: aa?.time ?: bC?.time ?: bC?.time ?: cp?.time ?: nC?.time ?: cU?.time
|
||||
val second = cU?.second
|
||||
val minute = ds?.minute ?: cU?.minute
|
||||
val hour = ds?.hour ?: cU?.hour
|
||||
val day = a2?.day ?: ds?.day ?: cU?.day
|
||||
val month = a2?.month ?: ds?.month ?: cU?.month
|
||||
val iob1 = a2?.iob1 ?: ds?.iob1 ?: cU?.iob1
|
||||
val iob2 = a2?.iob2 ?: cp?.iob2 ?: ds?.iob2 ?: sP?.iob2 ?: cU?.iob2
|
||||
val chart = a2?.chart ?: aa?.chart ?: bC?.chart ?: bC?.chart ?: ds?.chart ?: sP?.chart ?: cU?.chart
|
||||
val status = aL?.status ?: aa?.status ?: bC?.status ?: bC?.status ?: nC?.status
|
||||
val timePeriod = ds?.timePeriod ?: aL?.timePeriod ?: nC?.timePeriod ?: bC?.timePeriod
|
||||
val dayName = ds?.dayName
|
||||
val timePeriod = ds?.timePeriod ?: aL?.timePeriod ?: nC?.timePeriod ?: bC?.timePeriod ?: cU?.timePeriod
|
||||
val dayName = ds?.dayName ?: cU?.dayName
|
||||
val mainMenuTap = ds?.mainMenuTap ?: sP?.mainMenuTap
|
||||
val chartZoomTap = ds?.chartZoomTap ?: sP?.chartZoomTap
|
||||
val dateTime = ds?.dateTime ?: a2?.dateTime
|
||||
|
@ -91,6 +94,7 @@ class WatchfaceViewAdapter(
|
|||
is ActivityDigitalstyleBinding -> WatchfaceViewAdapter(null, null, null, null, null, bindLayout)
|
||||
is ActivityNochartBinding -> WatchfaceViewAdapter(null, null, null, null, null, null, bindLayout)
|
||||
is ActivitySteampunkBinding -> WatchfaceViewAdapter(null, null, null, null, null, null, null, bindLayout)
|
||||
is ActivityCustomBinding -> WatchfaceViewAdapter(null, null, null, null, null, null, null, null, bindLayout)
|
||||
else -> throw IllegalArgumentException("ViewBinding is not implement in WatchfaceViewAdapter")
|
||||
}
|
||||
}
|
||||
|
|
12
wear/src/main/res/drawable/ic_doubledown.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M2.702,16.281c1.952,1.81 4.279,4.076 5.31,5.654v0.002c0,0 0.001,-0.001 0.001,-0.001c0,0 0.001,0.001 0.001,0.001v-0.002c1.032,-1.578 3.359,-3.844 5.31,-5.654l-1.325,-1.821c0,0 -1.578,1.408 -2.934,2.728V2.063H6.961l0,15.125c-1.356,-1.319 -2.934,-2.728 -2.934,-2.728L2.702,16.281z"
|
||||
android:fillColor="@color/white"/>
|
||||
<path
|
||||
android:pathData="M10.676,16.281c1.952,1.81 4.279,4.076 5.31,5.654v0.002c0,0 0.001,-0.001 0.001,-0.001c0,0 0.001,0.001 0.001,0.001v-0.002c1.032,-1.578 3.359,-3.844 5.31,-5.654l-1.325,-1.821c0,0 -1.578,1.408 -2.934,2.728V2.063h-2.104v15.125c-1.356,-1.319 -2.934,-2.728 -2.934,-2.728L10.676,16.281z"
|
||||
android:fillColor="@color/white"/>
|
||||
</vector>
|
12
wear/src/main/res/drawable/ic_doubleup.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M2.702,7.719c1.952,-1.81 4.279,-4.076 5.31,-5.654V2.063c0,0 0.001,0.001 0.001,0.001c0,0 0.001,-0.001 0.001,-0.001v0.002c1.032,1.578 3.359,3.844 5.31,5.654L11.999,9.54c0,0 -1.578,-1.408 -2.934,-2.728v15.125H6.961l0,-15.125C5.605,8.132 4.027,9.54 4.027,9.54L2.702,7.719z"
|
||||
android:fillColor="@color/white"/>
|
||||
<path
|
||||
android:pathData="M10.676,7.719c1.952,-1.81 4.279,-4.076 5.31,-5.654V2.063c0,0 0.001,0.001 0.001,0.001c0,0 0.001,-0.001 0.001,-0.001v0.002c1.032,1.578 3.359,3.844 5.31,5.654L19.973,9.54c0,0 -1.578,-1.408 -2.934,-2.728v15.125h-2.104V6.813c-1.356,1.319 -2.934,2.728 -2.934,2.728L10.676,7.719z"
|
||||
android:fillColor="@color/white"/>
|
||||
</vector>
|
9
wear/src/main/res/drawable/ic_flat.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M16.281,17.311c1.81,-1.952 4.076,-4.279 5.654,-5.31h0.002c0,0 -0.001,-0.001 -0.001,-0.001c0,0 0.001,-0.001 0.001,-0.001h-0.002c-1.578,-1.032 -3.844,-3.359 -5.654,-5.31L14.46,8.014c0,0 1.408,1.578 2.728,2.934H2.063v2.104h15.125c-1.319,1.356 -2.728,2.934 -2.728,2.934L16.281,17.311z"
|
||||
android:fillColor="@color/white"/>
|
||||
</vector>
|
9
wear/src/main/res/drawable/ic_fortyfivedown.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M11.272,18.783c2.66,-0.1 5.908,-0.143 7.753,0.243l0.001,0.001c0,0 0,-0.001 0,-0.001c0,0 0.001,0 0.001,0l-0.001,-0.001c-0.386,-1.845 -0.343,-5.093 -0.243,-7.753l-2.225,-0.351c0,0 -0.12,2.111 -0.146,4.003L5.717,4.229L4.229,5.717l10.695,10.695c-1.892,0.026 -4.003,0.146 -4.003,0.146L11.272,18.783z"
|
||||
android:fillColor="@color/white"/>
|
||||
</vector>
|
9
wear/src/main/res/drawable/ic_fortyfiveup.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M18.783,12.728c-0.1,-2.66 -0.143,-5.908 0.243,-7.753l0.001,-0.001c0,0 -0.001,0 -0.001,0c0,0 0,-0.001 0,-0.001l-0.001,0.001c-1.845,0.386 -5.093,0.343 -7.753,0.243l-0.351,2.225c0,0 2.111,0.12 4.003,0.146L4.229,18.283l1.488,1.488L16.412,9.076c0.026,1.892 0.146,4.003 0.146,4.003L18.783,12.728z"
|
||||
android:fillColor="@color/white"/>
|
||||
</vector>
|
18
wear/src/main/res/drawable/ic_invalid.xml
Normal file
|
@ -0,0 +1,18 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M17.35,19.561m-1.147,0a1.147,1.147 0,1 1,2.294 0a1.147,1.147 0,1 1,-2.294 0"
|
||||
android:fillColor="@color/white"/>
|
||||
<path
|
||||
android:pathData="M17.301,16.804c-0.459,0 -0.83,-0.372 -0.83,-0.831c0,-2.985 1.391,-4.027 2.51,-4.864c0.937,-0.702 1.614,-1.209 1.614,-2.849c0,-2.262 -2.173,-2.965 -2.964,-2.965c-1.705,0 -3.083,1.022 -3.778,2.804c-0.167,0.427 -0.65,0.64 -1.075,0.471c-0.427,-0.167 -0.638,-0.648 -0.471,-1.075c0.944,-2.417 2.935,-3.86 5.325,-3.86c1.865,0 4.626,1.47 4.626,4.626c0,2.472 -1.264,3.418 -2.28,4.178c-1.031,0.772 -1.844,1.38 -1.844,3.535C18.131,16.432 17.76,16.804 17.301,16.804z"
|
||||
android:fillColor="@color/white"/>
|
||||
<path
|
||||
android:pathData="M6.846,19.561m-1.147,0a1.147,1.147 0,1 1,2.294 0a1.147,1.147 0,1 1,-2.294 0"
|
||||
android:fillColor="@color/white"/>
|
||||
<path
|
||||
android:pathData="M6.797,16.804c-0.459,0 -0.83,-0.372 -0.83,-0.831c0,-2.985 1.391,-4.027 2.51,-4.864c0.937,-0.702 1.614,-1.209 1.614,-2.849c0,-2.262 -2.173,-2.965 -2.964,-2.965c-1.705,0 -3.083,1.022 -3.778,2.804c-0.167,0.427 -0.65,0.64 -1.075,0.471C1.845,8.403 1.635,7.922 1.801,7.495c0.944,-2.417 2.935,-3.86 5.325,-3.86c1.865,0 4.626,1.47 4.626,4.626c0,2.472 -1.264,3.418 -2.28,4.178c-1.031,0.772 -1.844,1.38 -1.844,3.535C7.627,16.432 7.256,16.804 6.797,16.804z"
|
||||
android:fillColor="@color/white"/>
|
||||
</vector>
|
9
wear/src/main/res/drawable/ic_singledown.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M6.689,16.281c1.952,1.81 4.279,4.076 5.31,5.654v0.002c0,0 0.001,-0.001 0.001,-0.001c0,0 0.001,0.001 0.001,0.001v-0.002c1.032,-1.578 3.359,-3.844 5.31,-5.654l-1.325,-1.821c0,0 -1.578,1.408 -2.934,2.728V2.063h-2.104v15.125c-1.356,-1.319 -2.934,-2.728 -2.934,-2.728L6.689,16.281z"
|
||||
android:fillColor="@color/white"/>
|
||||
</vector>
|
9
wear/src/main/res/drawable/ic_singleup.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M17.102,7.719c-1.952,-1.81 -4.279,-4.076 -5.31,-5.654V2.063c0,0 -0.001,0.001 -0.001,0.001c0,0 -0.001,-0.001 -0.001,-0.001v0.002c-1.032,1.578 -3.359,3.844 -5.31,5.654L7.805,9.54c0,0 1.578,-1.408 2.934,-2.728v15.125h2.104V6.813c1.356,1.319 2.934,2.728 2.934,2.728L17.102,7.719z"
|
||||
android:fillColor="@color/white"/>
|
||||
</vector>
|
391
wear/src/main/res/layout/activity_custom.xml
Normal file
|
@ -0,0 +1,391 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/main_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".watchfaces.CustomWatchface">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/background"
|
||||
android:layout_width="400px"
|
||||
android:layout_height="400px"
|
||||
android:layout_marginTop="0px"
|
||||
android:layout_marginLeft="0px"
|
||||
android:visibility="visible"
|
||||
android:src="@drawable/background"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<lecho.lib.hellocharts.view.LineChartView
|
||||
android:id="@+id/chart"
|
||||
android:layout_width="400px"
|
||||
android:layout_height="170px"
|
||||
android:layout_marginTop="230px"
|
||||
android:layout_marginBottom="0px" />
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/cover_chart"
|
||||
android:layout_width="400px"
|
||||
android:layout_height="400px"
|
||||
android:layout_marginTop="0px"
|
||||
android:layout_marginLeft="0px"
|
||||
android:visibility="gone"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/freetext1"
|
||||
android:layout_width="0px"
|
||||
android:layout_height="0px"
|
||||
android:layout_marginTop="0px"
|
||||
android:layout_marginLeft="0px"
|
||||
android:textSize="21px"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
android:textColor="@color/light_grey" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/freetext2"
|
||||
android:layout_width="0px"
|
||||
android:layout_height="0px"
|
||||
android:layout_marginTop="0px"
|
||||
android:layout_marginLeft="0px"
|
||||
android:textSize="21px"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
android:textColor="@color/light_grey" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/iob1"
|
||||
android:layout_width="130px"
|
||||
android:layout_height="33px"
|
||||
android:layout_marginTop="168px"
|
||||
android:layout_marginLeft="270px"
|
||||
android:textSize="21px"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/light_grey"
|
||||
tools:text="@string/activity_IOB" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/iob2"
|
||||
android:layout_width="130px"
|
||||
android:layout_height="33px"
|
||||
android:layout_marginTop="196px"
|
||||
android:layout_marginLeft="270px"
|
||||
android:textSize="21px"
|
||||
android:textStyle="bold"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/light_grey"
|
||||
tools:text="@string/no_iob_u" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/cob1"
|
||||
android:layout_width="120px"
|
||||
android:layout_height="33px"
|
||||
android:layout_marginTop="168px"
|
||||
android:layout_marginLeft="0px"
|
||||
android:gravity="center"
|
||||
android:text="@string/activity_carb"
|
||||
android:textColor="@color/light_grey"
|
||||
android:textSize="21px" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/cob2"
|
||||
android:layout_width="120px"
|
||||
android:layout_height="33px"
|
||||
android:layout_marginTop="196px"
|
||||
android:layout_marginLeft="0px"
|
||||
android:gravity="center"
|
||||
android:text="@string/no_cob_g"
|
||||
android:textSize="21px"
|
||||
android:textColor="@color/light_grey"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/delta"
|
||||
android:layout_width="59px"
|
||||
android:layout_height="32px"
|
||||
android:layout_marginTop="133px"
|
||||
android:layout_marginLeft="15px"
|
||||
android:gravity="center"
|
||||
android:textSize="23px"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/light_grey"
|
||||
tools:text="+/-" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/avg_delta"
|
||||
android:layout_width="59px"
|
||||
android:layout_height="32px"
|
||||
android:layout_marginTop="133px"
|
||||
android:layout_marginLeft="74px"
|
||||
android:gravity="center"
|
||||
android:textSize="23px"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/light_grey"
|
||||
tools:text="@string/abbreviation_average" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/uploader_battery"
|
||||
android:layout_width="60px"
|
||||
android:layout_height="32px"
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="133px"
|
||||
android:layout_marginLeft="129px"
|
||||
android:textSize="23px"
|
||||
android:fontFamily="@font/roboto_condensed_bold"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/light_grey"
|
||||
tools:text="100%" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rig_battery"
|
||||
android:layout_width="60px"
|
||||
android:layout_height="32px"
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="133px"
|
||||
android:layout_marginLeft="189px"
|
||||
android:textSize="23px"
|
||||
android:fontFamily="@font/roboto_condensed_bold"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/light_grey"
|
||||
android:visibility="visible"
|
||||
tools:text="100%" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/basalRate"
|
||||
android:layout_width="91px"
|
||||
android:layout_height="32px"
|
||||
android:layout_marginTop="133px"
|
||||
android:layout_marginLeft="249px"
|
||||
android:gravity="center"
|
||||
android:fontFamily="@font/roboto_condensed_bold"
|
||||
android:textSize="23px"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/light_grey"
|
||||
tools:text="@string/no_tmp_basal_u_h" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bgi"
|
||||
android:layout_width="60px"
|
||||
android:layout_height="32px"
|
||||
android:layout_marginTop="133px"
|
||||
android:layout_marginLeft="340px"
|
||||
android:gravity="center"
|
||||
android:textSize="23px"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/light_grey"
|
||||
android:visibility="visible"
|
||||
tools:text="bgi" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/time"
|
||||
android:layout_width="0px"
|
||||
android:layout_height="0px"
|
||||
android:layout_marginTop="151px"
|
||||
android:gravity="center"
|
||||
android:fontFamily="@font/roboto_condensed_bold"
|
||||
android:textSize="70px"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/light_grey"
|
||||
android:visibility="gone"
|
||||
tools:text="22:00" />
|
||||
|
||||
<!-- hour -->
|
||||
<TextView
|
||||
android:id="@+id/hour"
|
||||
android:layout_width="0px"
|
||||
android:layout_height="0px"
|
||||
android:layout_marginTop="151px"
|
||||
android:layout_marginLeft="119px"
|
||||
android:fontFamily="@font/roboto_condensed_bold"
|
||||
android:textAllCaps="true"
|
||||
android:textSize="70px"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/light_grey"
|
||||
android:visibility="gone"
|
||||
tools:text="20" />
|
||||
<!-- minute -->
|
||||
<TextView
|
||||
android:id="@+id/minute"
|
||||
android:layout_width="0px"
|
||||
android:layout_height="0px"
|
||||
android:layout_marginTop="158px"
|
||||
android:layout_marginLeft="210px"
|
||||
android:fontFamily="@font/roboto_condensed_bold"
|
||||
android:textColor="@color/light_grey"
|
||||
android:textSize="46px"
|
||||
android:visibility="gone"
|
||||
tools:text="00" />
|
||||
<!-- minute -->
|
||||
<TextView
|
||||
android:id="@+id/second"
|
||||
android:layout_width="0px"
|
||||
android:layout_height="0px"
|
||||
android:layout_marginTop="158px"
|
||||
android:layout_marginLeft="210px"
|
||||
android:fontFamily="@font/roboto_condensed_bold"
|
||||
android:textColor="@color/light_grey"
|
||||
android:textSize="46px"
|
||||
android:visibility="gone"
|
||||
tools:text="00" />
|
||||
|
||||
<!-- 12h period AM / PM -->
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timePeriod"
|
||||
android:layout_width="0px"
|
||||
android:layout_height="0px"
|
||||
android:layout_marginLeft="210px"
|
||||
android:layout_marginTop="205px"
|
||||
android:fontFamily="@font/roboto_condensed_bold"
|
||||
android:textSize="17px"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/light_grey"
|
||||
android:visibility="gone"
|
||||
tools:text="AM" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/day_name"
|
||||
android:layout_width="56px"
|
||||
android:layout_height="36px"
|
||||
android:layout_marginTop="172px"
|
||||
android:layout_marginLeft="120px"
|
||||
android:textAllCaps="true"
|
||||
android:gravity="center"
|
||||
android:textSize="24px"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/white"
|
||||
android:visibility="visible"
|
||||
tools:text="DDD." />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/day"
|
||||
android:layout_width="56px"
|
||||
android:layout_height="36px"
|
||||
android:layout_marginTop="198px"
|
||||
android:layout_marginLeft="120px"
|
||||
android:gravity="center"
|
||||
android:textStyle="bold"
|
||||
android:textSize="24px"
|
||||
android:textColor="@color/white"
|
||||
tools:text="day" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/month"
|
||||
android:layout_width="50px"
|
||||
android:layout_height="36px"
|
||||
android:layout_marginTop="180px"
|
||||
android:layout_marginLeft="220px"
|
||||
android:gravity="center"
|
||||
android:textStyle="bold"
|
||||
android:textSize="24px"
|
||||
android:textColor="@color/white"
|
||||
tools:text="févr." />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/loop"
|
||||
android:layout_width="50px"
|
||||
android:layout_height="50px"
|
||||
android:layout_marginTop="61px"
|
||||
android:layout_marginLeft="68px"
|
||||
android:background="@drawable/loop_grey_25"
|
||||
android:gravity="center"
|
||||
android:textSize="24px"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/light_grey"
|
||||
tools:text="--'" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/direction"
|
||||
android:layout_width="52px"
|
||||
android:layout_height="52px"
|
||||
android:layout_marginTop="26px"
|
||||
android:layout_marginLeft="291px"
|
||||
android:visibility="gone"
|
||||
android:gravity="left"
|
||||
android:textSize="39px"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/light_grey"
|
||||
tools:text="--" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/direction2"
|
||||
android:layout_width="52px"
|
||||
android:layout_height="52px"
|
||||
android:layout_marginTop="26px"
|
||||
android:layout_marginLeft="291px"
|
||||
android:visibility="visible"
|
||||
android:src="@drawable/ic_flat"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timestamp"
|
||||
android:layout_width="52px"
|
||||
android:layout_height="34px"
|
||||
android:gravity="left"
|
||||
android:layout_marginTop="79px"
|
||||
android:layout_marginLeft="291px"
|
||||
android:textSize="25px"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/light_grey"
|
||||
tools:text="--'" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sgv"
|
||||
android:layout_width="400px"
|
||||
android:layout_height="100px"
|
||||
android:gravity="center"
|
||||
android:lines="1"
|
||||
android:layout_marginTop="26px"
|
||||
android:layout_marginLeft="0px"
|
||||
android:fontFamily="@font/roboto_condensed_bold"
|
||||
android:textSize="74px"
|
||||
android:textColor="@color/light_grey"
|
||||
tools:text="200" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/cover_plate"
|
||||
android:layout_width="400px"
|
||||
android:layout_height="400px"
|
||||
android:orientation="vertical"
|
||||
android:visibility="visible"
|
||||
android:src="@drawable/simplified_dial" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/hour_hand"
|
||||
android:layout_width="400px"
|
||||
android:layout_height="400px"
|
||||
android:orientation="vertical"
|
||||
android:rotation="20"
|
||||
android:src="@drawable/hour_hand" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/minute_hand"
|
||||
android:layout_width="400px"
|
||||
android:layout_height="400px"
|
||||
android:orientation="vertical"
|
||||
android:rotation="40"
|
||||
android:src="@drawable/minute_hand" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/second_hand"
|
||||
android:layout_width="400px"
|
||||
android:layout_height="400px"
|
||||
android:orientation="vertical"
|
||||
android:rotation="60"
|
||||
android:src="@drawable/second_hand"
|
||||
tools:ignore="UseAppTint" />
|
||||
|
||||
<!-- FLAGs -->
|
||||
<View
|
||||
android:id="@+id/AAPSv2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone" />
|
||||
|
||||
</FrameLayout>
|
|
@ -11,6 +11,7 @@
|
|||
<string name="label_watchface_cockpit">AAPS(Cockpit)</string>
|
||||
<string name="label_watchface_steampunk">AAPS(Steampunk)</string>
|
||||
<string name="label_watchface_digital_style">AAPS(DigitalStyle)</string>
|
||||
<string name="label_watchface_custom">AAPS(Custom)</string>
|
||||
<string name="label_actions_tile">AAPS(Actions)</string>
|
||||
<string name="label_temp_target_tile">AAPS(Temp Target)</string>
|
||||
<string name="label_quick_wizard_tile">AAPS(Quick Wizard)</string>
|
||||
|
@ -143,6 +144,7 @@
|
|||
<string name="pref_simplify_ui_sum">Only show time and BG</string>
|
||||
<string name="pref_vibrate_hourly">Vibrate hourly</string>
|
||||
<string name="pref_show_weeknumber">Show Week number</string>
|
||||
<string name="custom_pref_show_seconds">Show seconds</string>
|
||||
<string name="digitalstyle_pref_your_style">Your style:</string>
|
||||
<string name="digitalstyle_style_none">no style</string>
|
||||
<string name="digitalstyle_style_minimal">minimal style</string>
|
||||
|
@ -208,6 +210,14 @@
|
|||
<string name="key_complication_tap_action" translatable="false">complication_tap_action</string>
|
||||
<string name="key_carbs_button_increment_1" translatable="false">carbs_button_increment_1</string>
|
||||
<string name="key_carbs_button_increment_2" translatable="false">carbs_button_increment_2</string>
|
||||
<string name="key_enable_custom_setting" translatable="false">enable_custom_setting</string>
|
||||
<string name="key_digital_watchface" translatable="false">digital_watchface</string>
|
||||
<string name="key_digital_timing" translatable="false">digital_timing</string>
|
||||
<string name="key_analog_watchface" translatable="false">analog_watchface</string>
|
||||
<string name="key_show_seconds" translatable="false">show_second</string>
|
||||
<string name="key_high_color" translatable="false">highcolor</string>
|
||||
<string name="key_mid_color" translatable="false">midcolor</string>
|
||||
<string name="key_low_color" translatable="false">lowcolor</string>
|
||||
<string name="increment">increment</string>
|
||||
<string name="decrement">decrement</string>
|
||||
<string name="first_char_high">H</string>
|
||||
|
|
27
wear/src/main/res/xml/watch_face_configuration_custom.xml
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="true"
|
||||
android:key="@string/key_show_seconds"
|
||||
android:title="@string/custom_pref_show_seconds"
|
||||
app:wear_iconOff="@drawable/settings_off"
|
||||
app:wear_iconOn="@drawable/settings_on" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:key="@string/key_vibrate_hourly"
|
||||
android:title="@string/pref_vibrate_hourly"
|
||||
app:wear_iconOff="@drawable/settings_off"
|
||||
app:wear_iconOn="@drawable/settings_on" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="off"
|
||||
android:entries="@array/watchface_simplify_ui_name"
|
||||
android:entryValues="@array/watchface_simplify_ui_values"
|
||||
android:key="@string/key_simplify_ui"
|
||||
android:summary="@string/pref_simplify_ui_sum"
|
||||
android:title="@string/pref_simplify_ui" />
|
||||
|
||||
</PreferenceScreen>
|