Lomiri
IndicatorsLight.qml
1 /*
2  * Copyright 2014 Canonical Ltd.
3  * Copyright 2019 UBports Foundation
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation; version 3.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors:
18  * Renato Araujo Oliveira Filho <renato@canonical.com>
19  */
20 
21 import QtQuick 2.12
22 import Powerd 0.1
23 import Hfd 0.1
24 import QMenuModel 1.0 as QMenuModel
25 import Lomiri.Indicators 0.1 as Indicators
26 import Wizard 0.1
27 
28 import "../../.."
29 
30 Item {
31  id: root
32 
33  property bool supportsMultiColorLed: true
34 
35  property string indicatorState: "INDICATOR_OFF"
36 
37  property color color: "darkgreen"
38  property int onMillisec: 1000
39  property int offMillisec: 3000
40 
41  property double batteryLevel: 0
42  property string deviceState: ""
43  property alias hasMessages: _rootState.hasMessages
44 
45  property string batteryIconName: Status.batteryIcon
46  property string displayStatus: Powerd.status
47 
48  onSupportsMultiColorLedChanged: {
49  updateLightState("onSupportsMultiColorLedChanged")
50  }
51 
52  onDisplayStatusChanged: {
53  updateLightState("onDisplayStatusChanged")
54  }
55 
56  onBatteryIconNameChanged: {
57  updateLightState("onBatteryIconNameChanged")
58  }
59 
60  function updateLightState(msg) {
61  console.log("updateLightState: " + msg
62  + ", indicatorState: " + indicatorState
63  + ", supportsMultiColorLed: " + supportsMultiColorLed
64  + ", hasMessages: " + hasMessages
65  + ", icon: " + batteryIconName
66  + ", displayStatus: " + displayStatus
67  + ", deviceState: " + deviceState
68  + ", batteryLevel: " + batteryLevel)
69 
70  //
71  // priorities:
72  // unread messsages (highest), full&charging, charging, low
73  //
74  // Icons: (see device.s from indicator-power)
75  // %s-empty-symbolic empty
76  // %s-caution-charging-symbolic charging [ 0..10)
77  // %s-low-charging-symbolic charging [10..30)
78  // %s-good-charging-symbolic charging [30..60)
79  // %s-full-charging-symbolic charging [60..100]
80  // %s-full-charged-symbolic fully charged
81  // %s-low-symbolic ?
82  // %s-full-symbolic ?
83  //
84  // device-state: (see system-settings\plugins\battery)
85  // fully-charged
86  // charging
87  // discharging
88  //
89  // Indicators:
90  // unread notifications : darkgreen pulsing (1000/3000)
91  // charging : white continuously
92  // battery full : green continuously
93  // battery low : orangered pulsing (500/3000)
94  //
95  // Notes:
96  // Icon name 'full-charged' comes late (45m after 100%)
97  // so also check device-state and battery-level
98  //
99  // Battery low warning dialog on screen shows up at 10%
100  // but 'caution' icon at 9%.
101  //
102 
103  // only show led when display is off
104  if (displayStatus == Powerd.On) {
105  indicatorState = "INDICATOR_OFF"
106  return
107  }
108 
109  // unread messsages have highest priority
110  if (hasMessages) {
111  indicatorState = "HAS_MESSAGES"
112  return
113  }
114 
115  // if device does not support a multi color led set led off
116  if(!supportsMultiColorLed) {
117 console.log("no support for Multicolor LED. " + indicatorState)
118  indicatorState = "INDICATOR_OFF"
119  return
120  }
121 
122  var isCharging = batteryIconName.indexOf("charging") >= 0
123  || deviceState == "charging"
124  || deviceState == "fully-charged"
125 
126  var isFull = batteryIconName.indexOf("full-charged") >= 0
127  || deviceState == "fully-charged"
128  || batteryLevel >= 100
129 
130  if (isCharging && isFull) {
131  indicatorState = "BATTERY_FULL"
132  } else if (isCharging) {
133  indicatorState = "BATTERY_CHARGING"
134  } else if (batteryIconName.indexOf("caution") >= 0
135  || batteryIconName.indexOf("empty") >= 0) {
136  indicatorState = "BATTERY_LOW"
137  } else {
138  indicatorState = "INDICATOR_OFF"
139  }
140  }
141 
142  onIndicatorStateChanged: {
143  console.log("onIndicatorStateChange: " + indicatorState)
144 
145  switch (indicatorState) {
146  case "INDICATOR_OFF":
147  break;
148  case "HAS_MESSAGES":
149  color = "darkgreen"
150  onMillisec = 1000
151  offMillisec = 3000
152  break;
153  case "BATTERY_FULL":
154  color = "green"
155  onMillisec = 1000
156  offMillisec = 0
157  break;
158  case "BATTERY_CHARGING":
159  color = "white"
160  onMillisec = 1000
161  offMillisec = 0
162  break;
163  case "BATTERY_LOW":
164  color = "orangered"
165  onMillisec = 500
166  offMillisec = 3000
167  break;
168  default:
169  console.log("ERROR onIndicatorStateChange: unknown state: " + indicatorState)
170  break;
171  }
172 
173  // HACK: led is only updated after turn on so first always turn off
174  Leds.state = Leds.Off
175  if (indicatorState != "INDICATOR_OFF")
176  Leds.state = Leds.On
177  }
178 
179  // Existence of unread notifications is determined by checking for a specific icon name in a dbus signal.
180  property var _actionGroup: QMenuModel.QDBusActionGroup {
181  busType: 1
182  busName: "org.ayatana.indicator.messages"
183  objectPath: "/org/ayatana/indicator/messages"
184  }
185 
186  Indicators.ActionRootState {
187  id: _rootState
188  actionGroup: _actionGroup
189  actionName: "messages"
190  Component.onCompleted: actionGroup.start()
191  property bool hasMessages: (String(icons).indexOf("indicator-messages-new") != -1) && valid
192  onHasMessagesChanged: updateLightState("onHasMessagesChanged")
193  }
194 
195  // Charging state and battery level are determined by listening to dbus signals from upower.
196  // See also system-settings battery plugin.
197  property var _ipag: QMenuModel.QDBusActionGroup {
198  busType: 1
199  busName: "org.ayatana.indicator.power"
200  objectPath: "/org/ayatana/indicator/power"
201  property variant batteryLevel: action("battery-level").state
202  property variant deviceState: action("device-state").state
203  Component.onCompleted: start()
204  onBatteryLevelChanged: {
205  root.batteryLevel = batteryLevel ? batteryLevel : 0.00
206  updateLightState("onBatteryLevelChanged")
207  }
208  onDeviceStateChanged: {
209  root.deviceState = deviceState ? deviceState : "unknown"
210  updateLightState("onDeviceStateChanged")
211  }
212  }
213 
214  Component.onCompleted: Leds.state = Leds.Off
215  Component.onDestruction: Leds.state = Leds.Off
216 
217  property var _colorBinding: Binding {
218  target: Leds
219  property: "color"
220  value: root.color
221  }
222 
223  property var _onMillisecBinding: Binding {
224  target: Leds
225  property: "onMillisec"
226  value: root.onMillisec
227  }
228 
229  property var _offMillisecBinding: Binding {
230  target: Leds
231  property: "offMillisec"
232  value: root.offMillisec
233  }
234 }