Lomiri
Lockscreen.qml
1 /*
2  * Copyright (C) 2013-2017 Canonical Ltd.
3  * Copyright (C) 2021 UBports Foundation
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU 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 General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 import QtQuick 2.4
19 import Lomiri.Components 1.3
20 import Lomiri.Components.Popups 1.3
21 import Lomiri.Telephony 0.1 as Telephony
22 
23 Showable {
24  id: root
25 
26  // Determine if a numeric or alphanumeric pad is used.
27  property bool alphaNumeric: false
28 
29  // Whether to show an emergency call button
30  property bool showEmergencyCallButton: true
31 
32  // Whether to show a cancel button (not all lockscreen types normally do anyway)
33  property bool showCancelButton: true
34 
35  // Informational text. (e.g. some text to tell which domain this is pin is entered for)
36  property string infoText: ""
37 
38  // Retries text (e.g. 3 retries left)
39  // (This is not currently used, but will be necessary for SIM unlock screen)
40  property string retryText: ""
41 
42  // The text to be displayed in case the login failed
43  property string errorText: ""
44 
45  // Set those to a value greater 0 to restrict the pin length.
46  // If both are unset, the Lockscreen will show a confirm button and allow typing any length of pin before
47  // confirming. If minPinLength is set to a value > 0, the confirm button will only become active when the
48  // entered pin is at least that long. If maxPinLength is set, the lockscreen won't allow entering any
49  // more numbers than that. If both are set to the same value, the lockscreen will enter auto confirming
50  // behavior, hiding the confirmation button and triggering that automatically when the entered pin reached
51  // that length. This is ignored by the alphaNumeric lockscreen as that one is always confirmed by pressing
52  // enter on the OSK.
53  property int minPinLength: -1
54  property int maxPinLength: -1
55 
56  property url background: ""
57  property alias backgroundSourceSize: backgroundImage.sourceSize
58  // Use this to put a black overlay above the background
59  // 0: normal background, 1: black background
60  property real darkenBackground: 0
61 
62  property color foregroundColor: "#f3f3e7"
63 
64  readonly property string passphrase: (pinPadLoader.item && pinPadLoader.item.passphrase) ? pinPadLoader.item.passphrase : ""
65 
66  signal entered(string passphrase)
67  signal cancel()
68  signal emergencyCall()
69  signal infoPopupConfirmed()
70 
71  onActiveFocusChanged: if (activeFocus && pinPadLoader.item) pinPadLoader.item.forceActiveFocus()
72 
73  function reset() {
74  // This causes the loader below to destry and recreate the source
75  pinPadLoader.resetting = true;
76  pinPadLoader.resetting = false;
77  }
78 
79  function clear(showAnimation) {
80  if (pinPadLoader.item) {
81  pinPadLoader.item.clear(showAnimation);
82  }
83  pinPadLoader.showWrongText = showAnimation
84  pinPadLoader.waiting = false
85  }
86 
87  function showInfoPopup(title, text) {
88  var popup = PopupUtils.open(infoPopupComponent, root, {title: title, text: text})
89  // FIXME: SDK will do this internally soonish
90  popup.z = Number.MAX_VALUE
91  }
92 
93  Rectangle {
94  // In case background fails to load
95  id: backgroundBackup
96  anchors.fill: parent
97  color: "black"
98  visible: root.background.toString() !== ""
99  }
100 
101  Wallpaper {
102  id: backgroundImage
103  objectName: "lockscreenBackground"
104  anchors {
105  fill: parent
106  }
107  source: root.required ? root.background : ""
108  }
109 
110  // This is to
111  // a) align it with the greeter and
112  // b) keep the white fonts readable on bright backgrounds
113  Rectangle {
114  anchors.fill: parent
115  color: "black"
116  opacity: root.darkenBackground
117  }
118 
119  Loader {
120  id: pinPadLoader
121  objectName: "pinPadLoader"
122  anchors.fill: parent
123  property bool resetting: false
124  property bool waiting: false
125  property bool showWrongText: false
126  focus: true
127 
128  source: {
129  if (resetting || !root.required) {
130  return ""
131  } else if (root.alphaNumeric) {
132  return "PassphraseLockscreen.qml"
133  } else {
134  return "PinLockscreen.qml"
135  }
136  }
137  onSourceChanged: {
138  waiting = false
139  showWrongText = false
140  }
141 
142  Connections {
143  target: pinPadLoader.item
144 
145  onEntered: {
146  pinPadLoader.waiting = true
147  root.entered(passphrase);
148  }
149 
150  onCancel: {
151  root.cancel()
152  }
153  }
154 
155  Binding {
156  target: pinPadLoader.item
157  property: "minPinLength"
158  value: root.minPinLength
159  }
160  Binding {
161  target: pinPadLoader.item
162  property: "maxPinLength"
163  value: root.maxPinLength
164  }
165  Binding {
166  target: pinPadLoader.item
167  property: "infoText"
168  value: root.infoText
169  }
170  Binding {
171  target: pinPadLoader.item
172  property: "retryText"
173  value: root.retryText
174  }
175  Binding {
176  target: pinPadLoader.item
177  property: "errorText"
178  value: pinPadLoader.showWrongText ? root.errorText : ""
179  }
180  Binding {
181  target: pinPadLoader.item
182  property: "entryEnabled"
183  value: !pinPadLoader.waiting
184  }
185  Binding {
186  target: pinPadLoader.item
187  property: "showCancelButton"
188  value: root.showCancelButton
189  }
190  Binding {
191  target: pinPadLoader.item
192  property: "foregroundColor"
193  value: root.foregroundColor
194  }
195  }
196 
197  Item {
198  id: emergencyCallRow
199 
200  visible: showEmergencyCallButton
201 
202  anchors {
203  bottom: parent.bottom
204  bottomMargin: units.gu(7) + (Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0)
205  left: parent.left
206  right: parent.right
207  }
208 
209  Label {
210  id: emergencyCallLabel
211  objectName: "emergencyCallLabel"
212  anchors.horizontalCenter: parent.horizontalCenter
213 
214  text: callManager.hasCalls ? i18n.tr("Return to Call") : i18n.tr("Emergency Call")
215  color: root.foregroundColor
216  }
217 
218  Icon {
219  id: emergencyCallIcon
220  anchors.left: emergencyCallLabel.right
221  anchors.leftMargin: units.gu(1)
222  width: emergencyCallLabel.height
223  height: emergencyCallLabel.height
224  name: "call-start"
225  color: root.foregroundColor
226  }
227 
228  MouseArea {
229  anchors.top: emergencyCallLabel.top
230  anchors.bottom: emergencyCallLabel.bottom
231  anchors.left: emergencyCallLabel.left
232  anchors.right: emergencyCallIcon.right
233  onClicked: root.emergencyCall()
234  }
235  }
236 
237  Component {
238  id: infoPopupComponent
239  ShellDialog {
240  id: dialog
241  objectName: "infoPopup"
242  property var dialogLoader // dummy to satisfy ShellDialog's context dependent prop
243 
244  Button {
245  width: parent.width
246  objectName: "infoPopupOkButton"
247  text: i18n.tr("OK")
248  focus: true
249  onClicked: {
250  PopupUtils.close(dialog)
251  root.infoPopupConfirmed();
252  }
253  }
254  }
255  }
256 }