Lomiri
NarrowView.qml
1 /*
2  * Copyright (C) 2015-2016 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 QtQuick.Window 2.2
20 import QtGraphicalEffects 1.12
21 import Lomiri.Components 1.3
22 import Lomiri.Telephony 0.1 as Telephony
23 import "../Components"
24 
25 FocusScope {
26  id: root
27 
28  property alias dragHandleLeftMargin: coverPage.dragHandleLeftMargin
29  property alias launcherOffset: coverPage.launcherOffset
30  property alias currentIndex: loginList.currentIndex
31  property alias delayMinutes: delayedLockscreen.delayMinutes
32  property real panelHeight
33  property url background
34  property real backgroundSourceSize
35  property bool hasCustomBackground
36  property bool locked
37  property alias alphanumeric: loginList.alphanumeric
38  property alias hasKeyboard: loginList.hasKeyboard
39  property alias userModel: loginList.model
40  property alias infographicModel: coverPage.infographicModel
41  property string sessionToStart
42  property bool waiting
43  readonly property bool fullyShown: coverPage.showProgress === 1 || lockscreen.shown
44  readonly property bool required: coverPage.required || lockscreen.required
45  readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
46 
47  property rect inputMethodRect
48 
49  signal selected(int index)
50  signal responded(string response)
51  signal tease()
52  signal emergencyCall()
53 
54  function hide() {
55  lockscreen.hide();
56  coverPage.hide();
57  }
58 
59  function showFakePassword() {
60  loginList.showFakePassword();
61  }
62 
63  function notifyAuthenticationFailed() {
64  loginList.showError();
65  }
66 
67  function showErrorMessage(msg) {
68  coverPage.showErrorMessage(msg);
69  }
70 
71  function forceShow() {
72  coverPage.show();
73  }
74 
75  function tryToUnlock(toTheRight) {
76  var coverChanged = coverPage.shown;
77  lockscreen.maybeShow();
78  if (toTheRight) {
79  coverPage.hideRight();
80  } else {
81  coverPage.hide();
82  }
83  return coverChanged;
84  }
85 
86  onLockedChanged: {
87  if (locked) {
88  lockscreen.maybeShow();
89  } else {
90  lockscreen.hide();
91  }
92  }
93 
94  Keys.onSpacePressed: coverPage.hide();
95  Keys.onReturnPressed: coverPage.hide();
96  Keys.onEnterPressed: coverPage.hide();
97 
98  Showable {
99  id: lockscreen
100  objectName: "lockscreen"
101  anchors.fill: parent
102  shown: false
103  opacity: 0
104 
105  showAnimation: StandardAnimation { property: "opacity"; to: 1 }
106  hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
107 
108  Rectangle {
109  // In case background fails to load or doesn't cover the whole screen
110  id: backgroundBackup
111  anchors.fill: parent
112  color: "black"
113  }
114 
115  Wallpaper {
116  id: lockscreenBackground
117  objectName: "lockscreenBackground"
118  anchors {
119  fill: parent
120  }
121  source: root.background
122  sourceSize: root.backgroundSourceSize
123  }
124 
125  // Darken background to match CoverPage
126  Rectangle {
127  objectName: "lockscreenShade"
128  anchors.fill: parent
129  color: "black"
130  opacity: root.hasCustomBackground ? 0.4 : 0
131  }
132 
133  LoginList {
134  id: loginList
135  objectName: "loginList"
136 
137  anchors {
138  horizontalCenter: parent.horizontalCenter
139  top: parent.top
140  bottom: parent.bottom
141  }
142  width: units.gu(40)
143  boxVerticalOffset: units.gu(14)
144  enabled: !coverPage.shown && visible
145  visible: !delayedLockscreen.visible
146 
147  locked: root.locked
148 
149  onSelected: if (enabled) root.selected(index)
150  onResponded: root.responded(response)
151  }
152 
153  DelayedLockscreen {
154  id: delayedLockscreen
155  objectName: "delayedLockscreen"
156  anchors.fill: parent
157  visible: delayMinutes > 0
158  alphaNumeric: loginList.alphanumeric
159  }
160 
161  function maybeShow() {
162  if (root.locked && !shown) {
163  showNow();
164  }
165  }
166  }
167 
168  Rectangle {
169  anchors.fill: parent
170  color: "black"
171  opacity: coverPage.showProgress * 0.8
172  }
173 
174  CoverPage {
175  id: coverPage
176  objectName: "coverPage"
177  height: parent.height
178  width: parent.width
179  background: root.background
180  hasCustomBackground: root.hasCustomBackground
181  panelHeight: root.panelHeight
182  draggable: !root.waiting
183  onTease: root.tease()
184  onClicked: hide()
185  backgroundSourceSize: root.backgroundSourceSize
186 
187  onShowProgressChanged: {
188  if (showProgress === 0) {
189  if (lockscreen.shown) {
190  loginList.tryToUnlock();
191  } else {
192  root.responded("");
193  }
194  }
195  }
196 
197  Clock {
198  anchors {
199  top: parent.top
200  topMargin: units.gu(2) + panelHeight
201  horizontalCenter: parent.horizontalCenter
202  }
203  }
204  }
205 
206  StyledItem {
207  id: bottomBar
208  visible: lockscreen.shown
209  height: units.gu(4)
210 
211  anchors.left: parent.left
212  anchors.right: parent.right
213  anchors.top: parent.bottom
214  anchors.topMargin: - height * (1 - coverPage.showProgress)
215  - ( inputMethodRect.height )
216 
217  Label {
218  text: i18n.tr("Cancel")
219  anchors.left: parent.left
220  anchors.leftMargin: units.gu(2)
221  anchors.top: parent.top
222  anchors.bottom: parent.bottom
223  verticalAlignment: Text.AlignVCenter
224  font.weight: Font.Light
225  fontSize: "small"
226  color: theme.palette.normal.raisedSecondaryText
227 
228  AbstractButton {
229  anchors.fill: parent
230  anchors.leftMargin: -units.gu(2)
231  anchors.rightMargin: -units.gu(2)
232  onClicked: coverPage.show()
233  }
234  }
235 
236  Label {
237  objectName: "emergencyCallLabel"
238  text: callManager.hasCalls ? i18n.tr("Return to Call") : i18n.tr("Emergency")
239  anchors.right: parent.right
240  anchors.rightMargin: units.gu(2)
241  anchors.top: parent.top
242  anchors.bottom: parent.bottom
243  verticalAlignment: Text.AlignVCenter
244  font.weight: Font.Light
245  fontSize: "small"
246  color: theme.palette.normal.raisedSecondaryText
247  // TODO: uncomment once bug 1616538 is fixed
248  // visible: telepathyHelper.ready && telepathyHelper.emergencyCallsAvailable
249  enabled: visible
250 
251  AbstractButton {
252  anchors.fill: parent
253  anchors.leftMargin: -units.gu(2)
254  anchors.rightMargin: -units.gu(2)
255  onClicked: root.emergencyCall()
256  }
257  }
258  }
259 
260  // FIXME: It's difficult to keep something tied closely to the OSK (bug
261  // 1616163). But as a hack to avoid the background peeking out,
262  // we add an extra Rectangle that just serves to hide the background
263  // during OSK animations.
264  Rectangle {
265  visible: bottomBar.visible
266  height: inputMethodRect.height
267  anchors.bottom: parent.bottom
268  anchors.left: parent.left
269  anchors.right: parent.right
270  color: LomiriColors.porcelain
271  }
272 }